Merge branch 'master' into cross-compiling

This commit is contained in:
Mislav Marohnić 2015-09-27 21:38:34 +02:00
Родитель 2e24486ca1 1d5d4ba326
Коммит fc08248073
60 изменённых файлов: 1394 добавлений и 334 удалений

Просмотреть файл

@ -1,6 +1,9 @@
Contributing to hub
===================
This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to uphold this code.
[code-of-conduct]: http://todogroup.org/opencodeofconduct/#Hub/opensource@github.com
You will need:
1. Go 1.4 or better

19
Godeps/Godeps.json сгенерированный
Просмотреть файл

@ -1,15 +1,10 @@
{
"ImportPath": "github.com/github/hub",
"GoVersion": "go1.4",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "bitbucket.org/kardianos/osext",
"Comment": "null-15",
"Rev": "44140c5fc69ecf1102c5ef451d73cd98ef59b178"
},
{
"ImportPath": "github.com/BurntSushi/toml",
"Comment": "v0.1.0-9-g3883ac1",
@ -26,11 +21,11 @@
},
{
"ImportPath": "github.com/howeyc/gopass",
"Rev": "62ab5a80502a82291f265e6980d72310b8f480d5"
"Rev": "2c70fa70727c953c51695f800f25d6b44abb368e"
},
{
"ImportPath": "github.com/inconshreveable/go-update",
"Rev": "221d034a558b4c21b0624b2a450c076913854a57"
"Rev": "68f5725818189545231c1fd8694793d45f2fc529"
},
{
"ImportPath": "github.com/jingweno/go-sawyer",
@ -41,6 +36,10 @@
"Comment": "0.1-14-g0a85813",
"Rev": "0a85813ecac22e3cbe916ab9480b33f2f4a06b2e"
},
{
"ImportPath": "github.com/kardianos/osext",
"Rev": "6e7f843663477789fac7c02def0d0909e969b4e5"
},
{
"ImportPath": "github.com/kballard/go-shellquote",
"Rev": "e5c918b80c17694cbc49aab32a759f9a40067f5d"
@ -58,6 +57,10 @@
"ImportPath": "github.com/kr/text",
"Rev": "6807e777504f54ad073ecef66747de158294b639"
},
{
"ImportPath": "github.com/mattn/go-colorable",
"Rev": "d67e0b7d1797975196499f79bcc322c08b9f218b"
},
{
"ImportPath": "github.com/mattn/go-isatty",
"Rev": "6152ce208cfa13d58f065348a3312b4160fb98d1"

79
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go сгенерированный поставляемый
Просмотреть файл

@ -1,79 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin linux freebsd netbsd windows
package osext
import (
"fmt"
"os"
oexec "os/exec"
"path/filepath"
"runtime"
"testing"
)
const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
func TestExecPath(t *testing.T) {
ep, err := Executable()
if err != nil {
t.Fatalf("ExecPath failed: %v", err)
}
// we want fn to be of the form "dir/prog"
dir := filepath.Dir(filepath.Dir(ep))
fn, err := filepath.Rel(dir, ep)
if err != nil {
t.Fatalf("filepath.Rel: %v", err)
}
cmd := &oexec.Cmd{}
// make child start with a relative program path
cmd.Dir = dir
cmd.Path = fn
// forge argv[0] for child, so that we can verify we could correctly
// get real path of the executable without influenced by argv[0].
cmd.Args = []string{"-", "-test.run=XXXX"}
cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("exec(self) failed: %v", err)
}
outs := string(out)
if !filepath.IsAbs(outs) {
t.Fatalf("Child returned %q, want an absolute path", out)
}
if !sameFile(outs, ep) {
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
}
}
func sameFile(fn1, fn2 string) bool {
fi1, err := os.Stat(fn1)
if err != nil {
return false
}
fi2, err := os.Stat(fn2)
if err != nil {
return false
}
return os.SameFile(fi1, fi2)
}
func init() {
if e := os.Getenv(execPath_EnvVar); e != "" {
// first chdir to another path
dir := "/"
if runtime.GOOS == "windows" {
dir = filepath.VolumeName(".")
}
os.Chdir(dir)
if ep, err := Executable(); err != nil {
fmt.Fprint(os.Stderr, "ERROR: ", err)
} else {
fmt.Fprint(os.Stderr, ep)
}
os.Exit(0)
}
}

2
Godeps/_workspace/src/github.com/howeyc/gopass/pass.go сгенерированный поставляемый
Просмотреть файл

@ -22,7 +22,7 @@ func getPasswd(masked bool) []byte {
}
} else if v == 13 || v == 10 {
break
} else {
} else if v != 0 {
pass = append(pass, v)
os.Stdout.Write(mask)
}

7
Godeps/_workspace/src/github.com/howeyc/gopass/win.go сгенерированный поставляемый
Просмотреть файл

@ -29,12 +29,7 @@ func getch() byte {
var n uint16
procReadConsole.Call(uintptr(syscall.Stdin), uintptr(unsafe.Pointer(pLine)), uintptr(len(line)), uintptr(unsafe.Pointer(&n)))
// For some reason n returned seems to big by 2 (Null terminated maybe?)
if n > 2 {
n -= 2
}
b := []byte(string(utf16.Decode(line[:n])))
b := []byte(string(utf16.Decode(line)))
procSetConsoleMode.Call(uintptr(syscall.Stdin), uintptr(mode))

2
Godeps/_workspace/src/github.com/inconshreveable/go-update/check/check.go сгенерированный поставляемый
Просмотреть файл

@ -10,8 +10,8 @@ import (
"net/http"
"runtime"
"github.com/github/hub/Godeps/_workspace/src/bitbucket.org/kardianos/osext"
"github.com/github/hub/Godeps/_workspace/src/github.com/inconshreveable/go-update"
"github.com/github/hub/Godeps/_workspace/src/github.com/kardianos/osext"
)
type Initiative string

7
Godeps/_workspace/src/github.com/inconshreveable/go-update/update.go сгенерированный поставляемый
Просмотреть файл

@ -113,7 +113,6 @@ while outputting a progress meter and supports resuming partial downloads.
package update
import (
"github.com/github/hub/Godeps/_workspace/src/bitbucket.org/kardianos/osext"
"bytes"
"crypto"
"crypto/rsa"
@ -122,12 +121,14 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/github/hub/Godeps/_workspace/src/github.com/inconshreveable/go-update/download"
"github.com/github/hub/Godeps/_workspace/src/github.com/kr/binarydist"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/github/hub/Godeps/_workspace/src/github.com/inconshreveable/go-update/download"
"github.com/github/hub/Godeps/_workspace/src/github.com/kardianos/osext"
"github.com/github/hub/Godeps/_workspace/src/github.com/kr/binarydist"
)
// The type of a binary patch, if any. Only bsdiff is supported

Просмотреть файл

16
Godeps/_workspace/src/github.com/kardianos/osext/README.md сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,16 @@
### Extensions to the "os" package.
## Find the current Executable and ExecutableFolder.
There is sometimes utility in finding the current executable file
that is running. This can be used for upgrading the current executable
or finding resources located relative to the executable file. Both
working directory and the os.Args[0] value are arbitrary and cannot
be relied on; os.Args[0] can be "faked".
Multi-platform and supports:
* Linux
* OS X
* Windows
* Plan 9
* BSDs.

Просмотреть файл

@ -16,17 +16,12 @@ func Executable() (string, error) {
}
// Returns same path as Executable, returns just the folder
// path. Excludes the executable name.
// path. Excludes the executable name and any trailing slash.
func ExecutableFolder() (string, error) {
p, err := Executable()
if err != nil {
return "", err
}
folder, _ := filepath.Split(p)
return folder, nil
}
// Depricated. Same as Executable().
func GetExePath() (exePath string, err error) {
return Executable()
return filepath.Dir(p), nil
}

Просмотреть файл

Просмотреть файл

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux netbsd openbsd solaris
// +build linux netbsd openbsd solaris dragonfly
package osext
@ -11,15 +11,23 @@ import (
"fmt"
"os"
"runtime"
"strings"
)
func executable() (string, error) {
switch runtime.GOOS {
case "linux":
return os.Readlink("/proc/self/exe")
const deletedTag = " (deleted)"
execpath, err := os.Readlink("/proc/self/exe")
if err != nil {
return execpath, err
}
execpath = strings.TrimSuffix(execpath, deletedTag)
execpath = strings.TrimPrefix(execpath, deletedTag)
return execpath, nil
case "netbsd":
return os.Readlink("/proc/curproc/exe")
case "openbsd":
case "openbsd", "dragonfly":
return os.Readlink("/proc/curproc/file")
case "solaris":
return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))

Просмотреть файл

203
Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,203 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin linux freebsd netbsd windows
package osext
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)
const (
executableEnvVar = "OSTEST_OUTPUT_EXECUTABLE"
executableEnvValueMatch = "match"
executableEnvValueDelete = "delete"
)
func TestPrintExecutable(t *testing.T) {
ef, err := Executable()
if err != nil {
t.Fatalf("Executable failed: %v", err)
}
t.Log("Executable:", ef)
}
func TestPrintExecutableFolder(t *testing.T) {
ef, err := ExecutableFolder()
if err != nil {
t.Fatalf("ExecutableFolder failed: %v", err)
}
t.Log("Executable Folder:", ef)
}
func TestExecutableFolder(t *testing.T) {
ef, err := ExecutableFolder()
if err != nil {
t.Fatalf("ExecutableFolder failed: %v", err)
}
if ef[len(ef)-1] == filepath.Separator {
t.Fatal("ExecutableFolder ends with a trailing slash.")
}
}
func TestExecutableMatch(t *testing.T) {
ep, err := Executable()
if err != nil {
t.Fatalf("Executable failed: %v", err)
}
// fullpath to be of the form "dir/prog".
dir := filepath.Dir(filepath.Dir(ep))
fullpath, err := filepath.Rel(dir, ep)
if err != nil {
t.Fatalf("filepath.Rel: %v", err)
}
// Make child start with a relative program path.
// Alter argv[0] for child to verify getting real path without argv[0].
cmd := &exec.Cmd{
Dir: dir,
Path: fullpath,
Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueMatch)},
}
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("exec(self) failed: %v", err)
}
outs := string(out)
if !filepath.IsAbs(outs) {
t.Fatalf("Child returned %q, want an absolute path", out)
}
if !sameFile(outs, ep) {
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
}
}
func TestExecutableDelete(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip()
}
fpath, err := Executable()
if err != nil {
t.Fatalf("Executable failed: %v", err)
}
r, w := io.Pipe()
stderrBuff := &bytes.Buffer{}
stdoutBuff := &bytes.Buffer{}
cmd := &exec.Cmd{
Path: fpath,
Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueDelete)},
Stdin: r,
Stderr: stderrBuff,
Stdout: stdoutBuff,
}
err = cmd.Start()
if err != nil {
t.Fatalf("exec(self) start failed: %v", err)
}
tempPath := fpath + "_copy"
_ = os.Remove(tempPath)
err = copyFile(tempPath, fpath)
if err != nil {
t.Fatalf("copy file failed: %v", err)
}
err = os.Remove(fpath)
if err != nil {
t.Fatalf("remove running test file failed: %v", err)
}
err = os.Rename(tempPath, fpath)
if err != nil {
t.Fatalf("rename copy to previous name failed: %v", err)
}
w.Write([]byte{0})
w.Close()
err = cmd.Wait()
if err != nil {
t.Fatalf("exec wait failed: %v", err)
}
childPath := stderrBuff.String()
if !filepath.IsAbs(childPath) {
t.Fatalf("Child returned %q, want an absolute path", childPath)
}
if !sameFile(childPath, fpath) {
t.Fatalf("Child returned %q, not the same file as %q", childPath, fpath)
}
}
func sameFile(fn1, fn2 string) bool {
fi1, err := os.Stat(fn1)
if err != nil {
return false
}
fi2, err := os.Stat(fn2)
if err != nil {
return false
}
return os.SameFile(fi1, fi2)
}
func copyFile(dest, src string) error {
df, err := os.Create(dest)
if err != nil {
return err
}
defer df.Close()
sf, err := os.Open(src)
if err != nil {
return err
}
defer sf.Close()
_, err = io.Copy(df, sf)
return err
}
func TestMain(m *testing.M) {
env := os.Getenv(executableEnvVar)
switch env {
case "":
os.Exit(m.Run())
case executableEnvValueMatch:
// First chdir to another path.
dir := "/"
if runtime.GOOS == "windows" {
dir = filepath.VolumeName(".")
}
os.Chdir(dir)
if ep, err := Executable(); err != nil {
fmt.Fprint(os.Stderr, "ERROR: ", err)
} else {
fmt.Fprint(os.Stderr, ep)
}
case executableEnvValueDelete:
bb := make([]byte, 1)
var err error
n, err := os.Stdin.Read(bb)
if err != nil {
fmt.Fprint(os.Stderr, "ERROR: ", err)
os.Exit(2)
}
if n != 1 {
fmt.Fprint(os.Stderr, "ERROR: n != 1, n == ", n)
os.Exit(2)
}
if ep, err := Executable(); err != nil {
fmt.Fprint(os.Stderr, "ERROR: ", err)
} else {
fmt.Fprint(os.Stderr, ep)
}
}
os.Exit(0)
}

Просмотреть файл

42
Godeps/_workspace/src/github.com/mattn/go-colorable/README.md сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,42 @@
# go-colorable
Colorable writer for windows.
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
This package is possible to handle escape sequence for ansi color on windows.
## Too Bad!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png)
## So Good!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png)
## Usage
```go
logrus.SetOutput(colorable.NewColorableStdout())
logrus.Info("succeeded")
logrus.Warn("not correct")
logrus.Error("something error")
logrus.Fatal("panic")
```
You can compile above code on non-windows OSs.
## Installation
```
$ go get github.com/mattn/go-colorable
```
# License
MIT
# Author
Yasuhiro Matsumoto (a.k.a mattn)

16
Godeps/_workspace/src/github.com/mattn/go-colorable/colorable_others.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,16 @@
// +build !windows
package colorable
import (
"io"
"os"
)
func NewColorableStdout() io.Writer {
return os.Stdout
}
func NewColorableStderr() io.Writer {
return os.Stderr
}

594
Godeps/_workspace/src/github.com/mattn/go-colorable/colorable_windows.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,594 @@
package colorable
import (
"bytes"
"fmt"
"io"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/github/hub/Godeps/_workspace/src/github.com/mattn/go-isatty"
)
const (
foregroundBlue = 0x1
foregroundGreen = 0x2
foregroundRed = 0x4
foregroundIntensity = 0x8
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
backgroundBlue = 0x10
backgroundGreen = 0x20
backgroundRed = 0x40
backgroundIntensity = 0x80
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
)
type wchar uint16
type short int16
type dword uint32
type word uint16
type coord struct {
x short
y short
}
type smallRect struct {
left short
top short
right short
bottom short
}
type consoleScreenBufferInfo struct {
size coord
cursorPosition coord
attributes word
window smallRect
maximumWindowSize coord
}
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
)
type Writer struct {
out io.Writer
handle syscall.Handle
lastbuf bytes.Buffer
oldattr word
}
func NewColorableStdout() io.Writer {
var csbi consoleScreenBufferInfo
out := os.Stdout
if !isatty.IsTerminal(out.Fd()) {
return out
}
handle := syscall.Handle(out.Fd())
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
return &Writer{out: out, handle: handle, oldattr: csbi.attributes}
}
func NewColorableStderr() io.Writer {
var csbi consoleScreenBufferInfo
out := os.Stderr
if !isatty.IsTerminal(out.Fd()) {
return out
}
handle := syscall.Handle(out.Fd())
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
return &Writer{out: out, handle: handle, oldattr: csbi.attributes}
}
var color256 = map[int]int{
0: 0x000000,
1: 0x800000,
2: 0x008000,
3: 0x808000,
4: 0x000080,
5: 0x800080,
6: 0x008080,
7: 0xc0c0c0,
8: 0x808080,
9: 0xff0000,
10: 0x00ff00,
11: 0xffff00,
12: 0x0000ff,
13: 0xff00ff,
14: 0x00ffff,
15: 0xffffff,
16: 0x000000,
17: 0x00005f,
18: 0x000087,
19: 0x0000af,
20: 0x0000d7,
21: 0x0000ff,
22: 0x005f00,
23: 0x005f5f,
24: 0x005f87,
25: 0x005faf,
26: 0x005fd7,
27: 0x005fff,
28: 0x008700,
29: 0x00875f,
30: 0x008787,
31: 0x0087af,
32: 0x0087d7,
33: 0x0087ff,
34: 0x00af00,
35: 0x00af5f,
36: 0x00af87,
37: 0x00afaf,
38: 0x00afd7,
39: 0x00afff,
40: 0x00d700,
41: 0x00d75f,
42: 0x00d787,
43: 0x00d7af,
44: 0x00d7d7,
45: 0x00d7ff,
46: 0x00ff00,
47: 0x00ff5f,
48: 0x00ff87,
49: 0x00ffaf,
50: 0x00ffd7,
51: 0x00ffff,
52: 0x5f0000,
53: 0x5f005f,
54: 0x5f0087,
55: 0x5f00af,
56: 0x5f00d7,
57: 0x5f00ff,
58: 0x5f5f00,
59: 0x5f5f5f,
60: 0x5f5f87,
61: 0x5f5faf,
62: 0x5f5fd7,
63: 0x5f5fff,
64: 0x5f8700,
65: 0x5f875f,
66: 0x5f8787,
67: 0x5f87af,
68: 0x5f87d7,
69: 0x5f87ff,
70: 0x5faf00,
71: 0x5faf5f,
72: 0x5faf87,
73: 0x5fafaf,
74: 0x5fafd7,
75: 0x5fafff,
76: 0x5fd700,
77: 0x5fd75f,
78: 0x5fd787,
79: 0x5fd7af,
80: 0x5fd7d7,
81: 0x5fd7ff,
82: 0x5fff00,
83: 0x5fff5f,
84: 0x5fff87,
85: 0x5fffaf,
86: 0x5fffd7,
87: 0x5fffff,
88: 0x870000,
89: 0x87005f,
90: 0x870087,
91: 0x8700af,
92: 0x8700d7,
93: 0x8700ff,
94: 0x875f00,
95: 0x875f5f,
96: 0x875f87,
97: 0x875faf,
98: 0x875fd7,
99: 0x875fff,
100: 0x878700,
101: 0x87875f,
102: 0x878787,
103: 0x8787af,
104: 0x8787d7,
105: 0x8787ff,
106: 0x87af00,
107: 0x87af5f,
108: 0x87af87,
109: 0x87afaf,
110: 0x87afd7,
111: 0x87afff,
112: 0x87d700,
113: 0x87d75f,
114: 0x87d787,
115: 0x87d7af,
116: 0x87d7d7,
117: 0x87d7ff,
118: 0x87ff00,
119: 0x87ff5f,
120: 0x87ff87,
121: 0x87ffaf,
122: 0x87ffd7,
123: 0x87ffff,
124: 0xaf0000,
125: 0xaf005f,
126: 0xaf0087,
127: 0xaf00af,
128: 0xaf00d7,
129: 0xaf00ff,
130: 0xaf5f00,
131: 0xaf5f5f,
132: 0xaf5f87,
133: 0xaf5faf,
134: 0xaf5fd7,
135: 0xaf5fff,
136: 0xaf8700,
137: 0xaf875f,
138: 0xaf8787,
139: 0xaf87af,
140: 0xaf87d7,
141: 0xaf87ff,
142: 0xafaf00,
143: 0xafaf5f,
144: 0xafaf87,
145: 0xafafaf,
146: 0xafafd7,
147: 0xafafff,
148: 0xafd700,
149: 0xafd75f,
150: 0xafd787,
151: 0xafd7af,
152: 0xafd7d7,
153: 0xafd7ff,
154: 0xafff00,
155: 0xafff5f,
156: 0xafff87,
157: 0xafffaf,
158: 0xafffd7,
159: 0xafffff,
160: 0xd70000,
161: 0xd7005f,
162: 0xd70087,
163: 0xd700af,
164: 0xd700d7,
165: 0xd700ff,
166: 0xd75f00,
167: 0xd75f5f,
168: 0xd75f87,
169: 0xd75faf,
170: 0xd75fd7,
171: 0xd75fff,
172: 0xd78700,
173: 0xd7875f,
174: 0xd78787,
175: 0xd787af,
176: 0xd787d7,
177: 0xd787ff,
178: 0xd7af00,
179: 0xd7af5f,
180: 0xd7af87,
181: 0xd7afaf,
182: 0xd7afd7,
183: 0xd7afff,
184: 0xd7d700,
185: 0xd7d75f,
186: 0xd7d787,
187: 0xd7d7af,
188: 0xd7d7d7,
189: 0xd7d7ff,
190: 0xd7ff00,
191: 0xd7ff5f,
192: 0xd7ff87,
193: 0xd7ffaf,
194: 0xd7ffd7,
195: 0xd7ffff,
196: 0xff0000,
197: 0xff005f,
198: 0xff0087,
199: 0xff00af,
200: 0xff00d7,
201: 0xff00ff,
202: 0xff5f00,
203: 0xff5f5f,
204: 0xff5f87,
205: 0xff5faf,
206: 0xff5fd7,
207: 0xff5fff,
208: 0xff8700,
209: 0xff875f,
210: 0xff8787,
211: 0xff87af,
212: 0xff87d7,
213: 0xff87ff,
214: 0xffaf00,
215: 0xffaf5f,
216: 0xffaf87,
217: 0xffafaf,
218: 0xffafd7,
219: 0xffafff,
220: 0xffd700,
221: 0xffd75f,
222: 0xffd787,
223: 0xffd7af,
224: 0xffd7d7,
225: 0xffd7ff,
226: 0xffff00,
227: 0xffff5f,
228: 0xffff87,
229: 0xffffaf,
230: 0xffffd7,
231: 0xffffff,
232: 0x080808,
233: 0x121212,
234: 0x1c1c1c,
235: 0x262626,
236: 0x303030,
237: 0x3a3a3a,
238: 0x444444,
239: 0x4e4e4e,
240: 0x585858,
241: 0x626262,
242: 0x6c6c6c,
243: 0x767676,
244: 0x808080,
245: 0x8a8a8a,
246: 0x949494,
247: 0x9e9e9e,
248: 0xa8a8a8,
249: 0xb2b2b2,
250: 0xbcbcbc,
251: 0xc6c6c6,
252: 0xd0d0d0,
253: 0xdadada,
254: 0xe4e4e4,
255: 0xeeeeee,
}
func (w *Writer) Write(data []byte) (n int, err error) {
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
er := bytes.NewBuffer(data)
loop:
for {
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
if r1 == 0 {
break loop
}
c1, _, err := er.ReadRune()
if err != nil {
break loop
}
if c1 != 0x1b {
fmt.Fprint(w.out, string(c1))
continue
}
c2, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
break loop
}
if c2 != 0x5b {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
continue
}
var buf bytes.Buffer
var m rune
for {
c, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
w.lastbuf.Write(buf.Bytes())
break loop
}
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
m = c
break
}
buf.Write([]byte(string(c)))
}
switch m {
case 'm':
attr := csbi.attributes
cs := buf.String()
if cs == "" {
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
continue
}
token := strings.Split(cs, ";")
for i, ns := range token {
if n, err = strconv.Atoi(ns); err == nil {
switch {
case n == 0 || n == 100:
attr = w.oldattr
case 1 <= n && n <= 5:
attr |= foregroundIntensity
case n == 7:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
case 22 == n || n == 25 || n == 25:
attr |= foregroundIntensity
case n == 27:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
case 30 <= n && n <= 37:
attr = (attr & backgroundMask)
if (n-30)&1 != 0 {
attr |= foregroundRed
}
if (n-30)&2 != 0 {
attr |= foregroundGreen
}
if (n-30)&4 != 0 {
attr |= foregroundBlue
}
case n == 38: // set foreground color.
if i < len(token)-2 && token[i+1] == "5" {
if n256, err := strconv.Atoi(token[i+2]); err == nil {
if n256foreAttr == nil {
n256setup()
}
attr &= backgroundMask
attr |= n256foreAttr[n256]
i += 2
}
} else {
attr = attr & (w.oldattr & backgroundMask)
}
case n == 39: // reset foreground color.
attr &= backgroundMask
attr |= w.oldattr & foregroundMask
case 40 <= n && n <= 47:
attr = (attr & foregroundMask)
if (n-40)&1 != 0 {
attr |= backgroundRed
}
if (n-40)&2 != 0 {
attr |= backgroundGreen
}
if (n-40)&4 != 0 {
attr |= backgroundBlue
}
case n == 48: // set background color.
if i < len(token)-2 && token[i+1] == "5" {
if n256, err := strconv.Atoi(token[i+2]); err == nil {
if n256backAttr == nil {
n256setup()
}
attr &= foregroundMask
attr |= n256backAttr[n256]
i += 2
}
} else {
attr = attr & (w.oldattr & foregroundMask)
}
case n == 49: // reset foreground color.
attr &= foregroundMask
attr |= w.oldattr & backgroundMask
}
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
}
}
}
}
return len(data) - w.lastbuf.Len(), nil
}
type consoleColor struct {
red bool
green bool
blue bool
intensity bool
}
func minmax3(a, b, c int) (min, max int) {
if a < b {
if b < c {
return a, c
} else if a < c {
return a, b
} else {
return c, b
}
} else {
if a < c {
return b, c
} else if b < c {
return b, a
} else {
return c, a
}
}
}
func toConsoleColor(rgb int) (c consoleColor) {
r, g, b := (rgb&0xFF0000)>>16, (rgb&0x00FF00)>>8, rgb&0x0000FF
min, max := minmax3(r, g, b)
a := (min + max) / 2
if r < 128 && g < 128 && b < 128 {
if r >= a {
c.red = true
}
if g >= a {
c.green = true
}
if b >= a {
c.blue = true
}
// non-intensed white is lighter than intensed black, so swap those.
if c.red && c.green && c.blue {
c.red, c.green, c.blue = false, false, false
c.intensity = true
}
} else {
if min < 128 {
min = 128
a = (min + max) / 2
}
if r >= a {
c.red = true
}
if g >= a {
c.green = true
}
if b >= a {
c.blue = true
}
c.intensity = true
// intensed black is darker than non-intensed white, so swap those.
if !c.red && !c.green && !c.blue {
c.red, c.green, c.blue = true, true, true
c.intensity = false
}
}
return c
}
func (c consoleColor) foregroundAttr() (attr word) {
if c.red {
attr |= foregroundRed
}
if c.green {
attr |= foregroundGreen
}
if c.blue {
attr |= foregroundBlue
}
if c.intensity {
attr |= foregroundIntensity
}
return
}
func (c consoleColor) backgroundAttr() (attr word) {
if c.red {
attr |= backgroundRed
}
if c.green {
attr |= backgroundGreen
}
if c.blue {
attr |= backgroundBlue
}
if c.intensity {
attr |= backgroundIntensity
}
return
}
var n256foreAttr []word
var n256backAttr []word
func n256setup() {
n256foreAttr = make([]word, 256)
n256backAttr = make([]word, 256)
for i, rgb := range color256 {
c := toConsoleColor(rgb)
n256foreAttr[i] = c.foregroundAttr()
n256backAttr[i] = c.backgroundAttr()
}
}

Просмотреть файл

@ -286,6 +286,9 @@ the API and exchange it for an OAuth token, which it saves in "~/.config/hub".
To avoid being prompted, use **GITHUB_USER** and **GITHUB_PASSWORD** environment
variables.
Alternatively, you may provide **GITHUB_TOKEN**, an access token with
**repo** permissions. This will not be written to `~/.config/hub`.
### HTTPS instead of git protocol
If you prefer the HTTPS protocol for GitHub repositories, you can set

Просмотреть файл

@ -2,12 +2,9 @@ package commands
import (
"fmt"
"io/ioutil"
"strings"
"github.com/github/hub/cmd"
flag "github.com/github/hub/Godeps/_workspace/src/github.com/ogier/pflag"
)
type Args struct {
@ -24,7 +21,7 @@ type Args struct {
func (a *Args) Words() []string {
aa := make([]string, 0)
for _, p := range a.Params {
if !strings.HasPrefix(p, "-") {
if !looksLikeFlag(p) {
aa = append(aa, p)
}
}
@ -171,7 +168,8 @@ func NewArgs(args []string) *Args {
globalFlags []string
)
slurpGlobalFlags(&args, &globalFlags, &noop)
slurpGlobalFlags(&args, &globalFlags)
noop = removeValue(&globalFlags, noopFlag)
if len(args) == 0 {
params = []string{}
@ -191,90 +189,42 @@ func NewArgs(args []string) *Args {
}
}
func slurpGlobalFlags(args *[]string, globalFlags *[]string, noop *bool) {
var (
globalFlagSet flag.FlagSet
const (
noopFlag = "--noop"
versionFlag = "--version"
helpFlag = "--help"
configFlag = "-c"
chdirFlag = "-C"
flagPrefix = "-"
)
configParam mapValue = make(mapValue)
paginate bool
noPaginate bool
noReplaceObjects bool
bare bool
version bool
help bool
func looksLikeFlag(value string) bool {
return strings.HasPrefix(value, flagPrefix)
}
execPath string
gitDir string
workTree string
)
func slurpGlobalFlags(args *[]string, globalFlags *[]string) {
slurpNextValue := false
commandIndex := 0
globalFlagSet.BoolVarP(noop, "noop", "", false, "")
globalFlagSet.VarP(configParam, "", "c", "")
globalFlagSet.BoolVarP(&paginate, "paginate", "p", false, "")
globalFlagSet.BoolVarP(&noPaginate, "no-pager", "", false, "")
globalFlagSet.BoolVarP(&noReplaceObjects, "no-replace-objects", "", false, "")
globalFlagSet.BoolVarP(&bare, "bare", "", false, "")
globalFlagSet.BoolVarP(&version, "version", "", false, "")
globalFlagSet.BoolVarP(&help, "help", "", false, "")
globalFlagSet.StringVarP(&execPath, "exec-path", "", "", "")
globalFlagSet.StringVarP(&gitDir, "git-dir", "", "", "")
globalFlagSet.StringVarP(&workTree, "work-tree", "", "", "")
globalFlagSet.SetOutput(ioutil.Discard)
globalFlagSet.Init("hub", flag.ContinueOnError)
aa := make([]string, 0)
err := globalFlagSet.Parse(*args)
if err == nil {
aa = globalFlagSet.Args()
} else {
aa = *args
for i, arg := range *args {
if slurpNextValue {
commandIndex = i + 1
slurpNextValue = false
} else if arg == versionFlag || arg == helpFlag || !looksLikeFlag(arg) {
break
} else {
commandIndex = i + 1
if arg == configFlag || arg == chdirFlag {
slurpNextValue = true
}
}
}
// manipulate global flags
if version {
aa = append([]string{"version"}, aa...)
if commandIndex > 0 {
aa := *args
*globalFlags = aa[0:commandIndex]
*args = aa[commandIndex:]
}
if help {
aa = append([]string{"help"}, aa...)
}
for k, v := range configParam {
*globalFlags = append(*globalFlags, "-c")
*globalFlags = append(*globalFlags, fmt.Sprintf("%s=%s", k, v))
}
if paginate {
*globalFlags = append(*globalFlags, "--paginate")
}
if noPaginate {
*globalFlags = append(*globalFlags, "--no-pager")
}
if noReplaceObjects {
*globalFlags = append(*globalFlags, "--no-replace-objects")
}
if bare {
*globalFlags = append(*globalFlags, "--bare")
}
if execPath != "" {
*globalFlags = append(*globalFlags, "--exec-path", execPath)
}
if gitDir != "" {
*globalFlags = append(*globalFlags, "--git-dir", gitDir)
}
if workTree != "" {
*globalFlags = append(*globalFlags, "--work-tree", workTree)
}
*args = aa
}
func removeItem(slice []string, index int) (newSlice []string, item string) {
@ -287,3 +237,15 @@ func removeItem(slice []string, index int) (newSlice []string, item string) {
return newSlice, item
}
func removeValue(slice *[]string, value string) (found bool) {
aa := *slice
for i := len(aa) - 1; i >= 0; i-- {
arg := aa[i]
if arg == value {
found = true
*slice, _ = removeItem(*slice, i)
}
}
return found
}

Просмотреть файл

@ -1,7 +1,6 @@
package commands
import (
"strings"
"testing"
"github.com/github/hub/Godeps/_workspace/src/github.com/bmizerany/assert"
@ -20,34 +19,22 @@ func TestNewArgs(t *testing.T) {
assert.Equal(t, "command", args.Command)
assert.Equal(t, 1, args.ParamsSize())
args = NewArgs([]string{"--noop", "command", "args"})
assert.Equal(t, "command", args.Command)
assert.Equal(t, 1, args.ParamsSize())
assert.T(t, args.Noop)
args = NewArgs([]string{"--version"})
assert.Equal(t, "version", args.Command)
assert.Equal(t, "--version", args.Command)
assert.Equal(t, 0, args.ParamsSize())
args = NewArgs([]string{"--help"})
assert.Equal(t, "help", args.Command)
assert.Equal(t, "--help", args.Command)
assert.Equal(t, 0, args.ParamsSize())
args = NewArgs([]string{"--noop", "--version"})
assert.T(t, args.Noop)
assert.Equal(t, "version", args.Command)
args = NewArgs([]string{"-c", "foo=bar", "--git-dir=path", "--bare"})
assert.Equal(t, 5, len(args.GlobalFlags))
assert.Equal(t, "-c foo=bar --bare --git-dir path", strings.Join(args.GlobalFlags, " "))
}
func TestArgs_Words(t *testing.T) {
args := NewArgs([]string{"--no-ff", "master"})
args := NewArgs([]string{"merge", "--no-ff", "master", "-m", "message"})
a := args.Words()
assert.Equal(t, 1, len(a))
assert.Equal(t, 2, len(a))
assert.Equal(t, "master", a[0])
assert.Equal(t, "message", a[1])
}
func TestArgs_Insert(t *testing.T) {
@ -86,3 +73,35 @@ func TestArgs_Remove(t *testing.T) {
assert.Equal(t, "2", args.FirstParam())
assert.Equal(t, "4", args.GetParam(1))
}
func TestArgs_GlobalFlags(t *testing.T) {
args := NewArgs([]string{"-c", "key=value", "status", "-s", "-b"})
assert.Equal(t, "status", args.Command)
assert.Equal(t, []string{"-c", "key=value"}, args.GlobalFlags)
assert.Equal(t, []string{"-s", "-b"}, args.Params)
assert.Equal(t, false, args.Noop)
}
func TestArgs_GlobalFlags_Noop(t *testing.T) {
args := NewArgs([]string{"-c", "key=value", "--noop", "--literal-pathspecs", "status", "-s", "-b"})
assert.Equal(t, "status", args.Command)
assert.Equal(t, []string{"-c", "key=value", "--literal-pathspecs"}, args.GlobalFlags)
assert.Equal(t, []string{"-s", "-b"}, args.Params)
assert.Equal(t, true, args.Noop)
}
func TestArgs_GlobalFlags_NoopTwice(t *testing.T) {
args := NewArgs([]string{"--noop", "--bare", "--noop", "status"})
assert.Equal(t, "status", args.Command)
assert.Equal(t, []string{"--bare"}, args.GlobalFlags)
assert.Equal(t, 0, len(args.Params))
assert.Equal(t, true, args.Noop)
}
func TestArgs_GlobalFlags_Repeated(t *testing.T) {
args := NewArgs([]string{"-C", "mydir", "-c", "a=b", "--bare", "-c", "c=d", "-c", "e=f", "status"})
assert.Equal(t, "status", args.Command)
assert.Equal(t, []string{"-C", "mydir", "-c", "a=b", "--bare", "-c", "c=d", "-c", "e=f"}, args.GlobalFlags)
assert.Equal(t, 0, len(args.Params))
assert.Equal(t, false, args.Noop)
}

Просмотреть файл

@ -2,9 +2,9 @@ package commands
import (
"fmt"
"net/url"
"regexp"
"strings"
"net/url"
"github.com/github/hub/github"
"github.com/github/hub/utils"
@ -26,10 +26,12 @@ With "-u", outputs the URL rather than opening the browser.
var (
flagCompareURLOnly bool
flagCompareBase string
)
func init() {
cmdCompare.Flag.BoolVarP(&flagCompareURLOnly, "url-only", "u", false, "URL only")
cmdCompare.Flag.StringVarP(&flagCompareBase, "base", "b", "", "BASE")
CmdRunner.Use(cmdCompare)
}
@ -57,20 +59,33 @@ func compare(command *Command, args *Args) {
branch, project, err = localRepo.RemoteBranchAndProject("", false)
utils.Check(err)
usageHelp := func() {
utils.Check(fmt.Errorf("Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]"))
}
if args.IsParamsEmpty() {
if branch != nil && !branch.IsMaster() {
r = branch.ShortName()
if branch == nil ||
(branch.IsMaster() && flagCompareBase == "") ||
(flagCompareBase == branch.ShortName()) {
usageHelp()
} else {
err = fmt.Errorf("Usage: hub compare [USER] [<START>...]<END>")
utils.Check(err)
r = branch.ShortName()
if flagCompareBase != "" {
r = parseCompareRange(flagCompareBase + "..." + r)
}
}
} else {
r = parseCompareRange(args.RemoveParam(args.ParamsSize() - 1))
if args.IsParamsEmpty() {
project, err = localRepo.CurrentProject()
utils.Check(err)
if flagCompareBase != "" {
usageHelp()
} else {
project = github.NewProject(args.RemoveParam(args.ParamsSize()-1), "", "")
r = parseCompareRange(args.RemoveParam(args.ParamsSize() - 1))
if args.IsParamsEmpty() {
project, err = localRepo.CurrentProject()
utils.Check(err)
} else {
project = github.NewProject(args.RemoveParam(args.ParamsSize()-1), "", "")
}
}
}

Просмотреть файл

@ -17,7 +17,7 @@ var cmdHelp = &Command{
func init() {
cmdHelp.Run = runHelp
CmdRunner.Use(cmdHelp)
CmdRunner.Use(cmdHelp, "--help")
}
func runHelp(cmd *Command, args *Args) {

Просмотреть файл

@ -95,8 +95,8 @@ func writeIssueTitleAndBody(project *github.Project) (string, string, error) {
message := `
# Creating issue for %s.
#
# Write a message for this issue. The first block
# of text is the title and the rest is description.
# Write a message for this issue. The first block of
# text is the title and the rest is the description.
`
message = fmt.Sprintf(message, project.Name)

Просмотреть файл

@ -77,7 +77,7 @@ func transformMergeArgs(args *Args) error {
mergeMsg := fmt.Sprintf("Merge pull request #%v from %s\n\n%s", id, mergeHead, pullRequest.Title)
args.AppendParams(mergeHead, "-m", mergeMsg)
if args.IndexOfParam("--ff-only") == -1 && args.IndexOfParam("--squash") == -1 {
if args.IndexOfParam("--ff-only") == -1 && args.IndexOfParam("--squash") == -1 && args.IndexOfParam("--ff") == -1 {
i := args.IndexOfParam("-m")
args.InsertParam(i, "--no-ff")
}

Просмотреть файл

@ -13,7 +13,7 @@ import (
var cmdPullRequest = &Command{
Run: pullRequest,
Usage: "pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-o] [-b <BASE>] [-h <HEAD>] [-a <USER>]",
Usage: "pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-o] [-b <BASE>] [-h <HEAD>] [-a <USER>] [-M <MILESTONE>] [-l <LABELS>]",
Short: "Open a pull request on GitHub",
Long: `Opens a pull request on GitHub for the project that the "origin" remote
points to. The default head of the pull request is the current branch.
@ -31,7 +31,8 @@ If instead of normal <TITLE> an issue number is given with "-i", the pull
request will be attached to an existing GitHub issue. Alternatively, instead
of title you can paste a full URL to an issue on GitHub.
You can assign the new pull request to a user with "-a <USER>".
You can assign the new pull request to a user with "-a <USER>", add it to a
milestone with "-M <MILESTONE>" and apply labels with "-l <LABELS>".
`,
}
@ -41,9 +42,11 @@ var (
flagPullRequestIssue,
flagPullRequestMessage,
flagPullRequestAssignee,
flagPullRequestLabels,
flagPullRequestFile string
flagPullRequestBrowse,
flagPullRequestForce bool
flagPullRequestMilestone uint64
)
func init() {
@ -55,6 +58,8 @@ func init() {
cmdPullRequest.Flag.BoolVarP(&flagPullRequestForce, "force", "f", false, "FORCE")
cmdPullRequest.Flag.StringVarP(&flagPullRequestFile, "file", "F", "", "FILE")
cmdPullRequest.Flag.StringVarP(&flagPullRequestAssignee, "assign", "a", "", "USER")
cmdPullRequest.Flag.Uint64VarP(&flagPullRequestMilestone, "milestone", "M", 0, "MILESTONE")
cmdPullRequest.Flag.StringVarP(&flagPullRequestLabels, "labels", "l", "", "LABELS")
CmdRunner.Use(cmdPullRequest)
}
@ -215,8 +220,16 @@ func pullRequest(cmd *Command, args *Args) {
pullRequestURL = pr.HTMLURL
if flagPullRequestAssignee != "" {
err = client.UpdateIssueAssignee(baseProject, pr.Number, flagPullRequestAssignee)
if flagPullRequestAssignee != "" || flagPullRequestMilestone > 0 ||
flagPullRequestLabels != "" {
params := octokit.IssueParams{
Assignee: flagPullRequestAssignee,
Milestone: flagPullRequestMilestone,
Labels: strings.Split(flagPullRequestLabels, ","),
}
err = client.UpdateIssue(baseProject, pr.Number, params)
utils.Check(err)
}
}

Просмотреть файл

@ -13,7 +13,7 @@ const pullRequestTmpl = `{{if .InitMsg}}{{.InitMsg}}
{{.CS}} Requesting a pull to {{.Base}} from {{.Head}}
{{.CS}}
{{.CS}} Write a message for this pull request. The first block
{{.CS}} of text is the title and the rest is description.{{if .HasCommitLogs}}
{{.CS}} of text is the title and the rest is the description.{{if .HasCommitLogs}}
{{.CS}}
{{.CS}} Changes:
{{.CS}}{{if .HasCommitLogs}}

Просмотреть файл

@ -15,7 +15,7 @@ func TestRenderPullRequestTpl(t *testing.T) {
# Requesting a pull to base from head
#
# Write a message for this pull request. The first block
# of text is the title and the rest is description.
# of text is the title and the rest is the description.
#
# Changes:
#

Просмотреть файл

@ -7,8 +7,8 @@ import (
const releaseTmpl = `{{.CS}} Creating release {{.TagName}} for {{.ProjectName}} from {{.BranchName}}
{{.CS}}
{{.CS}} Write a message for this release. The first block
{{.CS}} of text is the title and the rest is description.`
{{.CS}} Write a message for this release. The first block of
{{.CS}} text is the title and the rest is the description.`
type releaseMsg struct {
CS string

Просмотреть файл

@ -12,7 +12,7 @@ func TestRenderReleaseTpl(t *testing.T) {
expMsg := `# Creating release 1.0 for hub from master
#
# Write a message for this release. The first block
# of text is the title and the rest is description.`
# Write a message for this release. The first block of
# text is the title and the rest is the description.`
assert.Equal(t, expMsg, msg)
}

Просмотреть файл

@ -54,8 +54,11 @@ func (r *Runner) All() map[string]*Command {
return r.commands
}
func (r *Runner) Use(command *Command) {
func (r *Runner) Use(command *Command, aliases ...string) {
r.commands[command.Name()] = command
if len(aliases) > 0 {
r.commands[aliases[0]] = command
}
}
func (r *Runner) Lookup(name string) *Command {

Просмотреть файл

@ -18,6 +18,7 @@ import (
"github.com/github/hub/github"
"github.com/github/hub/ui"
"github.com/github/hub/utils"
"github.com/github/hub/version"
)
const (
@ -27,15 +28,15 @@ const (
var EnableAutoUpdate = false
func NewUpdater() *Updater {
version := os.Getenv("HUB_VERSION")
if version == "" {
version = Version
ver := os.Getenv("HUB_VERSION")
if ver == "" {
ver = version.Version
}
timestampPath := filepath.Join(os.Getenv("HOME"), ".config", "hub-update")
return &Updater{
Host: github.DefaultGitHubHost(),
CurrentVersion: version,
CurrentVersion: ver,
timestampPath: timestampPath,
}
}

Просмотреть файл

@ -1,16 +1,12 @@
package commands
import (
"fmt"
"os"
"github.com/github/hub/git"
"github.com/github/hub/ui"
"github.com/github/hub/utils"
"github.com/github/hub/version"
)
var Version = "2.2.1"
var cmdVersion = &Command{
Run: runVersion,
Usage: "version",
@ -19,17 +15,10 @@ var cmdVersion = &Command{
}
func init() {
CmdRunner.Use(cmdVersion)
CmdRunner.Use(cmdVersion, "--version")
}
func runVersion(cmd *Command, args *Args) {
gitVersion, err := git.Version()
utils.Check(err)
ghVersion := fmt.Sprintf("hub version %s", Version)
ui.Println(gitVersion)
ui.Println(ghVersion)
ui.Println(version.FullVersion())
os.Exit(0)
}

Просмотреть файл

@ -225,13 +225,13 @@ EOF
fi
}
# hub pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-b <BASE>] [-h <HEAD>]
# hub pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-b <BASE>] [-h <HEAD>] [-a <USER>] [-M <MILESTONE>] [-l <LABELS>]
_git_pull_request() {
local i c=2 flags="-f -m -F -i -b -h"
local i c=2 flags="-f -m -F -i -b -h -a -M -l"
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
-m|-F|-i|-b|-h)
-m|-F|-i|-b|-h|-a|-M|-l)
((c++))
flags=${flags/$i/}
;;
@ -245,7 +245,7 @@ EOF
-i)
COMPREPLY=()
;;
-b|-h)
-b|-h|-a|-M|-l)
# (Doesn't seem to need this...)
# Uncomment the following line when 'owner/repo:[TAB]' misbehaved
#_get_comp_words_by_ref -n : cur

Просмотреть файл

@ -58,6 +58,9 @@ __hub_setup_zsh_fns () {
- set1 \
'-m[message]' \
'-F[file]' \
'-a[user]' \
'-M[milestone]' \
'-l[labels]' \
- set2 \
'-i[issue]:issue number:' \
- set3 \

Просмотреть файл

@ -117,6 +117,44 @@ Feature: OAuth authentication
Then the output should not contain "github.com password for mislav"
And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"
Scenario: Credentials from GITHUB_TOKEN
Given the GitHub API server:
"""
get('/user') {
halt 401 unless request.env["HTTP_AUTHORIZATION"] == "token OTOKEN"
json :login => 'mislav'
}
post('/user/repos') {
halt 401 unless request.env["HTTP_AUTHORIZATION"] == "token OTOKEN"
json :full_name => 'mislav/dotfiles'
}
"""
Given $GITHUB_TOKEN is "OTOKEN"
When I successfully run `hub create`
Then the output should not contain "github.com password"
And the output should not contain "github.com username"
And the file "../home/.config/hub" should not exist
Scenario: Credentials from GITHUB_TOKEN override those from config file
Given I am "mislav" on github.com with OAuth token "OTOKEN"
Given the GitHub API server:
"""
get('/user') {
halt 401 unless request.env["HTTP_AUTHORIZATION"] == "token PTOKEN"
json :login => 'parkr'
}
get('/repos/parkr/dotfiles') {
halt 401 unless request.env["HTTP_AUTHORIZATION"] == "token PTOKEN"
json :private => false,
:permissions => { :push => true }
}
"""
Given $GITHUB_TOKEN is "PTOKEN"
When I successfully run `hub clone dotfiles`
Then it should clone "git@github.com:parkr/dotfiles.git"
And the file "../home/.config/hub" should contain "user: mislav"
And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"
Scenario: Wrong password
Given the GitHub API server:
"""

Просмотреть файл

@ -18,12 +18,12 @@ Feature: bash tab-completion
Scenario: Offers pull-request flags
When I type "git pull-request -" and press <Tab>
When I press <Tab> again
Then the completion menu should offer "-F -b -f -h -i -m" unsorted
Then the completion menu should offer "-F -b -f -h -i -m -a -M -l" unsorted
Scenario: Doesn't offer already used pull-request flags
When I type "git pull-request -F myfile -h mybranch -" and press <Tab>
When I press <Tab> again
Then the completion menu should offer "-b -f -i -m" unsorted
Then the completion menu should offer "-b -f -i -m -a -M -l" unsorted
Scenario: Browse to issues
When I type "git browse -- i" and press <Tab>

Просмотреть файл

@ -23,7 +23,7 @@ Feature: hub compare
Then the exit status should be 1
And the stderr should contain:
"""
hub compare [USER] [<START>...]<END>
Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]
"""
Scenario: Can't compare default branch to self
@ -33,7 +33,7 @@ Feature: hub compare
Then the exit status should be 1
And the stderr should contain:
"""
hub compare [USER] [<START>...]<END>
Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]
"""
Scenario: No args, has upstream branch
@ -63,6 +63,41 @@ Feature: hub compare
https://github.com/mislav/dotfiles/compare/1.0...fix\n
"""
Scenario: Compare base in branch that is not master
Given I am on the "feature" branch with upstream "origin/experimental"
And git "push.default" is set to "upstream"
When I successfully run `hub compare -b master`
Then there should be no output
And "open https://github.com/mislav/dotfiles/compare/master...experimental" should be run
Scenario: Compare base in master branch
Given I am on the "master" branch with upstream "origin/master"
And git "push.default" is set to "upstream"
When I successfully run `hub compare -b experimental`
Then there should be no output
And "open https://github.com/mislav/dotfiles/compare/experimental...master" should be run
Scenario: Compare base with same branch as the current branch
Given I am on the "feature" branch with upstream "origin/experimental"
And git "push.default" is set to "upstream"
When I run `hub compare -b experimental`
Then "open https://github.com/mislav/dotfiles/compare/experimental...experimental" should not be run
And the exit status should be 1
And the stderr should contain:
"""
Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]
"""
Scenario: Compare base with parameters
Given I am on the "master" branch with upstream "origin/master"
When I run `hub compare -b master experimental..master`
Then "open https://github.com/mislav/dotfiles/compare/experimental...master" should not be run
And the exit status should be 1
And the stderr should contain:
"""
Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]
"""
Scenario: Compare 2-dots range for tags
When I successfully run `hub compare 1.0..fix`
Then there should be no output

Просмотреть файл

@ -83,6 +83,32 @@ Feature: hub merge
Fast-forward (no commit created; -m option ignored)
"""
Scenario: Merge pull request with --ff option
Given the GitHub API server:
"""
require 'json'
get('/repos/defunkt/hub/pulls/164') { json \
:head => {
:ref => "hub_merge",
:repo => {
:owner => { :login => "jfirebaugh" },
:name => "hub",
:private => false
}
},
:title => "Add `hub merge` command"
}
"""
And there is a commit named "jfirebaugh/hub_merge"
When I successfully run `hub merge --ff https://github.com/defunkt/hub/pull/164`
Then "git fetch git://github.com/jfirebaugh/hub.git +refs/heads/hub_merge:refs/remotes/jfirebaugh/hub_merge" should be run
And "git merge --ff jfirebaugh/hub_merge -m Merge pull request #164 from jfirebaugh/hub_merge" should be run
When I successfully run `git show -s --format=%B`
Then the output should contain:
"""
Fast-forward (no commit created; -m option ignored)
"""
Scenario: Merge private pull request
Given the GitHub API server:
"""

Просмотреть файл

@ -120,7 +120,7 @@ Feature: hub pull-request
# Requesting a pull to mislav:master from mislav:topic
#
# Write a message for this pull request. The first block
# of text is the title and the rest is description.
# of text is the title and the rest is the description.
#
# Changes:
#
@ -611,3 +611,35 @@ Feature: hub pull-request
"""
When I successfully run `hub pull-request -m hereyougo -a mislav`
Then the output should contain exactly "the://url\n"
Scenario: Pull request with milestone
Given I am on the "feature" branch with upstream "origin/feature"
Given the GitHub API server:
"""
post('/repos/mislav/coral/pulls') {
assert :head => "mislav:feature"
json :html_url => "the://url", :number => 1234
}
patch('/repos/mislav/coral/issues/1234') {
assert :milestone => 1234
json :html_url => "the://url"
}
"""
When I successfully run `hub pull-request -m hereyougo -M 1234`
Then the output should contain exactly "the://url\n"
Scenario: Pull request with labels
Given I am on the "feature" branch with upstream "origin/feature"
Given the GitHub API server:
"""
post('/repos/mislav/coral/pulls') {
assert :head => "mislav:feature"
json :html_url => "the://url", :number => 1234
}
patch('/repos/mislav/coral/issues/1234') {
assert :labels => ["feature", "release"]
json :html_url => "the://url"
}
"""
When I successfully run `hub pull-request -m hereyougo -l feature,release`
Then the output should contain exactly "the://url\n"

Просмотреть файл

@ -46,6 +46,8 @@ Before do
set_env 'GIT_EDITOR', 'false'
# reset current localization settings
set_env 'LANG', nil
# ignore current user's token
set_env 'GITHUB_TOKEN', nil
author_name = "Hub"
author_email = "hub@test.local"

Просмотреть файл

@ -26,6 +26,9 @@ Feature: zsh tab-completion
| -F | file |
| -i | issue |
| -f | force (skip check for local commits) |
| -a | user |
| -M | milestone |
| -l | labels |
Scenario: Completion of fork arguments
When I type "git fork -" and press <Tab>

Просмотреть файл

@ -37,6 +37,15 @@ func Dir() (string, error) {
}
func HasFile(segments ...string) bool {
// The blessed way to resolve paths within git dir since Git 2.5.0
output, err := gitOutput("rev-parse", "-q", "--git-path", filepath.Join(segments...))
if err == nil && output[0] != "--git-path" {
if _, err := os.Stat(output[0]); err == nil {
return true
}
}
// Fallback for older git versions
dir, err := Dir()
if err != nil {
return false

Просмотреть файл

@ -1,6 +1,7 @@
package git
import (
"os"
"strings"
"testing"
@ -18,7 +19,16 @@ func TestGitDir(t *testing.T) {
func TestGitEditor(t *testing.T) {
repo := fixtures.SetupTestRepo()
defer repo.TearDown()
editor := os.Getenv("GIT_EDITOR")
if err := os.Unsetenv("GIT_EDITOR"); err != nil {
t.Fatal(err)
}
defer func() {
repo.TearDown()
if err := os.Setenv("GIT_EDITOR", editor); err != nil {
t.Fatal(err)
}
}()
SetGlobalConfig("core.editor", "foo")
gitEditor, err := Editor()

Просмотреть файл

@ -428,7 +428,7 @@ func (client *Client) CreateIssue(project *Project, title, body string, labels [
return
}
func (client *Client) UpdateIssueAssignee(project *Project, issueNumber int, assignee string) (err error) {
func (client *Client) UpdateIssue(project *Project, issueNumber int, params octokit.IssueParams) (err error) {
url, err := octokit.RepoIssuesURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name, "number": issueNumber})
if err != nil {
return
@ -440,9 +440,6 @@ func (client *Client) UpdateIssueAssignee(project *Project, issueNumber int, ass
return
}
params := octokit.IssueParams{
Assignee: assignee,
}
_, result := api.Issues(client.requestURL(url)).Update(params)
if result.HasError() {
err = FormatError("updating issue", result.Err)

Просмотреть файл

@ -48,19 +48,55 @@ type Host struct {
}
type Config struct {
Hosts []Host `toml:"hosts"`
Hosts []*Host `toml:"hosts"`
}
func (c *Config) PromptForHost(host string) (h *Host, err error) {
token := c.DetectToken()
tokenFromEnv := token != ""
h = c.Find(host)
if h != nil {
return
if tokenFromEnv {
h.AccessToken = token
} else {
return
}
} else {
h = &Host{
Host: host,
AccessToken: token,
Protocol: "https",
}
c.Hosts = append(c.Hosts, h)
}
client := NewClientWithHost(h)
if !tokenFromEnv {
err = c.authorizeClient(client, host)
if err != nil {
return
}
}
currentUser, err := client.CurrentUser()
if err != nil {
return
}
h.User = currentUser.Login
if !tokenFromEnv {
err = newConfigService().Save(configsFile(), c)
}
return
}
func (c *Config) authorizeClient(client *Client, host string) (err error) {
user := c.PromptForUser(host)
pass := c.PromptForPassword(host, user)
client := NewClient(host)
var code, token string
for {
token, err = client.FindOrCreateToken(user, pass, code)
@ -78,28 +114,17 @@ func (c *Config) PromptForHost(host string) (h *Host, err error) {
}
}
if err != nil {
return
if err == nil {
client.Host.AccessToken = token
}
client.Host.AccessToken = token
currentUser, err := client.CurrentUser()
if err != nil {
return
}
h = &Host{
Host: host,
User: currentUser.Login,
AccessToken: token,
Protocol: "https",
}
c.Hosts = append(c.Hosts, *h)
err = newConfigService().Save(configsFile(), c)
return
}
func (c *Config) DetectToken() string {
return os.Getenv("GITHUB_TOKEN")
}
func (c *Config) PromptForUser(host string) (user string) {
user = os.Getenv("GITHUB_USER")
if user != "" {
@ -147,7 +172,7 @@ func (c *Config) scanLine() string {
func (c *Config) Find(host string) *Host {
for _, h := range c.Hosts {
if h.Host == host {
return &h
return h
}
}
@ -158,7 +183,7 @@ func (c *Config) selectHost() *Host {
options := len(c.Hosts)
if options == 1 {
return &c.Hosts[0]
return c.Hosts[0]
}
prompt := "Select host:\n"
@ -174,7 +199,7 @@ func (c *Config) selectHost() *Host {
utils.Check(fmt.Errorf("Error: must enter a number [1-%d]", options))
}
return &c.Hosts[i-1]
return c.Hosts[i-1]
}
func configsFile() string {
@ -186,11 +211,18 @@ func configsFile() string {
return configsFile
}
func CurrentConfig() *Config {
c := &Config{}
newConfigService().Load(configsFile(), c)
var currentConfig *Config
var configLoadedFrom = ""
return c
func CurrentConfig() *Config {
filename := configsFile()
if configLoadedFrom != filename {
currentConfig = &Config{}
newConfigService().Load(filename, currentConfig)
configLoadedFrom = filename
}
return currentConfig
}
func (c *Config) DefaultHost() (host *Host, err error) {
@ -198,6 +230,8 @@ func (c *Config) DefaultHost() (host *Host, err error) {
host, err = c.PromptForHost(GitHubHostEnv)
} else if len(c.Hosts) > 0 {
host = c.selectHost()
// HACK: forces host to inherit GITHUB_TOKEN if applicable
host, err = c.PromptForHost(host.Host)
} else {
host, err = c.PromptForHost(DefaultGitHubHost())
}
@ -210,13 +244,13 @@ func CreateTestConfigs(user, token string) *Config {
f, _ := ioutil.TempFile("", "test-config")
defaultConfigsFile = f.Name()
host := Host{
host := &Host{
User: "jingweno",
AccessToken: "123",
Host: GitHubHost,
}
c := &Config{Hosts: []Host{host}}
c := &Config{Hosts: []*Host{host}}
err := newConfigService().Save(f.Name(), c)
if err != nil {
panic(err)

Просмотреть файл

@ -38,7 +38,7 @@ func (y *yamlConfigDecoder) Decode(r io.Reader, c *Config) error {
for h, v := range yc {
vv := v[0]
host := Host{
host := &Host{
Host: h,
User: vv.User,
AccessToken: vv.OAuthToken,

Просмотреть файл

@ -54,19 +54,19 @@ func TestConfigService_TomlSave(t *testing.T) {
file, _ := ioutil.TempFile("", "test-gh-config-")
defer os.RemoveAll(file.Name())
host := Host{
host := &Host{
Host: "github.com",
User: "jingweno",
AccessToken: "123",
Protocol: "https",
}
c := Config{Hosts: []Host{host}}
c := &Config{Hosts: []*Host{host}}
cs := &configService{
Encoder: &tomlConfigEncoder{},
Decoder: &tomlConfigDecoder{},
}
err := cs.Save(file.Name(), &c)
err := cs.Save(file.Name(), c)
assert.Equal(t, nil, err)
b, _ := ioutil.ReadFile(file.Name())
@ -82,19 +82,19 @@ func TestConfigService_YamlSave(t *testing.T) {
file, _ := ioutil.TempFile("", "test-gh-config-")
defer os.RemoveAll(file.Name())
host := Host{
host := &Host{
Host: "github.com",
User: "jingweno",
AccessToken: "123",
Protocol: "https",
}
c := Config{Hosts: []Host{host}}
c := &Config{Hosts: []*Host{host}}
cs := &configService{
Encoder: &yamlConfigEncoder{},
Decoder: &yamlConfigDecoder{},
}
err := cs.Save(file.Name(), &c)
err := cs.Save(file.Name(), c)
assert.Equal(t, nil, err)
b, _ := ioutil.ReadFile(file.Name())

Просмотреть файл

@ -12,6 +12,7 @@ import (
"github.com/github/hub/git"
"github.com/github/hub/ui"
"github.com/github/hub/utils"
"github.com/github/hub/version"
)
const (
@ -74,19 +75,31 @@ func report(reportedError error, stack string) {
ui.Println(issue.HTMLURL)
}
func reportTitleAndBody(reportedError error, stack string) (title, body string, err error) {
message := "Crash report - %v\n\nError (%s): `%v`\n\nStack:\n\n```\n%s\n```\n\nRuntime:\n\n```\n%s\n```\n\n"
message += `
const crashReportTmpl = "Crash report - %v\n\n" +
"Error (%s): `%v`\n\n" +
"Stack:\n\n```\n%s\n```\n\n" +
"Runtime:\n\n```\n%s\n```\n\n" +
"Version:\n\n```\n%s\n```\n" +
`
# Creating crash report:
#
# This information will be posted as a new issue under jingweno/gh.
# This information will be posted as a new issue under github/hub.
# We're NOT including any information about the command that you were executing,
# but knowing a little bit more about it would really help us to solve this problem.
# Feel free to modify the title and the description for this issue.
`
func reportTitleAndBody(reportedError error, stack string) (title, body string, err error) {
errType := reflect.TypeOf(reportedError).String()
message = fmt.Sprintf(message, reportedError, errType, reportedError, stack, runtimeInfo())
message := fmt.Sprintf(
crashReportTmpl,
reportedError,
errType,
reportedError,
stack,
runtimeInfo(),
version.FullVersion(),
)
editor, err := NewEditor("CRASH_REPORT", "crash report", message)
if err != nil {

Просмотреть файл

@ -26,7 +26,7 @@ func (h GitHubHosts) Include(host string) bool {
func knownGitHubHosts() (hosts GitHubHosts) {
defaultHost := DefaultGitHubHost()
hosts = append(hosts, defaultHost)
hosts = append(hosts, "ssh." + GitHubHost)
hosts = append(hosts, "ssh."+GitHubHost)
ghHosts, _ := git.Config("hub.host")
for _, ghHost := range strings.Split(ghHosts, "\n") {

Просмотреть файл

@ -13,6 +13,7 @@ import (
"strings"
"time"
"github.com/github/hub/ui"
"github.com/github/hub/utils"
)
@ -134,7 +135,7 @@ func newHttpClient(testHost string, verbose bool) *http.Client {
},
Verbose: verbose,
OverrideURL: testURL,
Out: os.Stderr,
Out: ui.Stderr,
Colorized: isTerminal(os.Stderr.Fd()),
}
return &http.Client{Transport: tr}

Просмотреть файл

@ -1,8 +1,8 @@
package github
import (
"github.com/github/hub/git"
"github.com/github/hub/Godeps/_workspace/src/github.com/mattn/go-isatty"
"github.com/github/hub/git"
)
func IsHttpsProtocol() bool {

Просмотреть файл

@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "HUB" "1" "May 2015" "GITHUB" "Hub Manual"
.TH "HUB" "1" "September 2015" "GITHUB" "Hub Manual"
.
.SH "NAME"
\fBhub\fR \- git + hub = github
@ -55,13 +55,13 @@
\fBgit browse\fR [\fB\-u\fR] [[\fIUSER\fR\fB/\fR]\fIREPOSITORY\fR] [SUBPAGE]
.
.br
\fBgit compare\fR [\fB\-u\fR] [\fIUSER\fR] [[\fISTART\fR\.\.\.]\fIEND\fR]
\fBgit compare\fR [\fB\-u\fR] [\fB\-b\fR \fIBASE\fR] [\fIUSER\fR] [[\fISTART\fR\.\.\.]\fIEND\fR]
.
.br
\fBgit fork\fR [\fB\-\-no\-remote\fR]
.
.br
\fBgit pull\-request\fR [\fB\-o\fR|\fB\-\-browse\fR] [\fB\-f\fR] [\fB\-m\fR \fIMESSAGE\fR|\fB\-F\fR \fIFILE\fR|\fB\-i\fR \fIISSUE\fR|\fIISSUE\-URL\fR] [\fB\-b\fR \fIBASE\fR] [\fB\-h\fR \fIHEAD\fR] [\fB\-a\fR \fIUSER\fR]:
\fBgit pull\-request\fR [\fB\-o\fR|\fB\-\-browse\fR] [\fB\-f\fR] [\fB\-m\fR \fIMESSAGE\fR|\fB\-F\fR \fIFILE\fR|\fB\-i\fR \fIISSUE\fR|\fIISSUE\-URL\fR] [\fB\-b\fR \fIBASE\fR] [\fB\-h\fR \fIHEAD\fR] [\fB\-a\fR \fIUSER\fR] [\fB\-M\fR \fIMILESTONE\fR] [\fB\-l\fR \fILABELS\fR]:
.
.br
\fBgit ci\-status\fR [\fB\-v\fR] [\fICOMMIT\fR]
@ -140,15 +140,15 @@ Create a new public GitHub repository from the current git repository and add re
Open repository\'s GitHub page in the system\'s default web browser using \fBopen(1)\fR or the \fBBROWSER\fR env variable\. If the repository isn\'t specified, \fBbrowse\fR opens the page of the repository found in the current directory\. If SUBPAGE is specified, the browser will open on the specified subpage: one of "wiki", "commits", "issues" or other (the default is "tree")\. A specific commit in the default repository can be opened with \fBgit browse \-\- commit/<COMMIT>\fR\. With \fB\-u\fR, outputs the URL rather than opening the browser\.
.
.TP
\fBgit compare\fR [\fB\-u\fR] [\fIUSER\fR] [[\fISTART\fR\.\.\.]\fIEND\fR]
Open a GitHub compare view page in the system\'s default web browser\. \fISTART\fR to \fIEND\fR are branch names, tag names, or commit SHA1s specifying the range of history to compare\. If a range with two dots (\fBa\.\.b\fR) is given, it will be transformed into one with three dots\. If \fISTART\fR is omitted, GitHub will compare against the base branch (the default is "master")\. If \fIEND\fR is omitted, GitHub compare view is opened for the current branch\. With \fB\-u\fR, outputs the URL rather than opening the browser\.
\fBgit compare\fR [\fB\-u\fR] [\fB\-b\fR \fIBASE\fR] [\fIUSER\fR] [[\fISTART\fR\.\.\.]\fIEND\fR]
Open a GitHub compare view page in the system\'s default web browser\. \fISTART\fR to \fIEND\fR are branch names, tag names, or commit SHA1s specifying the range of history to compare\. If a range with two dots (\fBa\.\.b\fR) is given, it will be transformed into one with three dots\. If \fISTART\fR is omitted, GitHub will compare against the base branch (the default is "master")\. If \fIEND\fR is omitted, GitHub compare view is opened for the current branch\. With \fB\-u\fR, outputs the URL rather than opening the browser\. With \fB\-b\fR, uses \fIBASE\fR as base branch rather than the default branch (master)\.
.
.TP
\fBgit fork\fR [\fB\-\-no\-remote\fR]
Forks the original project (referenced by "origin" remote) on GitHub and adds a new remote for it under your username\.
.
.TP
\fBgit pull\-request\fR [\fB\-o\fR|\fB\-\-browse\fR] [\fB\-f\fR] [\fB\-m\fR \fIMESSAGE\fR|\fB\-F\fR \fIFILE\fR|\fB\-i\fR \fIISSUE\fR|\fIISSUE\-URL\fR] [\fB\-b\fR \fIBASE\fR] [\fB\-h\fR \fIHEAD\fR] [\fB\-a\fR \fIUSER\fR]
\fBgit pull\-request\fR [\fB\-o\fR|\fB\-\-browse\fR] [\fB\-f\fR] [\fB\-m\fR \fIMESSAGE\fR|\fB\-F\fR \fIFILE\fR|\fB\-i\fR \fIISSUE\fR|\fIISSUE\-URL\fR] [\fB\-b\fR \fIBASE\fR] [\fB\-h\fR \fIHEAD\fR] [\fB\-a\fR \fIUSER\fR] [\fB\-M\fR \fIMILESTONE\fR] [\fB\-l\fR \fILABELS\fR]
Opens a pull request on GitHub for the project that the "origin" remote points to\. The default head of the pull request is the current branch\. Both base and head of the pull request can be explicitly given in one of the following formats: "branch", "owner:branch", "owner/repo:branch"\. This command will abort operation if it detects that the current topic branch has local commits that are not yet pushed to its upstream branch on the remote\. To skip this check, use \fB\-f\fR\.
.
.IP
@ -158,7 +158,7 @@ Without \fIMESSAGE\fR or \fIFILE\fR, a text editor will open in which title and
With \fB\-o\fR or \fB\-\-browse\fR, the new pull request will open in the web browser\.
.
.IP
With \fB\-a\fR or \fB\-\-assign\fR, the new pull request will be assigned to \fIUSER\fR\.
With \fB\-a\fR or \fB\-\-assign\fR, the new pull request will be assigned to \fIUSER\fR\. With \fB\-M\fR or \fB\-\-milestone\fR, it will be added to milestone with ID \fIMILESTONE\fR\. With \fB\-l\fR or \fB\-\-labels\fR, the comma\-separated list of labels will be applied\.
.
.IP
Issue to pull request conversion via \fB\-i <ISSUE>\fR or \fIISSUE\-URL\fR arguments is deprecated and will likely be removed from the future versions of both hub and GitHub API\.
@ -181,6 +181,9 @@ Hub will prompt for GitHub username & password the first time it needs to access
.P
To avoid being prompted, use \fIGITHUB_USER\fR and \fIGITHUB_PASSWORD\fR environment variables\.
.
.P
Alternatively, you may provide \fIGITHUB_TOKEN\fR, an access token with \fIrepo\fR permissions\. This will not be written to \fB~/\.config/hub\fR\.
.
.SS "HTTPS instead of git protocol"
If you prefer the HTTPS protocol for GitHub repositories, you can set "hub\.protocol" to "https"\. This will affect \fBclone\fR, \fBfork\fR, \fBremote add\fR and other operations that expand references to GitHub repositories as full URLs that otherwise use git and ssh protocols\.
.

Просмотреть файл

@ -98,9 +98,9 @@
<p><code>git create</code> [<var>NAME</var>] [<code>-p</code>] [<code>-d</code> <var>DESCRIPTION</var>] [<code>-h</code> <var>HOMEPAGE</var>]<br />
<code>git browse</code> [<code>-u</code>] [[<var>USER</var><code>/</code>]<var>REPOSITORY</var>] [SUBPAGE]<br />
<code>git compare</code> [<code>-u</code>] [<var>USER</var>] [[<var>START</var>...]<var>END</var>]<br />
<code>git compare</code> [<code>-u</code>] [<code>-b</code> <var>BASE</var>] [<var>USER</var>] [[<var>START</var>...]<var>END</var>]<br />
<code>git fork</code> [<code>--no-remote</code>]<br />
<code>git pull-request</code> [<code>-o</code>|<code>--browse</code>] [<code>-f</code>] [<code>-m</code> <var>MESSAGE</var>|<code>-F</code> <var>FILE</var>|<code>-i</code> <var>ISSUE</var>|<var>ISSUE-URL</var>] [<code>-b</code> <var>BASE</var>] [<code>-h</code> <var>HEAD</var>] [<code>-a</code> <var>USER</var>]:<br />
<code>git pull-request</code> [<code>-o</code>|<code>--browse</code>] [<code>-f</code>] [<code>-m</code> <var>MESSAGE</var>|<code>-F</code> <var>FILE</var>|<code>-i</code> <var>ISSUE</var>|<var>ISSUE-URL</var>] [<code>-b</code> <var>BASE</var>] [<code>-h</code> <var>HEAD</var>] [<code>-a</code> <var>USER</var>] [<code>-M</code> <var>MILESTONE</var>] [<code>-l</code> <var>LABELS</var>]:<br />
<code>git ci-status</code> [<code>-v</code>] [<var>COMMIT</var>]</p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
@ -178,16 +178,17 @@ subpage: one of "wiki", "commits", "issues" or other (the default is
"tree"). A specific commit in the default repository can be opened with
<code>git browse -- commit/&lt;COMMIT></code>.
With <code>-u</code>, outputs the URL rather than opening the browser.</p></dd>
<dt><code>git compare</code> [<code>-u</code>] [<var>USER</var>] [[<var>START</var>...]<var>END</var>]</dt><dd><p>Open a GitHub compare view page in the system's default web browser.
<dt><code>git compare</code> [<code>-u</code>] [<code>-b</code> <var>BASE</var>] [<var>USER</var>] [[<var>START</var>...]<var>END</var>]</dt><dd><p>Open a GitHub compare view page in the system's default web browser.
<var>START</var> to <var>END</var> are branch names, tag names, or commit SHA1s specifying
the range of history to compare. If a range with two dots (<code>a..b</code>) is given,
it will be transformed into one with three dots. If <var>START</var> is omitted,
GitHub will compare against the base branch (the default is "master").
If <var>END</var> is omitted, GitHub compare view is opened for the current branch.
With <code>-u</code>, outputs the URL rather than opening the browser.</p></dd>
With <code>-u</code>, outputs the URL rather than opening the browser. With <code>-b</code>, uses
<var>BASE</var> as base branch rather than the default branch (master).</p></dd>
<dt><code>git fork</code> [<code>--no-remote</code>]</dt><dd><p>Forks the original project (referenced by "origin" remote) on GitHub and
adds a new remote for it under your username.</p></dd>
<dt><code>git pull-request</code> [<code>-o</code>|<code>--browse</code>] [<code>-f</code>] [<code>-m</code> <var>MESSAGE</var>|<code>-F</code> <var>FILE</var>|<code>-i</code> <var>ISSUE</var>|<var>ISSUE-URL</var>] [<code>-b</code> <var>BASE</var>] [<code>-h</code> <var>HEAD</var>] [<code>-a</code> <var>USER</var>]</dt><dd><p>Opens a pull request on GitHub for the project that the "origin" remote
<dt><code>git pull-request</code> [<code>-o</code>|<code>--browse</code>] [<code>-f</code>] [<code>-m</code> <var>MESSAGE</var>|<code>-F</code> <var>FILE</var>|<code>-i</code> <var>ISSUE</var>|<var>ISSUE-URL</var>] [<code>-b</code> <var>BASE</var>] [<code>-h</code> <var>HEAD</var>] [<code>-a</code> <var>USER</var>] [<code>-M</code> <var>MILESTONE</var>] [<code>-l</code> <var>LABELS</var>]</dt><dd><p>Opens a pull request on GitHub for the project that the "origin" remote
points to. The default head of the pull request is the current branch.
Both base and head of the pull request can be explicitly given in one of
the following formats: "branch", "owner:branch", "owner/repo:branch".
@ -201,7 +202,9 @@ Pull request message can also be passed via stdin with <code>-F -</code>.</p>
<p>With <code>-o</code> or <code>--browse</code>, the new pull request will open in the web browser.</p>
<p>With <code>-a</code> or <code>--assign</code>, the new pull request will be assigned to <var>USER</var>.</p>
<p>With <code>-a</code> or <code>--assign</code>, the new pull request will be assigned to <var>USER</var>.
With <code>-M</code> or <code>--milestone</code>, it will be added to milestone with ID <var>MILESTONE</var>.
With <code>-l</code> or <code>--labels</code>, the comma-separated list of labels will be applied.</p>
<p>Issue to pull request conversion via <code>-i &lt;ISSUE></code> or <var>ISSUE-URL</var>
arguments is deprecated and will likely be removed from the future versions
@ -224,6 +227,9 @@ the API and exchange it for an OAuth token, which it saves in "~/.config/hub".</
<p>To avoid being prompted, use <var>GITHUB_USER</var> and <var>GITHUB_PASSWORD</var> environment
variables.</p>
<p>Alternatively, you may provide <var>GITHUB_TOKEN</var>, an access token with
<var>repo</var> permissions. This will not be written to <code>~/.config/hub</code>.</p>
<h3 id="HTTPS-instead-of-git-protocol">HTTPS instead of git protocol</h3>
<p>If you prefer the HTTPS protocol for GitHub repositories, you can set
@ -481,7 +487,7 @@ $ git help hub
<ol class='man-decor man-foot man foot'>
<li class='tl'>GITHUB</li>
<li class='tc'>May 2015</li>
<li class='tc'>September 2015</li>
<li class='tr'>hub(1)</li>
</ol>

Просмотреть файл

@ -25,9 +25,9 @@ hub(1) -- git + hub = github
`git create` [<NAME>] [`-p`] [`-d` <DESCRIPTION>] [`-h` <HOMEPAGE>]
`git browse` [`-u`] [[<USER>`/`]<REPOSITORY>] [SUBPAGE]
`git compare` [`-u`] [<USER>] [[<START>...]<END>]
`git compare` [`-u`] [`-b` <BASE>] [<USER>] [[<START>...]<END>]
`git fork` [`--no-remote`]
`git pull-request` [`-o`|`--browse`] [`-f`] [`-m` <MESSAGE>|`-F` <FILE>|`-i` <ISSUE>|<ISSUE-URL>] [`-b` <BASE>] [`-h` <HEAD>] [`-a` <USER>]:
`git pull-request` [`-o`|`--browse`] [`-f`] [`-m` <MESSAGE>|`-F` <FILE>|`-i` <ISSUE>|<ISSUE-URL>] [`-b` <BASE>] [`-h` <HEAD>] [`-a` <USER>] [`-M` <MILESTONE>] [`-l` <LABELS>]:
`git ci-status` [`-v`] [<COMMIT>]
## DESCRIPTION
@ -132,20 +132,21 @@ hub also adds some custom commands that are otherwise not present in git:
`git browse -- commit/<COMMIT>`.
With `-u`, outputs the URL rather than opening the browser.
* `git compare` [`-u`] [<USER>] [[<START>...]<END>]:
* `git compare` [`-u`] [`-b` <BASE>] [<USER>] [[<START>...]<END>]:
Open a GitHub compare view page in the system's default web browser.
<START> to <END> are branch names, tag names, or commit SHA1s specifying
the range of history to compare. If a range with two dots (`a..b`) is given,
it will be transformed into one with three dots. If <START> is omitted,
GitHub will compare against the base branch (the default is "master").
If <END> is omitted, GitHub compare view is opened for the current branch.
With `-u`, outputs the URL rather than opening the browser.
With `-u`, outputs the URL rather than opening the browser. With `-b`, uses
<BASE> as base branch rather than the default branch (master).
* `git fork` [`--no-remote`]:
Forks the original project (referenced by "origin" remote) on GitHub and
adds a new remote for it under your username.
* `git pull-request` [`-o`|`--browse`] [`-f`] [`-m` <MESSAGE>|`-F` <FILE>|`-i` <ISSUE>|<ISSUE-URL>] [`-b` <BASE>] [`-h` <HEAD>] [`-a` <USER>]:
* `git pull-request` [`-o`|`--browse`] [`-f`] [`-m` <MESSAGE>|`-F` <FILE>|`-i` <ISSUE>|<ISSUE-URL>] [`-b` <BASE>] [`-h` <HEAD>] [`-a` <USER>] [`-M` <MILESTONE>] [`-l` <LABELS>]:
Opens a pull request on GitHub for the project that the "origin" remote
points to. The default head of the pull request is the current branch.
Both base and head of the pull request can be explicitly given in one of
@ -161,6 +162,8 @@ hub also adds some custom commands that are otherwise not present in git:
With `-o` or `--browse`, the new pull request will open in the web browser.
With `-a` or `--assign`, the new pull request will be assigned to <USER>.
With `-M` or `--milestone`, it will be added to milestone with ID <MILESTONE>.
With `-l` or `--labels`, the comma-separated list of labels will be applied.
Issue to pull request conversion via `-i <ISSUE>` or <ISSUE-URL>
arguments is deprecated and will likely be removed from the future versions

Просмотреть файл

@ -18,14 +18,14 @@ fi
STATUS=0
if ! go version; then
echo "You need to install Go 1.2 to build hub" >&2
echo "You need to install Go 1.4.1 or higher to build hub" >&2
STATUS=1
fi
{ ruby --version
bundle install --path vendor/bundle -j 4
} || {
echo "You need Ruby 2.1 and Bundler to run hub tests" >&2
echo "You need Ruby 1.9 or higher and Bundler to run hub tests" >&2
STATUS=1
}

Просмотреть файл

@ -41,7 +41,7 @@ up_to_date() {
build_hub() {
setup_gopath
[ -n "$1" ] && (up_to_date "$1" || go build -ldflags "-X github.com/github/hub/commands.Version=`./script/version`" -o "$1")
[ -n "$1" ] && (up_to_date "$1" || go build -ldflags "-X github.com/github/hub/version.Version=`./script/version`" -o "$1")
}
test_hub() {

22
script/ctags Executable file
Просмотреть файл

@ -0,0 +1,22 @@
#!/bin/bash
# Usage: script/ctags [<tags-file=.git/tags>]
#
# Generates tags for Go runtime files. Requires universal-ctags.
set -e
case "$1" in
-h | --help )
sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0"
exit 0
;;
esac
ctags -R --tag-relative -f "${1:-.git/tags}" \
--exclude=Godeps \
--exclude=etc \
--exclude=script \
--exclude=tmp \
--exclude=vendor \
--exclude='*_test.go' \
.

Просмотреть файл

@ -3,7 +3,8 @@ package ui
import (
"fmt"
"io"
"os"
"github.com/github/hub/Godeps/_workspace/src/github.com/mattn/go-colorable"
)
type UI interface {
@ -13,7 +14,11 @@ type UI interface {
Errorln(a ...interface{}) (n int, err error)
}
var Default UI = Console{Stdout: os.Stdout, Stderr: os.Stderr}
var (
Stdout = colorable.NewColorableStdout()
Stderr = colorable.NewColorableStderr()
Default UI = Console{Stdout: Stdout, Stderr: Stderr}
)
func Printf(format string, a ...interface{}) (n int, err error) {
return Default.Printf(format, a...)

16
version/version.go Normal file
Просмотреть файл

@ -0,0 +1,16 @@
package version
import (
"fmt"
"github.com/github/hub/git"
"github.com/github/hub/utils"
)
var Version = "2.2.0"
func FullVersion() string {
gitVersion, err := git.Version()
utils.Check(err)
return fmt.Sprintf("%s\nhub version %s", gitVersion, Version)
}