Skip to content

Commit bea3209

Browse files
authored
Merge pull request #934 from devlights/add-gitbkup-example
2 parents c64ba83 + 6e1c248 commit bea3209

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
gitbkup
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
vars:
6+
APP_NAME: gitbkup
7+
8+
tasks:
9+
default:
10+
cmds:
11+
- task: build
12+
build:
13+
cmds:
14+
- go build -o {{.APP_NAME}}{{exeExt}} .
15+
clean:
16+
cmds:
17+
- rm -f ./{{.APP_NAME}}{{exeExt}}
18+
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
"io"
8+
"os/exec"
9+
"strings"
10+
)
11+
12+
type (
13+
GitCmd struct{}
14+
)
15+
16+
func (me *GitCmd) exec(args []string) (*CmdReader, error) {
17+
var (
18+
cmd = exec.Command("git", args...)
19+
stdout io.ReadCloser
20+
err error
21+
)
22+
if stdout, err = cmd.StdoutPipe(); err != nil {
23+
return nil, err
24+
}
25+
if err = cmd.Start(); err != nil {
26+
return nil, err
27+
}
28+
29+
return &CmdReader{stdout, cmd}, nil
30+
}
31+
32+
// Prefix は、git rev-parse --show-prefix を実行します。
33+
func (me *GitCmd) Prefix() (string, error) {
34+
var (
35+
args = []string{"rev-parse", "--show-prefix"}
36+
reader *CmdReader
37+
err error
38+
)
39+
if reader, err = me.exec(args); err != nil {
40+
return "", err
41+
}
42+
defer reader.Close()
43+
44+
var (
45+
buf = new(bytes.Buffer)
46+
)
47+
if _, err = io.Copy(buf, reader); err != nil {
48+
return "", err
49+
}
50+
51+
return strings.ReplaceAll(buf.String(), "\n", ""), nil
52+
}
53+
54+
// Sha は、 git log --pretty=format:"%h" を実行しcount番目のSHAを返します。
55+
func (me *GitCmd) Sha(fpath string, count int) (string, error) {
56+
var (
57+
args = []string{"log", "--pretty=format:'%h'", fpath}
58+
reader *CmdReader
59+
err error
60+
)
61+
if reader, err = me.exec(args); err != nil {
62+
return "", err
63+
}
64+
defer reader.Close()
65+
66+
var (
67+
buf = new(bytes.Buffer)
68+
)
69+
if _, err = io.Copy(buf, reader); err != nil {
70+
return "", err
71+
}
72+
73+
var (
74+
scanner = bufio.NewScanner(buf)
75+
sha string
76+
)
77+
for i := 0; scanner.Scan(); i++ {
78+
if i == count {
79+
sha = scanner.Text()
80+
break
81+
}
82+
}
83+
84+
if err = scanner.Err(); err != nil {
85+
return "", err
86+
}
87+
88+
return strings.ReplaceAll(sha, "'", ""), nil
89+
}
90+
91+
// Show は、 git show sha:fpath を実行します。
92+
func (me *GitCmd) Show(sha, fpath string) (io.ReadCloser, error) {
93+
var (
94+
args = []string{"show", fmt.Sprintf("%s:%s", sha, fpath)}
95+
reader *CmdReader
96+
err error
97+
)
98+
if reader, err = me.exec(args); err != nil {
99+
return nil, err
100+
}
101+
102+
return reader, nil
103+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"io"
7+
"log"
8+
"os"
9+
"path/filepath"
10+
"strings"
11+
)
12+
13+
type (
14+
AppArgs struct {
15+
NumLogCount int
16+
File string
17+
}
18+
)
19+
20+
var (
21+
appArgs AppArgs
22+
out io.Writer
23+
)
24+
25+
func init() {
26+
flag.IntVar(&appArgs.NumLogCount, "n", 1, "何世代前の版を取得するかの値")
27+
flag.StringVar(&appArgs.File, "f", "", "ファイル")
28+
}
29+
30+
func main() {
31+
log.SetFlags(0)
32+
flag.Parse()
33+
34+
if appArgs.File == "" {
35+
log.Fatal("invalid argument: file")
36+
}
37+
38+
out = os.Stdout
39+
40+
if err := run(); err != nil {
41+
log.Fatal(err)
42+
}
43+
}
44+
45+
func run() error {
46+
// 1.パス文字が含まれていない場合、Prefixを取得してパス作る (git rev-parse --show-prefix)
47+
// 含んでいる場合、ユーザが明示的に指定しているとみなしPrefixは付けない
48+
// 2.SHA取得 (git log --pretty=format:"%h" -世代数 ファイル名)
49+
// 3.ファイル生成 (git show SHA:パス)
50+
//
51+
// MEMO:
52+
// git log と git show では渡すパスの形が異なる
53+
// - git log は カレントディレクトリからの相対パスを受け付ける仕様
54+
// - git show は リポジトリルートからのパスを受け付ける仕様
55+
56+
var (
57+
git = new(GitCmd)
58+
fpath = appArgs.File
59+
err error
60+
)
61+
if !strings.Contains(fpath, "/") {
62+
var (
63+
prefix string
64+
)
65+
if prefix, err = git.Prefix(); err != nil {
66+
return err
67+
}
68+
69+
fpath = filepath.Join(prefix, fpath)
70+
}
71+
72+
var (
73+
sha string
74+
)
75+
if sha, err = git.Sha(appArgs.File, appArgs.NumLogCount); err != nil {
76+
return err
77+
}
78+
if sha == "" {
79+
return fmt.Errorf("SHA取得失敗: %s", appArgs.File)
80+
}
81+
82+
var (
83+
r io.ReadCloser
84+
)
85+
if r, err = git.Show(sha, fpath); err != nil {
86+
return err
87+
}
88+
defer r.Close()
89+
90+
if _, err = io.Copy(out, r); err != nil {
91+
return err
92+
}
93+
94+
return nil
95+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"io"
5+
"os/exec"
6+
)
7+
8+
type (
9+
CmdReader struct {
10+
pipe io.ReadCloser
11+
cmd *exec.Cmd
12+
}
13+
)
14+
15+
var _ io.ReadCloser = (*CmdReader)(nil)
16+
17+
func (me *CmdReader) Read(p []byte) (int, error) {
18+
return me.pipe.Read(p)
19+
}
20+
21+
func (me *CmdReader) Close() error {
22+
var (
23+
errPipe = me.pipe.Close()
24+
errWait = me.cmd.Wait()
25+
)
26+
if errPipe != nil {
27+
return errPipe
28+
}
29+
if errWait != nil {
30+
return errWait
31+
}
32+
33+
return nil
34+
}

0 commit comments

Comments
 (0)