Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/config-url'
Browse files Browse the repository at this point in the history
  • Loading branch information
motemen committed Jun 26, 2014
2 parents a870a96 + 793a5ab commit 889980c
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 23 deletions.
4 changes: 2 additions & 2 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func doImportStarred(c *cli.Context) {

if githubToken == "" {
var err error
githubToken, err = GitConfig("ghq.github.token")
githubToken, err = GitConfigSingle("ghq.github.token")
utils.PanicIf(err)
}

Expand Down Expand Up @@ -417,7 +417,7 @@ func doImportPocket(c *cli.Context) {
return
}

accessToken, err := GitConfig("ghq.pocket.token")
accessToken, err := GitConfigSingle("ghq.pocket.token")
utils.PanicIf(err)

if accessToken == "" {
Expand Down
20 changes: 18 additions & 2 deletions ghq.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,27 @@ ghq.root::
want to specify "$GOPATH/src" as a secondary root (environment variables
should be expanded.)

ghq.<url>.vcs::
ghq tries to detect the remote repository's VCS backend for non-"github.com"
repositories. With this option you can explicitly specify the VCS for the
remote repository. The URL is matched against '<url>' using 'git config --get-urlmatch'. +
Accepted values are "git", "github" (an alias for "git"), "mercurial", "hg"
(an alias for "mercurial"). +
To get this configuration variable effective, you will need Git 1.8.5 or higher. +
For example in .gitconfig: +

....
[ghq "https://git.example.com/repos/"]
vcs = git
....


ghq.ghe.host::
The hostname of your GitHub Enterprise installation. A repository that has a
hostname set with this key will be regarded as same one as one on GitHub.
This variable can have multiple values. If so, `ghq` tries matching with
each hostnames.
each hostnames. +
This option is DEPRECATED, so use "ghq.<url>.vcs" configuration instead.

ghq.github.token::
GitHub's access token used in `ghq import starred` command.
Expand All @@ -100,7 +116,7 @@ Local repositories are placed under 'ghq.root' with named github.com/_user_/_rep
....


== [[installing]]INSTALLING
== [[installing]]INSTALLATION

----
go get github.com/motemen/ghq
Expand Down
73 changes: 57 additions & 16 deletions git.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,39 @@ package main
import (
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"syscall"

"github.com/motemen/ghq/utils"
)

func GitConfig(key string) (string, error) {
return gitConfig(key, false)
// GitConfigSingle fetches single git-config variable.
// returns an empty string and no error if no variable is found with the given key.
func GitConfigSingle(key string) (string, error) {
return GitConfig("--get", key)
}

// GitConfigAll fetches git-config variable of multiple values.
func GitConfigAll(key string) ([]string, error) {
value, err := gitConfig(key, true)
value, err := GitConfig("--get-all", key)
if err != nil {
return nil, err
}

var values = strings.Split(value, "\000")
if len(values) == 1 && values[0] == "" {
values = values[:0]
// No results found, return an empty slice
if value == "" {
return nil, nil
}

return values, nil
return strings.Split(value, "\000"), nil
}

func gitConfig(key string, all bool) (string, error) {
var getFlag string
if all == true {
getFlag = "--get-all"
} else {
getFlag = "--get"
}

cmd := exec.Command("git", "config", "--path", "--null", getFlag, key)
// GitConfig invokes 'git config' and handles some errors properly.
func GitConfig(args ...string) (string, error) {
gitArgs := append([]string{"config", "--path", "--null"}, args...)
cmd := exec.Command("git", gitArgs...)
cmd.Stderr = os.Stderr

buf, err := cmd.Output()
Expand All @@ -51,3 +53,42 @@ func gitConfig(key string, all bool) (string, error) {

return strings.TrimRight(string(buf), "\000"), nil
}

var versionRx = regexp.MustCompile(`(\d+)\.(\d+)\.(\d+)`)

var featureConfigURLMatchVersion = []uint{1, 8, 5}

func GitHasFeatureConfigURLMatch() bool {
cmd := exec.Command("git", "--version")
buf, err := cmd.Output()

if err != nil {
return false
}

return gitVersionOutputSatisfies(string(buf), featureConfigURLMatchVersion)
}

func gitVersionOutputSatisfies(gitVersionOutput string, baseVersionParts []uint) bool {
versionStrings := versionRx.FindStringSubmatch(gitVersionOutput)
if versionStrings == nil {
return false
}

for i, v := range baseVersionParts {
thisV64, err := strconv.ParseUint(versionStrings[i+1], 10, 0)
utils.PanicIf(err)

thisV := uint(thisV64)

if thisV > v {
return true
} else if v == thisV {
continue
} else {
return false
}
}

return true
}
76 changes: 75 additions & 1 deletion git_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,86 @@
package main

import (
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/gomega"
)

func TestGitConfigAll(t *testing.T) {
RegisterTestingT(t)

Expect(GitConfigAll("ghq.non.existent.key")).To(HaveLen(0))
}

func TestGitConfigURL(t *testing.T) {
RegisterTestingT(t)

if GitHasFeatureConfigURLMatch() == false {
t.Skip("Git does not have config --get-urlmatch feature")
}

reset, err := WithGitconfigFile(`
[ghq "https://ghe.example.com/"]
vcs = github
[ghq "https://ghe.example.com/hg/"]
vcs = hg
`)
if err != nil {
t.Fatal(err)
}
defer reset()

var (
value string
)

value, err = GitConfig("--get-urlmatch", "ghq.vcs", "https://ghe.example.com/foo/bar")
Expect(err).NotTo(HaveOccurred())
Expect(value).To(Equal("github"))

value, err = GitConfig("--get-urlmatch", "ghq.vcs", "https://ghe.example.com/hg/repo")
Expect(err).NotTo(HaveOccurred())
Expect(value).To(Equal("hg"))

value, err = GitConfig("--get-urlmatch", "ghq.vcs", "https://example.com")
Expect(err).NotTo(HaveOccurred())
Expect(value).To(Equal(""))
}

func TestGitVersionOutputSatisfies(t *testing.T) {
RegisterTestingT(t)

Expect(
gitVersionOutputSatisfies(
"git version 1.7.9",
[]uint{1, 8, 5},
),
).To(BeFalse())

Expect(
gitVersionOutputSatisfies(
"git version 1.8.2.3",
[]uint{1, 8, 5},
),
).To(BeFalse())

Expect(
gitVersionOutputSatisfies(
"git version 1.8.5",
[]uint{1, 8, 5},
),
).To(BeTrue())

Expect(
gitVersionOutputSatisfies(
"git version 1.9.1",
[]uint{1, 8, 5},
),
).To(BeTrue())

Expect(
gitVersionOutputSatisfies(
"git version 2.0.0",
[]uint{1, 8, 5},
),
).To(BeTrue())
}
25 changes: 25 additions & 0 deletions helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package main

import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/motemen/ghq/utils"
Expand All @@ -19,3 +22,25 @@ func NewFakeRunner(dispatch map[string]error) utils.RunFunc {
panic(fmt.Sprintf("No fake dispatch found for: %s", cmdString))
}
}

func WithGitconfigFile(configContent string) (func(), error) {
tmpdir, err := ioutil.TempDir("", "ghq-test")
if err != nil {
return nil, err
}

tmpGitconfigFile := filepath.Join(tmpdir, "gitconfig")

ioutil.WriteFile(
tmpGitconfigFile,
[]byte(configContent),
0777,
)

prevGitConfigEnv := os.Getenv("GIT_CONFIG")
os.Setenv("GIT_CONFIG", tmpGitconfigFile)

return func() {
os.Setenv("GIT_CONFIG", prevGitConfigEnv)
}, nil
}
22 changes: 22 additions & 0 deletions remote_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,28 @@ func (repo *OtherRepository) IsValid() bool {
}

func (repo *OtherRepository) VCS() *VCSBackend {
if GitHasFeatureConfigURLMatch() {
// Respect 'ghq.url.https://ghe.example.com/.vcs' config variable
// (in gitconfig:)
// [ghq "https://ghe.example.com/"]
// vcs = github
vcs, err := GitConfig("--get-urlmatch", "ghq.vcs", repo.URL().String())
if err != nil {
utils.Log("error", err.Error())
}

if vcs == "git" || vcs == "github" {
return GitBackend
}

if vcs == "hg" || vcs == "mercurial" {
return MercurialBackend
}
} else {
utils.Log("warning", "This version of Git does not support `config --get-urlmatch`; per-URL settings are not available")
}

// Detect VCS backend automatically
if utils.RunSilently("hg", "identify", repo.url.String()) == nil {
return MercurialBackend
} else if utils.RunSilently("git", "ls-remote", repo.url.String()) == nil {
Expand Down
5 changes: 3 additions & 2 deletions utils/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ var logger = &colorine.Logger{
"skip": colorine.Verbose,
"cd": colorine.Verbose,

"open": colorine.Warn,
"exists": colorine.Warn,
"open": colorine.Warn,
"exists": colorine.Warn,
"warning": colorine.Warn,

"authorized": colorine.Notice,

Expand Down
4 changes: 4 additions & 0 deletions wercker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ box: motemen/golang-goxc
build:
steps:
- setup-go-workspace
- script:
name: git version
code: |
git version
- script:
name: go get
code: |
Expand Down

0 comments on commit 889980c

Please sign in to comment.