-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
157 additions
and
60 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module github.com/broothie/self-referential-commit | ||
|
||
go 1.18 | ||
|
||
require ( | ||
github.com/pkg/errors v0.9.1 | ||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= | ||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"fmt" | ||
"math/rand" | ||
"os" | ||
"os/exec" | ||
"path" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
"golang.org/x/sync/errgroup" | ||
) | ||
|
||
func init() { | ||
rand.Seed(time.Now().UnixNano()) | ||
} | ||
|
||
func main() { | ||
start := time.Now() | ||
if err := os.RemoveAll("clones"); err != nil { | ||
panic(err) | ||
} | ||
|
||
if err := os.RemoveAll("short.sha"); err != nil { | ||
panic(err) | ||
} | ||
|
||
// Args | ||
workers := flag.Int("w", 1, "number of workers") | ||
flag.Parse() | ||
|
||
// Async | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
group, ctx := errgroup.WithContext(ctx) | ||
shaChan := make(chan string) | ||
|
||
// Start workers | ||
for worker := 0; worker < *workers; worker++ { | ||
group.Go(findLuckySHA(ctx, start, worker, shaChan)) | ||
} | ||
|
||
sha := <-shaChan | ||
fmt.Printf("success! the lucky sha was %q\n", sha) | ||
|
||
cancel() | ||
if err := group.Wait(); err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func findLuckySHA(ctx context.Context, start time.Time, worker int, shaChan chan string) func() error { | ||
return func() error { | ||
repoPath := path.Join("clones", strconv.Itoa(int(start.Unix())), fmt.Sprintf("%d-self-referential-commit", worker)) | ||
if err := gitInit(ctx, repoPath); err != nil { | ||
return errors.Wrapf(err, "failed to git init %q", repoPath) | ||
} | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
return nil | ||
default: | ||
shortSha := randomShortSHA() | ||
message := fmt.Sprintf("short sha: %s", shortSha) | ||
output, err := gitCommit(ctx, repoPath, message) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if strings.TrimSpace(output) == fmt.Sprintf("[main %s] %s", shortSha, message) { | ||
shaChan <- shortSha | ||
close(shaChan) | ||
|
||
if err := os.WriteFile("short.sha", []byte(shortSha), 0666); err != nil { | ||
return errors.Wrapf(err, "failed to write file under repo %q", repoPath) | ||
} | ||
} else { | ||
if err := gitReset(ctx, repoPath); err != nil { | ||
return err | ||
} | ||
|
||
if err := gitGC(ctx, repoPath); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
func gitInit(ctx context.Context, repoPath string) error { | ||
output, err := exec.CommandContext(ctx, "git", "clone", "https://github.com/broothie/self-referential-commit.git", repoPath).CombinedOutput() | ||
if err != nil { | ||
fmt.Println(string(output)) | ||
return errors.Wrapf(err, "failed to init git repo at %q", repoPath) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func gitCommit(ctx context.Context, repoPath, message string) (string, error) { | ||
output, err := exec.CommandContext(ctx, "git", "-C", repoPath, "commit", "--allow-empty", "-m", message).CombinedOutput() | ||
if err != nil { | ||
fmt.Println(string(output)) | ||
return "", errors.Wrapf(err, "failed to commit to repo at %q", repoPath) | ||
} | ||
|
||
return string(output), nil | ||
} | ||
|
||
func gitReset(ctx context.Context, repoPath string) error { | ||
output, err := exec.CommandContext(ctx, "git", "-C", repoPath, "reset", "--hard", "HEAD~").CombinedOutput() | ||
if err != nil { | ||
fmt.Println(string(output)) | ||
return errors.Wrapf(err, "failed to reset repo at %q", repoPath) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func gitGC(ctx context.Context, repoPath string) error { | ||
output, err := exec.CommandContext(ctx, "git", "-C", repoPath, "gc").CombinedOutput() | ||
if err != nil { | ||
fmt.Println(string(output)) | ||
return errors.Wrapf(err, "failed to gc repo at %q", repoPath) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func randomShortSHA() string { | ||
const hexRunes = "0123456789abcdef" | ||
|
||
runes := make([]rune, 7) | ||
for i := range runes { | ||
runes[i] = rune(hexRunes[rand.Intn(len(hexRunes))]) | ||
} | ||
|
||
return string(runes) | ||
} |