-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathfix.go
184 lines (148 loc) · 4.23 KB
/
fix.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
"io/ioutil"
"path/filepath"
"github.com/codegangsta/cli"
)
func fix(c *cli.Context) error {
repo := c.String("repo")
if fixErr := fixExistingSubmodules(repo); fixErr != nil {
return fmt.Errorf("failed to fix existing submodules: %s", fixErr)
}
return nil
}
// Convert any "semi-submodules" into first class submodules.
// See http://stackoverflow.com/questions/4161022/git-how-to-track-untracked-content/4162672#4162672
func fixExistingSubmodules(repo string) error {
submodules, err := getSubmodules(repo)
if err != nil {
return err
}
gitmodules, err := getGitModules(repo)
if err != nil {
return err
}
var lastErr error
for _, submodule := range submodules {
if !gitmodules[submodule] {
err = fixSubmodule(submodule)
if err != nil {
fmt.Printf("fixExistingSubmodules failed to fix submodule %s\n", submodule)
lastErr = err
}
}
}
return lastErr
}
func fixSubmodule(submodule string) error {
fmt.Printf("\x1b[32mFixing submodule %s .", submodule)
defer fmt.Println("\x1b[0m")
rm := exec.Command("git", "rm", "--cached", "-f", submodule)
rm.Stderr = os.Stderr
err := rm.Run()
if err != nil {
return fmt.Errorf("fixSubmodule failed to remove submodule path %s from the index: %s", submodule, err)
}
fmt.Printf(".")
url, err := submoduleUrl(submodule)
if err != nil {
return fmt.Errorf("fixSubmodule failed to determine URL of submodule %s: %s", submodule, err)
}
fmt.Printf(".")
submoduleAdd := exec.Command("git", "submodule", "add", url, submodule)
submoduleAdd.Stderr = os.Stderr
err = submoduleAdd.Run()
if err != nil {
return fmt.Errorf("fixSubmodule failed to add submodule %s: %s", submodule, err)
}
fmt.Printf(".")
fmt.Printf(". done.")
return nil
}
func submoduleUrl(submodule string) (string, error) {
submoduleQuery := exec.Command("git", "remote", "show", "origin")
submoduleQuery.Dir = submodule
submoduleQuery.Stderr = os.Stderr
lsFileOut, err := submoduleQuery.StdoutPipe()
if err != nil {
fmt.Printf("submoduleUrl failed to get StdoutPipe: %s\n", err)
return "", err
}
lineScanner := bufio.NewScanner(lsFileOut)
err = submoduleQuery.Start()
if err != nil {
fmt.Printf("submoduleUrl failed to start git remote show origin: %s\n", err)
return "", err
}
var url string
for lineScanner.Scan() {
segments := strings.Fields(lineScanner.Text())
if len(segments) < 3 {
continue
}
if segments[0] == "Fetch" && segments[1] == "URL:" {
url = segments[2]
}
}
if url == "" {
return "", fmt.Errorf("submoduleUrl failed to find the URL of %s\n", submodule)
}
err = submoduleQuery.Wait()
if err != nil {
fmt.Printf("submoduleUrl failed to wait for git remote show origin: %s\n", err)
return "", err
}
return url, nil
}
func getSubmodules(repo string) ([]string, error) {
lsFiles := exec.Command("git", "ls-files", "--stage")
lsFiles.Dir = repo
lsFiles.Stderr = os.Stderr
lsFileOut, err := lsFiles.StdoutPipe()
if err != nil {
fmt.Printf("getSubmodules failed to get StdoutPipe: %s\n", err)
return nil, err
}
lineScanner := bufio.NewScanner(lsFileOut)
err = lsFiles.Start()
if err != nil {
fmt.Printf("getSubmodules failed to start git ls-files --stage: %s\n", err)
return nil, err
}
submodules := []string{}
for lineScanner.Scan() {
segments := strings.Fields(lineScanner.Text())
if len(segments) < 4 {
return nil, fmt.Errorf("invalid git ls-files output: %q", lineScanner.Text())
}
if segments[0] == "160000" {
submodules = append(submodules, segments[3])
}
}
err = lsFiles.Wait()
if err != nil {
fmt.Printf("getSubmodules failed to wait for git ls-files --stage: %s\n", err)
return nil, err
}
return submodules, nil
}
func getGitModules(repo string) (map[string]bool, error) {
gitmodules := make(map[string]bool)
dotGitModules, err := ioutil.ReadFile(filepath.Join(repo, ".gitmodules"))
if err != nil {
return nil, fmt.Errorf("Failed to read .gitmodules file: %s", err)
}
lineScanner := bufio.NewScanner(strings.NewReader(string(dotGitModules)))
for lineScanner.Scan() {
segments := strings.Fields(lineScanner.Text())
if len(segments) == 3 && segments[0] == "path" && segments[1] == "=" {
gitmodules[segments[2]] = true
}
}
return gitmodules, nil
}