diff --git a/gitindex/clone.go b/gitindex/clone.go index 2399e29fa..493abef64 100644 --- a/gitindex/clone.go +++ b/gitindex/clone.go @@ -22,30 +22,81 @@ import ( "os/exec" "path/filepath" "sort" + "strings" git "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" ) +// returns a list of all the git zoekt settings that have changed or are +// new. To do this it gets the current config, turns into into a map and diffs +// against the new settings map +func getZoektSettingsToUpdate(repoDest string, newSettings map[string]string, newSettingsKeys []string) ([]string, error) { + cmd := exec.Command("git", "-C", repoDest, "config", "--local", "--get-regexp", "zoekt") + outBuf := &bytes.Buffer{} + errBuf := &bytes.Buffer{} + cmd.Stdout = outBuf + cmd.Stderr = errBuf + + if err := cmd.Run(); err != nil { + log.Printf("error getting settings\n") + return nil, err + } + + // collect every current setting and put it into a map + oldSettings := make(map[string]string) + for _, cl := range bytes.Split(outBuf.Bytes(), []byte{'\n'}) { + if len(cl) == 0 { + continue + } + + parts := bytes.SplitN(cl, []byte{' '}, 2) + if len(parts) != 2 { + return nil, fmt.Errorf("more parts than expected in git config key/v line") + } + oldSettings[string(parts[0])] = strings.TrimSpace(string(parts[1])) + } + + // get the list of settings that have changed, or are new + var settingsToUpdate []string + for _, k := range newSettingsKeys { + oldVal, oldHasSetting := oldSettings[k] + if (!oldHasSetting && newSettings[k] != "") || oldVal != newSettings[k] { + settingsToUpdate = append(settingsToUpdate, k) + } + } + + return settingsToUpdate, nil +} + // Updates the zoekt.* git config options after a repo is cloned. // Once a repo is cloned, we can no longer use the --config flag to update all // of it's zoekt.* settings at once. `git config` is limited to one option at once. -func updateZoektGitConfig(repoDest string, settings map[string]string) error { +func updateZoektGitConfig(repoDest string, settings map[string]string) (bool, error) { var keys []string for k := range settings { keys = append(keys, k) } sort.Strings(keys) - for _, k := range keys { + settingsToUpdate, err := getZoektSettingsToUpdate(repoDest, settings, keys) + if err != nil { + return false, err + } + + if len(settingsToUpdate) == 0 { + return false, nil + } + + for _, k := range settingsToUpdate { if settings[k] != "" { if err := exec.Command("git", "-C", repoDest, "config", k, settings[k]).Run(); err != nil { - return err + return false, err } } } - return nil + return true, nil } // CloneRepo clones one repository, adding the given config @@ -61,9 +112,13 @@ func CloneRepo(destDir, name, cloneURL string, settings map[string]string) (stri repoDest := filepath.Join(parent, filepath.Base(name)+".git") if _, err := os.Lstat(repoDest); err == nil { // Repository exists, ensure settings are in sync - if err := updateZoektGitConfig(repoDest, settings); err != nil { + hadUpdate, err := updateZoektGitConfig(repoDest, settings) + if err != nil { return "", fmt.Errorf("failed to update repository settings: %w", err) } + if hadUpdate { + return repoDest, nil + } return "", nil }