internal/platform/git/git.go (118 lines of code) (raw):
/*
* Copyright 2021-2024 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git
import (
"fmt"
"strings"
"github.com/JetBrains/qodana-cli/internal/platform/strutil"
"github.com/JetBrains/qodana-cli/internal/platform/utils"
log "github.com/sirupsen/logrus"
)
// gitRun runs the git command in the given directory and returns an error if any.
func gitRun(cwd string, command []string, logdir string) (string, string, error) {
args := []string{"git"}
args = append(args, command...)
logger, err := LOGGER.GetLogger(logdir, "git")
if err != nil {
log.Errorf("Failed to create git logger: %v", err)
return "", "", err
}
stdout, stderr, exitCode, err := utils.RunCmdRedirectOutput(cwd, args...)
if logger != nil {
logger.Printf("Executing command: %v", args)
logger.Println(stdout)
}
if stderr != "" {
if logger != nil {
logger.Error(stderr + "\n")
} else {
log.Error(stderr)
}
}
if exitCode != 0 {
err := fmt.Errorf("command %s exited with code %d", strings.Join(args, " "), exitCode)
log.Errorf("%s", err)
return stdout, stderr, err
}
if err != nil {
log.Errorf("An internal error occured while executing %s: %s", strings.Join(args, " "), err)
return stdout, stderr, err
}
return stdout, stderr, nil
}
// RevParse converts any commit reference (hash, branch name, tag name, etc.) to a full SHA1 commit hash.
func RevParse(cwd string, ref string, logdir string) (string, error) {
stdout, _, err := gitRun(cwd, []string{"rev-parse", "--verify", "--quiet", "--end-of-options", ref}, logdir)
return strings.TrimSpace(stdout), err
}
// Reset resets the git repository to the given commit.
func Reset(cwd string, sha string, logdir string) error {
_, _, err := gitRun(cwd, []string{"reset", "--soft", sha}, logdir)
return err
}
// ResetBack aborts the git reset.
func ResetBack(cwd string, logdir string) error {
_, _, err := gitRun(cwd, []string{"reset", "HEAD@{1}"}, logdir)
return err
}
// CheckoutAndUpdateSubmodule performs a git checkout to the specified rev and updates submodules recursively, QD-10767.
func CheckoutAndUpdateSubmodule(cwd string, where string, force bool, logdir string) error {
err := checkout(cwd, where, force, logdir)
if err != nil {
return err
}
err = submoduleUpdate(cwd, force, logdir)
return err
}
// checkout checks out the given commit / branch.
func checkout(cwd string, where string, force bool, logdir string) error {
var err error
if !force {
_, _, err = gitRun(cwd, []string{"checkout", where}, logdir)
} else {
_, _, err = gitRun(cwd, []string{"checkout", "-f", where}, logdir)
}
return err
}
// GitSubmoduleUpdate updates submodules according to current revision
func submoduleUpdate(cwd string, force bool, logdir string) error {
if !force {
_, _, err := gitRun(cwd, []string{"submodule", "update", "--init", "--recursive"}, logdir)
return err
}
_, _, err := gitRun(cwd, []string{"submodule", "update", "--init", "--recursive", "--force"}, logdir)
return err
}
// Clean cleans the git repository.
func Clean(cwd string, logdir string) error {
_, _, err := gitRun(cwd, []string{"clean", "-fdx"}, logdir)
return err
}
// Revisions returns the list of commits of the git repository in chronological order.
func Revisions(cwd string) []string {
return strutil.Reverse(Log(cwd, "%H", 0))
}
// Root returns absolute path of repo root
func Root(cwd string, logdir string) (string, error) {
stdout, _, err := gitRun(cwd, []string{"rev-parse", "--show-toplevel"}, logdir)
if err != nil {
return "", err
}
return strings.TrimSpace(stdout), nil
}
// RemoteUrl returns the remote url of the git repository.
func RemoteUrl(cwd string, logdir string) (string, error) {
stdout, _, err := gitRun(cwd, []string{"remote", "get-url", "origin"}, logdir)
if err != nil {
return "", err
}
return strings.TrimSpace(stdout), nil
}
// Branch returns the current branch of the git repository.
func Branch(cwd string, logdir string) (string, error) {
stdout, _, err := gitRun(cwd, []string{"rev-parse", "--abbrev-ref", "HEAD"}, logdir)
if err != nil {
return "", err
}
return strings.TrimSpace(stdout), nil
}
func CurrentRevision(cwd string, logdir string) (string, error) {
stdout, _, err := gitRun(cwd, []string{"rev-parse", "HEAD"}, logdir)
if err != nil {
return "", err
}
return strings.TrimSpace(stdout), nil
}
// RevisionExists returns true when revision exists in history.
func RevisionExists(cwd string, revision string, logdir string) bool {
_, stderr, err := gitRun(cwd, []string{"show", "--no-patch", revision}, logdir)
if strings.Contains(stderr, revision) || strings.Contains(stderr, "fatal:") || err != nil {
return false
}
return true
}