Merge pull request #765 from thockin/v4_fix_relative_submodules
Make relative-path submodules work, via origin
This commit is contained in:
commit
5a9037f8c6
31
main.go
31
main.go
|
|
@ -1208,6 +1208,8 @@ func (git *repoSync) RunWithStdin(ctx context.Context, cwd absPath, stdin string
|
||||||
// not, it will (re)initialize it. After running this function, callers can
|
// not, it will (re)initialize it. After running this function, callers can
|
||||||
// assume the repo is valid, though maybe empty.
|
// assume the repo is valid, though maybe empty.
|
||||||
func (git *repoSync) initRepo(ctx context.Context) error {
|
func (git *repoSync) initRepo(ctx context.Context) error {
|
||||||
|
needGitInit := false
|
||||||
|
|
||||||
// Check out the git root, and see if it is already usable.
|
// Check out the git root, and see if it is already usable.
|
||||||
_, err := os.Stat(git.root.String())
|
_, err := os.Stat(git.root.String())
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -1218,6 +1220,7 @@ func (git *repoSync) initRepo(ctx context.Context) error {
|
||||||
if err := os.MkdirAll(git.root.String(), defaultDirMode); err != nil {
|
if err := os.MkdirAll(git.root.String(), defaultDirMode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
needGitInit = true
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
default:
|
default:
|
||||||
|
|
@ -1225,7 +1228,6 @@ func (git *repoSync) initRepo(ctx context.Context) error {
|
||||||
git.log.V(3).Info("repo directory exists", "path", git.root)
|
git.log.V(3).Info("repo directory exists", "path", git.root)
|
||||||
if git.sanityCheckRepo(ctx) {
|
if git.sanityCheckRepo(ctx) {
|
||||||
git.log.V(4).Info("repo directory is valid", "path", git.root)
|
git.log.V(4).Info("repo directory is valid", "path", git.root)
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
// Maybe a previous run crashed? Git won't use this dir. We remove
|
// Maybe a previous run crashed? Git won't use this dir. We remove
|
||||||
// the contents rather than the dir itself, because a common use-case
|
// the contents rather than the dir itself, because a common use-case
|
||||||
|
|
@ -1235,9 +1237,11 @@ func (git *repoSync) initRepo(ctx context.Context) error {
|
||||||
if err := removeDirContents(git.root, git.log); err != nil {
|
if err := removeDirContents(git.root, git.log); err != nil {
|
||||||
return fmt.Errorf("can't wipe unusable root directory: %w", err)
|
return fmt.Errorf("can't wipe unusable root directory: %w", err)
|
||||||
}
|
}
|
||||||
|
needGitInit = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if needGitInit {
|
||||||
// Running `git init` in an existing repo is safe (according to git docs).
|
// Running `git init` in an existing repo is safe (according to git docs).
|
||||||
git.log.V(0).Info("initializing repo directory", "path", git.root)
|
git.log.V(0).Info("initializing repo directory", "path", git.root)
|
||||||
if _, _, err := git.Run(ctx, git.root, "init", "-b", "git-sync"); err != nil {
|
if _, _, err := git.Run(ctx, git.root, "init", "-b", "git-sync"); err != nil {
|
||||||
|
|
@ -1246,6 +1250,25 @@ func (git *repoSync) initRepo(ctx context.Context) error {
|
||||||
if !git.sanityCheckRepo(ctx) {
|
if !git.sanityCheckRepo(ctx) {
|
||||||
return fmt.Errorf("can't initialize git repo directory")
|
return fmt.Errorf("can't initialize git repo directory")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "origin" remote has special meaning, like in relative-path
|
||||||
|
// submodules.
|
||||||
|
if stdout, stderr, err := git.Run(ctx, git.root, "remote", "get-url", "origin"); err != nil {
|
||||||
|
if !strings.Contains(stderr, "No such remote") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// It doesn't exist - make it.
|
||||||
|
if _, _, err := git.Run(ctx, git.root, "remote", "add", "origin", git.repo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if strings.TrimSpace(stdout) != git.repo {
|
||||||
|
// It exists, but is wrong.
|
||||||
|
if _, _, err := git.Run(ctx, git.root, "remote", "set-url", "origin", git.repo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1643,14 +1666,14 @@ func lastNonEmptyLine(text string) string {
|
||||||
// is not a hash or is not known to this repo, even if it appears to be a hash,
|
// is not a hash or is not known to this repo, even if it appears to be a hash,
|
||||||
// this will return false.
|
// this will return false.
|
||||||
func (git *repoSync) IsKnownHash(ctx context.Context, ref string) (bool, error) {
|
func (git *repoSync) IsKnownHash(ctx context.Context, ref string) (bool, error) {
|
||||||
output, _, err := git.Run(ctx, git.root, "rev-parse", ref+"^{commit}")
|
stdout, stderr, err := git.Run(ctx, git.root, "rev-parse", ref+"^{commit}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "unknown revision") {
|
if strings.Contains(stderr, "unknown revision") {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
line := lastNonEmptyLine(output)
|
line := lastNonEmptyLine(stdout)
|
||||||
return strings.HasPrefix(line, ref), nil
|
return strings.HasPrefix(line, ref), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,13 +45,14 @@ func NewRunner(log logintf) Runner {
|
||||||
return Runner{log: log}
|
return Runner{log: log}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs given command
|
// Run runs the given command, returning the stdout, stderr, and any error.
|
||||||
func (r Runner) Run(ctx context.Context, cwd string, env []string, command string, args ...string) (string, string, error) {
|
func (r Runner) Run(ctx context.Context, cwd string, env []string, command string, args ...string) (string, string, error) {
|
||||||
// call depth = 2 to erase the runWithStdin frame and this one
|
// call depth = 2 to erase the runWithStdin frame and this one
|
||||||
return runWithStdin(ctx, r.log.WithCallDepth(2), cwd, env, "", command, args...)
|
return runWithStdin(ctx, r.log.WithCallDepth(2), cwd, env, "", command, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunWithStdin runs given command with standard input
|
// RunWithStdin runs the given command with standard input, returning the stdout,
|
||||||
|
// stderr, and any error.
|
||||||
func (r Runner) RunWithStdin(ctx context.Context, cwd string, env []string, stdin, command string, args ...string) (string, string, error) {
|
func (r Runner) RunWithStdin(ctx context.Context, cwd string, env []string, stdin, command string, args ...string) (string, string, error) {
|
||||||
// call depth = 2 to erase the runWithStdin frame and this one
|
// call depth = 2 to erase the runWithStdin frame and this one
|
||||||
return runWithStdin(ctx, r.log.WithCallDepth(2), cwd, env, stdin, command, args...)
|
return runWithStdin(ctx, r.log.WithCallDepth(2), cwd, env, stdin, command, args...)
|
||||||
|
|
@ -80,10 +81,10 @@ func runWithStdin(ctx context.Context, log logintf, cwd string, env []string, st
|
||||||
stdout := strings.TrimSpace(outbuf.String())
|
stdout := strings.TrimSpace(outbuf.String())
|
||||||
stderr := strings.TrimSpace(errbuf.String())
|
stderr := strings.TrimSpace(errbuf.String())
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
return "", "", fmt.Errorf("Run(%s): %w: { stdout: %q, stderr: %q }", cmdStr, ctx.Err(), stdout, stderr)
|
return stdout, stderr, fmt.Errorf("Run(%s): %w: { stdout: %q, stderr: %q }", cmdStr, ctx.Err(), stdout, stderr)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("Run(%s): %w: { stdout: %q, stderr: %q }", cmdStr, err, stdout, stderr)
|
return stdout, stderr, fmt.Errorf("Run(%s): %w: { stdout: %q, stderr: %q }", cmdStr, err, stdout, stderr)
|
||||||
}
|
}
|
||||||
log.V(6).Info("command result", "stdout", stdout, "stderr", stderr, "time", wallTime)
|
log.V(6).Info("command result", "stdout", stdout, "stderr", stderr, "time", wallTime)
|
||||||
|
|
||||||
|
|
|
||||||
36
test_e2e.sh
36
test_e2e.sh
|
|
@ -2527,6 +2527,42 @@ function e2e::submodule_sync_shallow() {
|
||||||
rm -rf $NESTED_SUBMODULE
|
rm -rf $NESTED_SUBMODULE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Test submodule sync with a relative path
|
||||||
|
##############################################
|
||||||
|
function e2e::submodule_sync_relative() {
|
||||||
|
# Init submodule repo
|
||||||
|
SUBMODULE_REPO_NAME="sub"
|
||||||
|
SUBMODULE="$WORK/$SUBMODULE_REPO_NAME"
|
||||||
|
mkdir "$SUBMODULE"
|
||||||
|
|
||||||
|
git -C "$SUBMODULE" init -q -b "$MAIN_BRANCH"
|
||||||
|
echo "submodule" > "$SUBMODULE/submodule"
|
||||||
|
git -C "$SUBMODULE" add submodule
|
||||||
|
git -C "$SUBMODULE" commit -aqm "init submodule file"
|
||||||
|
|
||||||
|
# Add submodule
|
||||||
|
REL="$(realpath --relative-to "$REPO" "$WORK/$SUBMODULE_REPO_NAME")"
|
||||||
|
echo $REL
|
||||||
|
git -C "$REPO" -c protocol.file.allow=always submodule add -q "${REL}"
|
||||||
|
git -C "$REPO" commit -aqm "add submodule"
|
||||||
|
|
||||||
|
GIT_SYNC \
|
||||||
|
--period=100ms \
|
||||||
|
--repo="file://$REPO" \
|
||||||
|
--root="$ROOT" \
|
||||||
|
--link="link" \
|
||||||
|
&
|
||||||
|
wait_for_sync "${MAXWAIT}"
|
||||||
|
assert_link_exists "$ROOT/link"
|
||||||
|
assert_file_exists "$ROOT/link/file"
|
||||||
|
assert_file_exists "$ROOT/link/$SUBMODULE_REPO_NAME/submodule"
|
||||||
|
assert_file_eq "$ROOT/link/$SUBMODULE_REPO_NAME/submodule" "submodule"
|
||||||
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
||||||
|
|
||||||
|
rm -rf $SUBMODULE
|
||||||
|
}
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
# Test SSH
|
# Test SSH
|
||||||
##############################################
|
##############################################
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue