Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit d3d9967

Browse files
committed
gps: Adaptively clean dirty git repos
Instead of forcing the user to clean up dirty git repositories, we can take at least basic steps to doing it ourselves - or, if we detect problems, instructing the user to fix it. The overhead introduced here is a `git status` call, which will be non-negligible on larger repos, but it's probably worth it for the resilient behavior.
1 parent 1d73a4a commit d3d9967

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

internal/gps/maybe_source.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,19 @@ func (m maybeGitSource) try(ctx context.Context, cachedir string, c singleSource
100100
return nil, 0, err
101101
}
102102

103-
c.setVersionMap(vl)
104103
state := sourceIsSetUp | sourceExistsUpstream | sourceHasLatestVersionList
105104

106105
if r.CheckLocal() {
107106
state |= sourceExistsLocally
107+
108+
// If repository already exists on disk, make a pass to be sure
109+
// everything's clean.
110+
if err = src.cleanup(ctx); err != nil {
111+
return nil, 0, err
112+
}
108113
}
109114

115+
c.setVersionMap(vl)
110116
return src, state, nil
111117
}
112118

internal/gps/vcs_source.go

+68
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,74 @@ type gitSource struct {
123123
baseVCSSource
124124
}
125125

126+
// ensureClean sees to it that a git repository is clean and in working order,
127+
// or returns an error if it can't.
128+
func (s *gitSource) cleanup(ctx context.Context) error {
129+
r := s.repo.(*gitRepo)
130+
cmd := commandContext(
131+
ctx,
132+
"git",
133+
"status",
134+
"--porcelain",
135+
)
136+
cmd.SetDir(r.LocalPath())
137+
138+
out, err := cmd.CombinedOutput()
139+
if err != nil {
140+
// An error on simple git status indicates some aggressive repository
141+
// corruption, outside of the purview that we can deal with here.
142+
return err
143+
}
144+
145+
if len(bytes.TrimSpace(out)) == 0 {
146+
// No output from status indicates a clean tree, without any modified or
147+
// untracked files - we're in good shape.
148+
return nil
149+
}
150+
151+
// We could be more parsimonious about this, but it's probably not worth it
152+
// - it's a rare case to have to do any cleanup anyway, so when we do, we
153+
// might as well just throw the kitchen sink at it.
154+
cmd = commandContext(
155+
ctx,
156+
"git",
157+
"reset",
158+
"--hard",
159+
)
160+
cmd.SetDir(r.LocalPath())
161+
_, err = cmd.CombinedOutput()
162+
if err != nil {
163+
return err
164+
}
165+
166+
// We also need to git clean -df; just reuse defendAgainstSubmodules here,
167+
// even though it's a bit layer-breaky.
168+
err = r.defendAgainstSubmodules(ctx)
169+
if err != nil {
170+
return err
171+
}
172+
173+
// Check status one last time. If it's still not clean, give up.
174+
cmd = commandContext(
175+
ctx,
176+
"git",
177+
"status",
178+
"--porcelain",
179+
)
180+
cmd.SetDir(r.LocalPath())
181+
182+
out, err = cmd.CombinedOutput()
183+
if err != nil {
184+
return err
185+
}
186+
187+
if len(bytes.TrimSpace(out)) != 0 {
188+
return errors.Errorf("failed to clean up git repository at %s - dirty? corrupted? status output: \n%s", r.LocalPath(), string(out))
189+
}
190+
191+
return nil
192+
}
193+
126194
func (s *gitSource) exportRevisionTo(ctx context.Context, rev Revision, to string) error {
127195
r := s.repo
128196

0 commit comments

Comments
 (0)