xref: /OpenGrok/tools/src/main/python/opengrok_tools/scm/git.py (revision f8b2d387364065cf0db7de33af31ffd9acd8ec9a)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# See LICENSE.txt included in this distribution for the specific
9# language governing permissions and limitations under the License.
10#
11# When distributing Covered Code, include this CDDL HEADER in each
12# file and include the License file at LICENSE.txt.
13# If applicable, add the following below this CDDL HEADER, with the
14# fields enclosed by brackets "[]" replaced with your own identifying
15# information: Portions Copyright [yyyy] [name of copyright owner]
16#
17# CDDL HEADER END
18#
19
20#
21# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
22# Portions Copyright (c) 2020, Krystof Tulinger <k.tulinger@seznam.cz>
23#
24
25from shutil import which
26
27from .repository import Repository, RepositoryException
28from ..utils.command import Command
29
30
31class GitRepository(Repository):
32    def __init__(self, name, logger, path, project, command, env, hooks, timeout):
33        super().__init__(name, logger, path, project, command, env, hooks, timeout)
34
35        self.command = self._repository_command(command, default=lambda: which('git'))
36
37        if not self.command:
38            raise RepositoryException("Cannot get git command")
39
40    def _configure_git_pull(self):
41        # The incoming() check relies on empty output so configure
42        # the repository first to avoid getting extra output.
43        git_command = [self.command, "config", "--local", "pull.ff", "only"]
44        cmd = self.get_command(git_command, work_dir=self.path,
45                               env_vars=self.env, logger=self.logger)
46        cmd.execute()
47        if cmd.getretcode() != 0 or cmd.getstate() != Command.FINISHED:
48            cmd.log_error("failed to configure git pull.ff")
49
50    def reposync(self):
51        self._configure_git_pull()
52        return self._run_custom_sync_command([self.command, 'pull', '--ff-only'])
53
54    def incoming_check(self):
55        self._configure_git_pull()
56        return self._run_custom_incoming_command([self.command, 'pull', '--dry-run'])
57
58    def get_branch(self):
59        status, out = self._run_command([self.command, 'branch', '--show-current'])
60        if status != 0:
61            raise RepositoryException("cannot get branch of {}: {}".format(self, out))
62
63        branch = out.split('\n')[0]
64
65        return branch
66
67    def fetch(self):
68        status, out = self._run_command([self.command, 'fetch'])
69        if status != 0:
70            raise RepositoryException("cannot fetch {}: {}".format(self, out))
71
72    def strip_outgoing(self):
73        """
74        Check for outgoing changes and if found, strip them.
75        :return: True if there were any changes stripped, False otherwise.
76        """
77        self._configure_git_pull()
78        self.fetch()
79        branch = self.get_branch()
80        status, out = self._run_command([self.command, 'log',
81                                        '--pretty=tformat:%H', '--reverse', 'origin/' + branch + '..'])
82        if status == 0:
83            lines = out.split('\n')
84            if len(lines) == 0:
85                return False
86
87            cset = lines[0]
88            if len(cset) > 0:
89                self.logger.debug("Resetting the repository {} to parent of changeset '{}'".
90                                  format(self, cset))
91                status, out = self._run_command([self.command, 'reset', '--hard',
92                                                 cset + '^'])
93                if status != 0:
94                    raise RepositoryException("failed to reset {} to parent of changeset {}: {}".
95                                              format(self, cset, out))
96                else:
97                    return True
98        else:
99            raise RepositoryException("failed to check for outgoing changes in {}: {}".
100                                      format(self, status))
101
102        return False
103