diff --git a/gitcmds/context.go b/gitcmds/context.go new file mode 100644 index 0000000..ff4a4f3 --- /dev/null +++ b/gitcmds/context.go @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026-present unTill Software Development Group B.V. + * @author Denis Gribanov + */ + +package gitcmds + +import "strings" + +// LoadRepoContext gathers repository state needed by Dev() in minimal git round-trips. +// Phase 1 runs five independent local git commands concurrently. +// Phase 2 issues one gh API call that requires Org/Repo obtained in phase 1. +func LoadRepoContext(wd string) (*RepoContext, error) { + rc := &RepoContext{} + + type result struct { + setFn func() + err error + } + + const numConcurrent = 5 + ch := make(chan result, numConcurrent) + + go func() { + v, err := HasRemote(wd, "upstream") + ch <- result{setFn: func() { rc.UpstreamExists = v }, err: err} + }() + + go func() { + repo, org, err := GetRepoAndOrgName(wd) + ch <- result{setFn: func() { rc.Repo = repo; rc.Org = org }, err: err} + }() + + go func() { + v, err := GetCurrentBranchName(wd) + ch <- result{setFn: func() { rc.CurrentBranch = v }, err: err} + }() + + go func() { + v, err := GetMainBranch(wd) + ch <- result{setFn: func() { rc.MainBranch = v }, err: err} + }() + + go func() { + v, err := HaveUncommittedChanges(wd) + ch <- result{setFn: func() { rc.HasUncommittedChanges = v }, err: err} + }() + + for range numConcurrent { + r := <-ch + if r.err != nil { + return nil, r.err + } + r.setFn() + } + + rc.IsMain = strings.EqualFold(rc.CurrentBranch, rc.MainBranch) + + // Phase 2: gh API call needs Org/Repo from phase 1 + parentRepo, err := getParentRepoByOrgRepo(wd, rc.Org, rc.Repo) + if err != nil { + return nil, err + } + rc.ParentRepo = parentRepo + + return rc, nil +} diff --git a/gitcmds/gitcmds.go b/gitcmds/gitcmds.go index 696d76d..f0937d7 100644 --- a/gitcmds/gitcmds.go +++ b/gitcmds/gitcmds.go @@ -479,13 +479,8 @@ func GetBranchType(wd string) (string, notesPkg.BranchType, error) { return currentBranchName, GetBranchTypeByName(currentBranchName), nil } -// GetParentRepoName - parent repo of forked -func GetParentRepoName(wd string) (name string, err error) { - repo, org, err := GetRepoAndOrgName(wd) - if err != nil { - return "", err - } - +// getParentRepoByOrgRepo fetches the parent repository full name via the GitHub API. +func getParentRepoByOrgRepo(wd, org, repo string) (string, error) { stdout, stderr, err := new(exec.PipedExec). Command("gh", "api", "repos/"+org+slash+repo, "--jq", ".parent.full_name"). WorkingDir(wd). @@ -503,6 +498,16 @@ func GetParentRepoName(wd string) (name string, err error) { return strings.TrimSpace(stdout), nil } +// GetParentRepoName - parent repo of forked +func GetParentRepoName(wd string) (name string, err error) { + repo, org, err := GetRepoAndOrgName(wd) + if err != nil { + return "", err + } + + return getParentRepoByOrgRepo(wd, org, repo) +} + // IsBranchInMain Is my branch in main org? func IsBranchInMain(wd string) (bool, error) { repo, org, err := GetRepoAndOrgName(wd) diff --git a/gitcmds/status.go b/gitcmds/status.go index 0abde43..5ce2e8c 100644 --- a/gitcmds/status.go +++ b/gitcmds/status.go @@ -165,6 +165,9 @@ func getListOfChangedFiles(wd, statusOutput string) ([]fileInfo, error) { oldSize = newFileSize case `??`: newFileSize, err2 = getFileSize(wd, name) + case `AD`: + // file was added, then removed -> consider it never existed + continue default: return nil, fmt.Errorf("unknown file status %s for file %s", statusCode, name) } diff --git a/gitcmds/types.go b/gitcmds/types.go index 15049a7..9718b42 100644 --- a/gitcmds/types.go +++ b/gitcmds/types.go @@ -9,3 +9,15 @@ type PRInfo struct { Title string `json:"title"` URL string `json:"url"` } + +// RepoContext holds repository state gathered upfront for Dev() +type RepoContext struct { + Repo string + Org string + CurrentBranch string + MainBranch string + IsMain bool + HasUncommittedChanges bool + UpstreamExists bool + ParentRepo string +} diff --git a/go.mod b/go.mod index 43be1c4..8830232 100644 --- a/go.mod +++ b/go.mod @@ -1,46 +1,46 @@ module github.com/untillpro/qs -go 1.26.0 +go 1.26.2 require ( github.com/atotto/clipboard v0.1.4 github.com/coreos/go-semver v0.3.1 - github.com/fatih/color v1.18.0 - github.com/go-git/go-git/v5 v5.16.5 + github.com/fatih/color v1.19.0 + github.com/go-git/go-git/v5 v5.18.0 github.com/google/uuid v1.6.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 github.com/untillpro/goutils v0.0.0-20231201170327-3c33c6010100 github.com/voedger/voedger v1.202405300917.1 - golang.org/x/mod v0.33.0 + golang.org/x/mod v0.35.0 ) require ( dario.cat/mergo v1.0.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect + github.com/ProtonMail/go-crypto v1.4.1 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.7.0 // indirect + github.com/go-git/go-billy/v5 v5.8.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.6.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-isatty v0.0.21 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/skeema/knownhosts v1.3.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.48.0 // indirect - golang.org/x/net v0.50.0 // indirect - golang.org/x/sys v0.41.0 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/sys v0.43.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0d3f544..1237f14 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= +github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -25,18 +25,18 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= -github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E= +github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= +github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= -github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= +github.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM= +github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -60,8 +60,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -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/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= +github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= @@ -96,30 +96,29 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -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.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/commands/dev.go b/internal/commands/dev.go index 4b521cd..5083e1d 100644 --- a/internal/commands/dev.go +++ b/internal/commands/dev.go @@ -19,14 +19,14 @@ import ( ) func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []string) error { - parentRepo, err := gitcmds.GetParentRepoName(wd) + rc, err := gitcmds.LoadRepoContext(wd) if err != nil { return err } // qs dev -d is running if doDelete { - return deleteBranches(wd, parentRepo) + return deleteBranches(wd, rc.ParentRepo) } // qs dev is running var devBranchName string @@ -42,48 +42,26 @@ func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []s // - If parentRepo exists OR upstream remote exists -> fork workflow // - If no parentRepo AND no upstream remote -> single remote workflow // Check if upstream remote exists - upstreamExists, err := gitcmds.HasRemote(wd, "upstream") - if err != nil { - return err - } // Only require fork if: // 1. No parent repo exists (not a fork), AND // 2. Upstream remote exists (indicating fork workflow was intended) // This catches the edge case where someone manually added upstream but didn't fork - if len(parentRepo) == 0 && upstreamExists { - repo, org, err := gitcmds.GetRepoAndOrgName(wd) - if err != nil { - return err - } - - return fmt.Errorf("you are in %s/%s repo with upstream remote but no fork detected\nExecute 'qs fork' first", org, repo) + if len(rc.ParentRepo) == 0 && rc.UpstreamExists { + return fmt.Errorf("you are in %s/%s repo with upstream remote but no fork detected\nExecute 'qs fork' first", rc.Org, rc.Repo) } - curBranch, mainBranch, isMain, err := gitcmds.GetCurrentBranchInfo(wd) - if err != nil { - return err - } - if !isMain { + if !rc.IsMain { fmt.Println("--------------------------------------------------------") fmt.Println("You are in") - repo, org, err := gitcmds.GetRepoAndOrgName(wd) - if err != nil { - return err - } - - color.New(color.FgHiCyan).Println(org + "/" + repo + "/" + curBranch) + color.New(color.FgHiCyan).Println(rc.Org + "/" + rc.Repo + "/" + rc.CurrentBranch) - return fmt.Errorf("switch to main branch before running 'qs dev'. You are in %s branch ", curBranch) + return fmt.Errorf("switch to main branch before running 'qs dev'. You are in %s branch ", rc.CurrentBranch) } // Stash current changes if needed stashedUncommittedChanges := false - if ok, err := gitcmds.HaveUncommittedChanges(wd); ok { - if err != nil { - return err - } - + if rc.HasUncommittedChanges { if err := gitcmds.Stash(wd); err != nil { return fmt.Errorf("error stashing changes: %w", err) } @@ -91,7 +69,7 @@ func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []s } // sync local MainBranch to ensure it's up to date with origin and upstream remotes - if err := gitcmds.SyncMainBranch(wd, mainBranch, upstreamExists); err != nil { + if err := gitcmds.SyncMainBranch(wd, rc.MainBranch, rc.UpstreamExists); err != nil { return err } @@ -124,25 +102,25 @@ func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []s switch response { case pushYes: - if len(parentRepo) > 0 && !upstreamExists { + if len(rc.ParentRepo) > 0 && !rc.UpstreamExists { var upstreamResponse string - fmt.Print("Upstream not found.\nRepository " + parentRepo + " will be added as upstream. Agree[y/n]?") + fmt.Print("Upstream not found.\nRepository " + rc.ParentRepo + " will be added as upstream. Agree[y/n]?") _, _ = fmt.Scanln(&upstreamResponse) if upstreamResponse != pushYes { fmt.Print(msgOkSeeYou) return nil } - if err := gitcmds.MakeUpstreamForBranch(wd, parentRepo); err != nil { + if err := gitcmds.MakeUpstreamForBranch(wd, rc.ParentRepo); err != nil { return err } } - if err := gitcmds.CreateDevBranch(wd, devBranchName, mainBranch, notes); err != nil { + if err := gitcmds.CreateDevBranch(wd, devBranchName, rc.MainBranch, notes); err != nil { return err } if issueInfo.Type == issue.GitHub { - notes, err = gitcmds.LinkBranchToGithubIssue(wd, parentRepo, issueInfo.Text, issueInfo.ID, devBranchName, args...) + notes, err = gitcmds.LinkBranchToGithubIssue(wd, rc.ParentRepo, issueInfo.Text, issueInfo.ID, devBranchName, args...) if err != nil { return err } diff --git a/internal/commands/dev_test.go b/internal/commands/dev_test.go new file mode 100644 index 0000000..f9c91aa --- /dev/null +++ b/internal/commands/dev_test.go @@ -0,0 +1,29 @@ +package commands + +import ( + "os/exec" + "testing" + + "github.com/stretchr/testify/require" +) + +func BenchmarkGitLsRemote(b *testing.B) { + wd := repoRoot(b) + b.ResetTimer() + for b.Loop() { + cmd := exec.Command("git", "ls-remote", "--heads", "origin", "main") + cmd.Dir = wd + out, err := cmd.Output() + require.NoError(b, err) + _ = out + } +} + +// repoRoot returns the root of the current git repository. +func repoRoot(b *testing.B) string { + b.Helper() + out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() + require.NoError(b, err) + return string(out[:len(out)-1]) // trim trailing newline +} + diff --git a/qs.go b/qs.go index 3eca3a2..4be2dee 100644 --- a/qs.go +++ b/qs.go @@ -8,6 +8,7 @@ import ( "github.com/voedger/voedger/pkg/goutils/logger" ) + func main() { if _, err := cmdproc.ExecRootCmd(context.Background(), os.Args); err != nil { logger.Verbose(err)