Пример #1
0
    def get_current_branch(self):
        """
        Return the name of the current branch.

        Will be called in the actual checkout's directory.

        Returns None if we are not on a branch (e.g., a detached HEAD)
        """
        retcode, out = utils.run2('git symbolic-ref -q HEAD',
                                  show_command=False)
        if retcode == 0:
            out = out.strip()
            if out.startswith('refs/heads'):
                return out[11:]
            else:
                raise GiveUp(
                    'Error running "git symbolic-ref -q HEAD" to determine current branch\n  Got back "%s" instead of "refs/heads/<something>"'
                    % out)
        elif retcode == 1:
            # HEAD is not a symbolic reference, but a detached HEAD
            return None
        else:
            raise GiveUp(
                'Error running "git symbolic-ref -q HEAD" to determine current branch'
            )
Пример #2
0
    def already_installed(self, pkg):
        """
        Decide if the quoted debian package is already installed.

        We use dpkg-query::

            $ dpkg-query -W -f=\${Status}\\n libreadline-dev
            install ok installed

        That third word means what it says (installed). Contrast with a package
        that is either not recognised or has not been downloaded at all::

            $ dpkg-query -W -f=\${Status}\\n a0d
            dpkg-query: no packages found matching a0d

        So we do some fairly simple processing of the output...
        """
        retval, stdout = run2(["dpkg-query", "-W", "-f=\${Status}\\n'", pkg],
                              show_command=False)
        if retval:
            # Assume it's not installed
            return False

        lines = stdout.splitlines()
        if len(lines) == 0:
            return False

        words = lines[0].split()
        if len(words) == 3 and words[2] == 'installed':
            return True
        else:
            return False
Пример #3
0
 def _git_describe_long(self,
                        co_leaf,
                        orig_revision,
                        force=False,
                        verbose=True):
     """
     This returns a "pretty" name for the revision, but only if there
     are annotated tags in its history.
     """
     retcode, revision = utils.run2('git describe --long',
                                    show_command=False)
     if retcode:
         if revision:
             text = utils.indent(revision.strip(), '    ')
             if force:
                 if verbose:
                     print "'git describe --long' had problems with checkout" \
                           " '%s'"%co_leaf
                     print "    %s" % text
                     print "using original revision %s" % orig_revision
                 return orig_revision
         else:
             text = '    (it failed with return code %d)' % retcode
         raise GiveUp("%s\n%s" % (utils.wrap(
             "%s: 'git describe --long'"
             " could not determine a revision id for checkout:" % co_leaf),
                                  text))
     return revision.strip()
Пример #4
0
    def goto_revision(self, revision, branch=None, repo=None, verbose=False):
        """
        Make the specified revision current.

        Note that this may leave the working data (the actual checkout
        directory) in an odd state, in which it is not sensible to
        commit, depending on the VCS and the revision.

        Will be called in the actual checkout's directory.

        If a branch name is given, we will go to that branch first, and see
        if we already got to the correct revision. Note that the check for this
        assumes that 'revision' is a full SHA1, so is a bit simplistic. If we
        don't appear to be at the required revision, we'll then go there as
        normal.

        Raises GiveUp if there is no such revision, or no such branch.
        """
        if branch:
            # First, go to the branch and see if that's all we need to do
            try:
                self.goto_branch(branch)
            except GiveUp as e:
                raise GiveUp('Error going to branch "%s" for'
                             ' revision "%s":\n%s' % (branch, revision, e))
            new_revision = self._git_rev_parse_HEAD()
            if new_revision == expand_revision(
                    revision):  # Heh, we're already there
                return

        retcode, out = utils.run2(['git', 'checkout', revision],
                                  show_command=verbose,
                                  show_output=True)
        if retcode:
            raise GiveUp('Error going to revision "%s": %s' % (revision, out))
Пример #5
0
    def _setup_remote(self, remote_name, remote_repo, verbose=True):
        """
        Re-associate the local repository with a remote.

        Makes some attempt to only do this if necessary. Of course, that may
        actually be slower than just always doing it...
        """
        need_to_set_url = False
        # Are there actually any values already stored for this remote?
        retcode, out = utils.run2("git config --get-regexp remote.%s.*"%remote_name,
                                  show_command=False)
        if retcode == 0:    # there were
            # Were the URLs OK?
            for line in out.split('\n'):
                if not line:
                    continue
                parts = line.split()
                if parts[0].endswith('url'):    # .url, .pushurl
                    url = ' '.join(parts[1:])   # are spaces allowed in our url?
                    if url != str(remote_repo):
                        need_to_set_url = True
                        break
            if need_to_set_url:
                # We don't want to do this unless it is necessary, because it
                # will give an error if there is nothing for it to remove
                utils.shell(["git", "remote", "rm", remote_name], show_command=verbose)
        else:               # there were not
            need_to_set_url = True

        if need_to_set_url:
            # 'git remote add' sets up remote.origin.fetch and remote.origin.url
            # which are the minimum we should need
            utils.shell(["git", "remote", "add", remote_name, remote_repo],
                       show_command=verbose)
Пример #6
0
    def goto_revision(self, revision, branch=None, repo=None, verbose=False):
        """
        Make the specified revision current.

        Note that this may leave the working data (the actual checkout
        directory) in an odd state, in which it is not sensible to
        commit, depending on the VCS and the revision.

        Will be called in the actual checkout's directory.

        If a branch name is given, we will go to that branch first, and see
        if we already got to the correct revision. Note that the check for this
        assumes that 'revision' is a full SHA1, so is a bit simplistic. If we
        don't appear to be at the required revision, we'll then go there as
        normal.

        Raises GiveUp if there is no such revision, or no such branch.
        """
        if branch:
            # First, go to the branch and see if that's all we need to do
            try:
                self.goto_branch(branch)
            except GiveUp as e:
                raise GiveUp('Error going to branch "%s" for'
                             ' revision "%s":\n%s'%(branch, revision, e))
            new_revision = self._git_rev_parse_HEAD()
            if new_revision == expand_revision(revision): # Heh, we're already there
                return

        retcode, out = utils.run2(['git', 'checkout', revision],
                                  show_command=verbose, show_output=True)
        if retcode:
            raise GiveUp('Error going to revision "%s": %s'%(revision, out))
Пример #7
0
    def already_installed(self, pkg):
        """
        Decide if the quoted debian package is already installed.

        We use dpkg-query::

            $ dpkg-query -W -f=\${Status}\\n libreadline-dev
            install ok installed

        That third word means what it says (installed). Contrast with a package
        that is either not recognised or has not been downloaded at all::

            $ dpkg-query -W -f=\${Status}\\n a0d
            dpkg-query: no packages found matching a0d

        So we do some fairly simple processing of the output...
        """
        retval, stdout = run2([ "dpkg-query", "-W", "-f=\${Status}\\n'", pkg ],
                                    show_command=False)
        if retval:
            # Assume it's not installed
            return False

        lines = stdout.splitlines()
        if len(lines) == 0:
            return False

        words = lines[0].split()
        if len(words) == 3 and words[2] == 'installed':
            return True
        else:
            return False
Пример #8
0
    def _calculate_revision(self,
                            co_leaf,
                            orig_revision,
                            force=False,
                            before=None,
                            verbose=True):
        """
        This returns a bare SHA1 object name for the current HEAD

        NB: if 'before' is specified, 'force' is ignored.
        """
        if before:
            print "git rev-list -n 1 --before='%s' HEAD" % before
            retcode, revision = utils.run2(
                "git rev-list -n 1 --before='%s' HEAD" % before,
                show_command=False)
            print retcode, revision
            if retcode:
                if revision:
                    text = utils.indent(revision.strip(), '    ')
                else:
                    text = '    (it failed with return code %d)' % retcode
                raise GiveUp("%s\n%s" % (utils.wrap(
                    "%s:"
                    " \"git rev-list -n 1 --before='%s' HEAD\"'"
                    " could not determine a revision id for checkout:" %
                    (co_leaf, before)), text))
        else:
            retcode, revision = utils.run2('git rev-parse HEAD',
                                           show_command=False)
            if retcode:
                if revision:
                    text = utils.indent(revision.strip(), '    ')
                    if force:
                        if verbose:
                            print "'git rev-parse HEAD' had problems with checkout" \
                                  " '%s'"%co_leaf
                            print "    %s" % text
                            print "using original revision %s" % orig_revision
                        return orig_revision
                else:
                    text = '    (it failed with return code %d)' % retcode
                raise GiveUp("%s\n%s" % (utils.wrap(
                    "%s: 'git rev-parse HEAD'"
                    " could not determine a revision id for checkout:" %
                    co_leaf), text))
        return revision.strip()
Пример #9
0
 def _git_rev_parse_HEAD(self):
     """
     This returns a bare SHA1 object name for the current HEAD
     """
     retcode, revision = utils.run2('git rev-parse HEAD', show_command=False)
     if retcode:
         raise GiveUp("'git rev-parse HEAD' failed with return code %d"%retcode)
     return revision.strip()
Пример #10
0
    def _is_it_safe(self):
        """
        No dentists here...

        Raise an exception if there are (uncommitted) local changes or
        untracked files...
        """
        retcode, text= utils.run2("git status --porcelain", show_command=False)
        if retcode == 129:
            print "Warning: Your git does not support --porcelain; you should upgrade it."
            retcode, text = utils.run2("git status", show_command=False)
            if text.find("working directory clean") >= 0:
                text = ''

        if text:
            raise GiveUp("There are uncommitted changes/untracked files\n"
                         "%s"%utils.indent(text,'    '))
Пример #11
0
    def _is_it_safe(self):
        """
        No dentists here...

        Raise an exception if there are (uncommitted) local changes or
        untracked files...
        """
        retcode, text = utils.run2("git status --porcelain",
                                   show_command=False)
        if retcode == 129:
            print "Warning: Your git does not support --porcelain; you should upgrade it."
            retcode, text = utils.run2("git status", show_command=False)
            if text.find("working directory clean") >= 0:
                text = ''

        if text:
            raise GiveUp("There are uncommitted changes/untracked files\n"
                         "%s" % utils.indent(text, '    '))
Пример #12
0
def expand_revision(revision):
    """Given something that names a revision, return its full SHA1.

    Raises GiveUp if the revision appears non-existent or ambiguous
    """
    rv, out = utils.run2('git rev-parse %s'%revision, show_command=False)
    if rv:
        raise GiveUp('Revision "%s" is either non-existant or ambiguous'%revision)
    return out.strip()
Пример #13
0
def expand_revision(revision):
    """Given something that names a revision, return its full SHA1.

    Raises GiveUp if the revision appears non-existent or ambiguous
    """
    rv, out = utils.run2('git rev-parse %s' % revision, show_command=False)
    if rv:
        raise GiveUp('Revision "%s" is either non-existant or ambiguous' %
                     revision)
    return out.strip()
Пример #14
0
 def _git_rev_parse_HEAD(self):
     """
     This returns a bare SHA1 object name for the current HEAD
     """
     retcode, revision = utils.run2('git rev-parse HEAD',
                                    show_command=False)
     if retcode:
         raise GiveUp("'git rev-parse HEAD' failed with return code %d" %
                      retcode)
     return revision.strip()
Пример #15
0
    def goto_branch(self, branch, verbose=False):
        """
        Make the named branch the current branch.

        Will be called in the actual checkout's directory.

        It is an error if the branch does not exist, in which case a GiveUp
        exception will be raised.
        """
        retcode, out = utils.run2(['git', 'checkout', branch], show_command=verbose)
        if retcode:
            raise GiveUp('Error going to branch "%s": %s'%(branch, out))
Пример #16
0
    def goto_branch(self, branch, verbose=False):
        """
        Make the named branch the current branch.

        Will be called in the actual checkout's directory.

        It is an error if the branch does not exist, in which case a GiveUp
        exception will be raised.
        """
        retcode, out = utils.run2(['git', 'checkout', branch],
                                  show_command=verbose)
        if retcode:
            raise GiveUp('Error going to branch "%s": %s' % (branch, out))
Пример #17
0
    def _run1(self, cmd, env=None, verbose=False, fold_stderr=True):
        """Run command, prune output, return just that.

        If fold_stderr is False, then ignore any stderr output.
        """
        if fold_stderr:
            rc, output = utils.run2(cmd, env=env, show_command=verbose)
        else:
            rc, output, errors = utils.run3(cmd, env=env, show_command=verbose)
        if rc:
            raise utils.ShellError(cmd, rc, output)
        else:
            return self._prune_spurious_bzr_output(output)
Пример #18
0
    def _calculate_revision(self, co_leaf, orig_revision, force=False,
                            before=None, verbose=True):
        """
        This returns a bare SHA1 object name for the current HEAD

        NB: if 'before' is specified, 'force' is ignored.
        """
        if before:
            print "git rev-list -n 1 --before='%s' HEAD"%before
            retcode, revision = utils.run2("git rev-list -n 1 --before='%s' HEAD"%before,
                                           show_command=False)
            print retcode, revision
            if retcode:
                if revision:
                    text = utils.indent(revision.strip(),'    ')
                else:
                    text = '    (it failed with return code %d)'%retcode
                raise GiveUp("%s\n%s"%(utils.wrap("%s:"
                    " \"git rev-list -n 1 --before='%s' HEAD\"'"
                    " could not determine a revision id for checkout:"%(co_leaf, before)),
                    text))
        else:
            retcode, revision = utils.run2('git rev-parse HEAD', show_command=False)
            if retcode:
                if revision:
                    text = utils.indent(revision.strip(),'    ')
                    if force:
                        if verbose:
                            print "'git rev-parse HEAD' had problems with checkout" \
                                  " '%s'"%co_leaf
                            print "    %s"%text
                            print "using original revision %s"%orig_revision
                        return orig_revision
                else:
                    text = '    (it failed with return code %d)'%retcode
                raise GiveUp("%s\n%s"%(utils.wrap("%s: 'git rev-parse HEAD'"
                    " could not determine a revision id for checkout:"%co_leaf),
                    text))
        return revision.strip()
Пример #19
0
    def _run1(self, cmd, env=None, verbose=False, fold_stderr=True):
        """Run command, prune output, return just that.

        If fold_stderr is False, then ignore any stderr output.
        """
        if fold_stderr:
            rc, output = utils.run2(cmd, env=env, show_command=verbose)
        else:
            rc, output, errors = utils.run3(cmd, env=env, show_command=verbose)
        if rc:
            raise utils.ShellError(cmd, rc, output)
        else:
            return self._prune_spurious_bzr_output(output)
Пример #20
0
    def already_installed(self, pkg):
        """
        Decide if a package is installed and the right version
        """
        retval, stdout = run2(
            ["dpkg-query", "-W", "-f=${Status} ${Version}\\n", pkg["name"]],
            show_command=True)
        if retval:
            # Assume it's not installed
            return False

        lines = stdout.splitlines()
        if len(lines) == 0:
            return False

        words = lines[0].split()
        if not (len(words) == 4 and words[2] == 'installed'):
            return False

        vsn_text = words[3]
        vsn = split_debian_version(words[3])
        if ("exact-version" in pkg):
            r = (debian_version_is(vsn,
                                   split_debian_version(pkg['exact-version'])))
            if (r != 0):
                print " %s=%s is not required version %s" % (
                    pkg['name'], vsn_text, pkg['exact-version'])
                return False
            else:
                return True
        if ("min-version" in pkg):
            r = debian_version_is(vsn,
                                  split_debian_version(pkg['min-version']))
            if (r < 0):
                print " %s=%s is too old (require %s)" % (
                    pkg['name'], vsn_text, pkg['min-version'])
                return False
            else:
                return True
        if ("max-version" in pkg):
            r = (debian_version_is(vsn, split_debian_version(
                pkg['max-version'])) < 1)
            if (r > 0):
                print " %s=%s is too new (require <= %s)" % (
                    pkg['name'], vsn_text, pkg['min-version'])
                return False
            else:
                return True

        # If no-one cares about the version, then ..
        return True
Пример #21
0
 def _is_detached_HEAD(self):
     """
     Detect if the current checkout is 'detached HEAD'
     """
     # This is a documented usage of 'git symbolic-ref -q HEAD'
     retcode, out = utils.run2('git symbolic-ref -q HEAD', show_command=False)
     if retcode == 0:
         # HEAD is a symbolic reference - so not detached
         return False
     elif retcode == 1:
         # HEAD is not a symbolic reference, but a detached HEAD
         return True
     else:
         raise GiveUp('Error running "git symbolic-ref -q HEAD" to detect detached HEAD')
Пример #22
0
    def already_installed(self, pkg):
        """
        Decide if a package is installed and the right version
        """
        retval, stdout = run2([ "dpkg-query", "-W", "-f=${Status} ${Version}\\n", pkg["name"] ],
                                    show_command=True)
        if retval:
            # Assume it's not installed
            return False

        lines = stdout.splitlines()
        if len(lines) == 0:
            return False

        words = lines[0].split()
        if not (len(words) == 4 and words[2] == 'installed'):
            return False
        
        vsn_text = words[3]
        vsn = split_debian_version(words[3])
        if ("exact-version" in pkg):
            r = (debian_version_is(vsn, split_debian_version(pkg['exact-version'])))
            if (r != 0):
                print " %s=%s is not required version %s"%(pkg['name'], vsn_text, 
                                                           pkg['exact-version'])
                return False
            else:
                return True
        if ("min-version" in pkg):
            r = debian_version_is(vsn, split_debian_version(pkg['min-version']))
            if (r < 0):
                print " %s=%s is too old (require %s)"%(pkg['name'], vsn_text, 
                                                        pkg['min-version'])
                return False
            else:
                return True
        if ("max-version" in pkg):
            r = (debian_version_is(vsn, split_debian_version(pkg['max-version'])) < 1)
            if (r > 0):
                print " %s=%s is too new (require <= %s)"%(pkg['name'], vsn_text,
                                                          pkg['min-version'])
                return False
            else:
                return True

        # If no-one cares about the version, then .. 
        return True
Пример #23
0
def git_supports_ff_only():
    """
    Does my git support --ff-only?
    """
    global g_supports_ff_only

    if (g_supports_ff_only is None):
        retcode, stdout = utils.run2("git --version", show_command=False)
        version = stdout
        m = re.search(r' ([0-9]+)\.([a0-9]+)', version)
        if (int(m.group(1)) <= 1 and int(m.group(2)) <= 6):
            # As of Jun 2012, that's 2 years ago - when do we drop this support?
            g_supports_ff_only = False
        else:
            g_supports_ff_only = True

    return g_supports_ff_only
Пример #24
0
def git_supports_ff_only():
    """
    Does my git support --ff-only?
    """
    global g_supports_ff_only

    if (g_supports_ff_only is None):
        retcode, stdout = utils.run2("git --version", show_command=False)
        version = stdout
        m = re.search(r' ([0-9]+)\.([a0-9]+)', version)
        if (int(m.group(1)) <= 1 and int(m.group(2)) <= 6):
            # As of Jun 2012, that's 2 years ago - when do we drop this support?
            g_supports_ff_only = False
        else:
            g_supports_ff_only = True

    return g_supports_ff_only
Пример #25
0
 def _is_detached_HEAD(self):
     """
     Detect if the current checkout is 'detached HEAD'
     """
     # This is a documented usage of 'git symbolic-ref -q HEAD'
     retcode, out = utils.run2('git symbolic-ref -q HEAD',
                               show_command=False)
     if retcode == 0:
         # HEAD is a symbolic reference - so not detached
         return False
     elif retcode == 1:
         # HEAD is not a symbolic reference, but a detached HEAD
         return True
     else:
         raise GiveUp(
             'Error running "git symbolic-ref -q HEAD" to detect detached HEAD'
         )
Пример #26
0
    def current_version(self, pkg):
        retval, stdout = run2([ "dpkg-query", "-W", 
                                "-f=${Status} ${Version}\\n", pkg["name"] ],
                                    show_command=True)
        if retval:
            # Assume it's not installed
            return None

        lines = stdout.splitlines()
        if len(lines) == 0:
            return None

        words = lines[0].split()
        if not (len(words) == 4 and words[2] == 'installed'):
            return None
        
        vsn_text = words[3]
        vsn = split_debian_version(words[3])
        return vsn
Пример #27
0
    def current_version(self, pkg):
        retval, stdout = run2(
            ["dpkg-query", "-W", "-f=${Status} ${Version}\\n", pkg["name"]],
            show_command=True)
        if retval:
            # Assume it's not installed
            return None

        lines = stdout.splitlines()
        if len(lines) == 0:
            return None

        words = lines[0].split()
        if not (len(words) == 4 and words[2] == 'installed'):
            return None

        vsn_text = words[3]
        vsn = split_debian_version(words[3])
        return vsn
Пример #28
0
    def get_current_branch(self):
        """
        Return the name of the current branch.

        Will be called in the actual checkout's directory.

        Returns None if we are not on a branch (e.g., a detached HEAD)
        """
        retcode, out = utils.run2('git symbolic-ref -q HEAD', show_command=False)
        if retcode == 0:
            out = out.strip()
            if out.startswith('refs/heads'):
                return out[11:]
            else:
                raise GiveUp('Error running "git symbolic-ref -q HEAD" to determine current branch\n  Got back "%s" instead of "refs/heads/<something>"'%out)
        elif retcode == 1:
            # HEAD is not a symbolic reference, but a detached HEAD
            return None
        else:
            raise GiveUp('Error running "git symbolic-ref -q HEAD" to determine current branch')
Пример #29
0
    def branch_exists(self, branch):
        """
        Is there a branch of this name?

        Will be called in the actual checkout's directory.
        """
        retcode, out = utils.run2('git branch -a', show_command=False)
        if retcode:
            raise GiveUp('Error looking up existing branches: %s'%out)

        lines = out.split('\n')
        for line in lines:
            text = line[2:]         # Ignore the initial '  ' or '* '
            text = text.strip()     # Ignore trailing whitespace
            if '->' in text:        # Ignore 'remotes/origin/HEAD -> origin/master'
                continue
            if '/' in text:
                text = text.split('/')[-1]  # just take the name at the end
            if text == branch:
                return True
        return False
Пример #30
0
    def branch_exists(self, branch):
        """
        Is there a branch of this name?

        Will be called in the actual checkout's directory.
        """
        retcode, out = utils.run2('git branch -a', show_command=False)
        if retcode:
            raise GiveUp('Error looking up existing branches: %s' % out)

        lines = out.split('\n')
        for line in lines:
            text = line[2:]  # Ignore the initial '  ' or '* '
            text = text.strip()  # Ignore trailing whitespace
            if '->' in text:  # Ignore 'remotes/origin/HEAD -> origin/master'
                continue
            if '/' in text:
                text = text.split('/')[-1]  # just take the name at the end
            if text == branch:
                return True
        return False
Пример #31
0
    def create_branch(self, branch, verbose=False):
        """
        Create a branch of the given name.

        Will be called in the actual checkout's directory.

        Also sets up the equivalent remote.

        It is an error if the branch already exists, in which case a GiveUp
        exception will be raised.
        """
        # If the user tried "git branch 'sp ace'", which is an illegal branch
        # name, we want the command to propagate 'sp ace' down as a single
        # word, so it gets reported with the appropriate error. Thus we need
        # to pass the command as a list.
        retcode, out = utils.run2(['git', 'branch', branch], show_command=verbose)
        if retcode:
            raise GiveUp('Error creating branch "%s": %s'%(branch, out))

        # Add this branch to the 'origin' remote for this checkout
        utils.shell(["git", "remote", "set-branches", "--add", "origin", branch],
                   show_command=verbose)
Пример #32
0
 def _git_describe_long(self, co_leaf, orig_revision, force=False, verbose=True):
     """
     This returns a "pretty" name for the revision, but only if there
     are annotated tags in its history.
     """
     retcode, revision = utils.run2('git describe --long', show_command=False)
     if retcode:
         if revision:
             text = utils.indent(revision.strip(),'    ')
             if force:
                 if verbose:
                     print "'git describe --long' had problems with checkout" \
                           " '%s'"%co_leaf
                     print "    %s"%text
                     print "using original revision %s"%orig_revision
                 return orig_revision
         else:
             text = '    (it failed with return code %d)'%retcode
         raise GiveUp("%s\n%s"%(utils.wrap("%s: 'git describe --long'"
             " could not determine a revision id for checkout:"%co_leaf),
             text))
     return revision.strip()
Пример #33
0
    def create_branch(self, branch, verbose=False):
        """
        Create a branch of the given name.

        Will be called in the actual checkout's directory.

        Also sets up the equivalent remote.

        It is an error if the branch already exists, in which case a GiveUp
        exception will be raised.
        """
        # If the user tried "git branch 'sp ace'", which is an illegal branch
        # name, we want the command to propagate 'sp ace' down as a single
        # word, so it gets reported with the appropriate error. Thus we need
        # to pass the command as a list.
        retcode, out = utils.run2(['git', 'branch', branch],
                                  show_command=verbose)
        if retcode:
            raise GiveUp('Error creating branch "%s": %s' % (branch, out))

        # Add this branch to the 'origin' remote for this checkout
        utils.shell(
            ["git", "remote", "set-branches", "--add", "origin", branch],
            show_command=verbose)
Пример #34
0
    def _setup_remote(self, remote_name, remote_repo, verbose=True):
        """
        Re-associate the local repository with a remote.

        Makes some attempt to only do this if necessary. Of course, that may
        actually be slower than just always doing it...
        """
        need_to_set_url = False
        # Are there actually any values already stored for this remote?
        retcode, out = utils.run2("git config --get-regexp remote.%s.*" %
                                  remote_name,
                                  show_command=False)
        if retcode == 0:  # there were
            # Were the URLs OK?
            for line in out.split('\n'):
                if not line:
                    continue
                parts = line.split()
                if parts[0].endswith('url'):  # .url, .pushurl
                    url = ' '.join(parts[1:])  # are spaces allowed in our url?
                    if url != str(remote_repo):
                        need_to_set_url = True
                        break
            if need_to_set_url:
                # We don't want to do this unless it is necessary, because it
                # will give an error if there is nothing for it to remove
                utils.shell(["git", "remote", "rm", remote_name],
                            show_command=verbose)
        else:  # there were not
            need_to_set_url = True

        if need_to_set_url:
            # 'git remote add' sets up remote.origin.fetch and remote.origin.url
            # which are the minimum we should need
            utils.shell(["git", "remote", "add", remote_name, remote_repo],
                        show_command=verbose)
Пример #35
0
 def _run2(self, cmd, env=None, verbose=False):
     """Run command, prune output, return return code and output.
     """
     rc, output = utils.run2(cmd, env=env, show_command=verbose)
     return rc, self._prune_spurious_bzr_output(output)
Пример #36
0
 def _run2(self, cmd, env=None, verbose=False):
     """Run command, prune output, return return code and output.
     """
     rc, output = utils.run2(cmd, env=env, show_command=verbose)
     return rc, self._prune_spurious_bzr_output(output)
Пример #37
0
    def _pull_or_merge(self, repo, options, upstream=None, verbose=True, merge=False):
        """
        Will be called in the actual checkout's directory.
        """
        starting_revision = self._git_rev_parse_HEAD()

        if merge:
            cmd = 'merge'
        else:
            cmd = 'pull'

        if repo.revision:
            revision = expand_revision(repo.revision)
        else:
            revision = None

        if revision and revision == starting_revision:

            # XXX We really only want to grumble here if an unfettered pull
            # XXX would take us past this revision - if it would have had
            # XXX no effect, it seems a bit unfair to complain.
            # XXX So ideally we'd check here with whatever is currently
            # XXX fetched, and then (if necessary) check again further on
            # XXX after actually doing a fetch. But it's not obvious how
            # XXX to do this, so I shall ignore it for the moment...
            #
            # XXX (checking HEAD against FETCH_HEAD may or may not be
            # XXX helpful)

            # It's a bit debatable whether we should raise GiveUp or just
            # print the message and return. However, if we just printed, the
            # "report any problems" mechanism in the "muddle pull" command
            # would not know to mention this happening, which would mean that
            # for a joint pull of many checkouts, such messages might get lost.
            # So we'll go with (perhaps) slightly overkill approach.
            raise GiveUp(\
                "The build description specifies revision %s... for this checkout,\n"
                "and it is already at that revision. 'muddle %s' will not take the\n"
                "checkout past the specified revision."%(repo.revision[:8], cmd))

        # Refuse to do anything if there are any local changes or untracked files.
        self._is_it_safe()

        # Are we on the correct branch?
        this_branch = self.get_current_branch()
        if repo.branch is None:
            if this_branch != 'master':
                self.goto_branch('master')
        elif repo.branch != this_branch:
            self.goto_branch(repo.branch)

        if not upstream:
            # If we're not given an upstream repository name, assume we're
            # dealing with an "ordinary" pull, from our origin
            upstream = 'origin'

        self._setup_remote(upstream, repo, verbose=verbose)

        # Retrieve changes from the remote repository to the local repository
        # We want to get the output from this so we can put it into any exception,
        # for instance if we try to fetch a branch that does not exist.
        # This *does* mean there's a slight delay before the user sees the output,
        # though
        cmd = "git fetch %s"%upstream
        rv, out = utils.run2(cmd, show_command=verbose)
        if rv:
            raise GiveUp('Error %d running "%s"\n%s'%(rv, cmd, out))
        else:
            # The older version of this code just used utils.run_cmd(), which
            # runs the command in a sub-shell, and thus its output is always
            # presented, regardless of the "verbose" setting. For the moment
            # at least, we'll stay compatible with that.
            print out.rstrip()

        if repo.branch is None:
            remote = 'remotes/%s/master'%(upstream)
        else:
            remote = 'remotes/%s/%s'%(upstream,repo.branch)

        if repo.revision:
            # If the build description specifies a particular revision, all we
            # can really do is go to that revision (we did the fetch anyway in
            # case the user had edited the build description to refer to a
            # revision id we had not yet reached).
            #
            # This may also be a revision specified in an "unstamp -update"
            # operation, which is why the message doesn't say it's the build
            # description we're obeying
            print '++ Just changing to the revision explicitly requested for this checkout'
            utils.shell(["git", "checkout", repo.revision])
        elif merge:
            # Just merge what we fetched into the current working tree
            utils.shell(["git", "merge", remote], show_command=verbose)
        else:
            # Merge what we fetched into the working tree, but only if this is
            # a fast-forward merge, and thus doesn't require the user to do any
            # thinking.
            # Don't specify branch name: we definitely want to update our idea
            # of where the remote head points to be updated.
            # (See git-pull(1) and git-fetch(1): "without storing the remote
            # branch anywhere locally".)
            if (git_supports_ff_only()):
                utils.shell(["git", "merge", "--ff-only", remote], show_command=verbose)
            else:
                utils.shell(["git", "merge", "--ff", remote], show_command=verbose)

        ending_revision = self._git_rev_parse_HEAD()

        # So, did we update things?
        return starting_revision != ending_revision
Пример #38
0
    def status(self, repo, options, quick=False):
        """
        Will be called in the actual checkout's directory.

        Return status text or None if there is no interesting status.
        """
        retcode, text = utils.run2("git status --porcelain", show_command=False)
        if retcode == 129:
            print "Warning: Your git does not support --porcelain; you should upgrade it."
            retcode, text = utils.run2("git status", show_command=False)

        detached_head = self._is_detached_HEAD()

        if detached_head:
            # That's all the user really needs to know
            note = '\n# Note that this checkout has a detached HEAD'
            if text:
                text = '%s\n#%s'%(text, note)
            else:
                text = note

        if text:
            return text

        # git status will tell us if there uncommitted changes, etc., but not if
        # we are ahead of or behind (the local idea of) the remote repository,
        # or it will, but not with --porcelain


        if detached_head:
            head_name = 'HEAD'
        else:
            # First, find out what our HEAD actually is
            retcode, text = utils.run2("git rev-parse --symbolic-full-name HEAD",
                                       show_command=False)
            head_name = text.strip()

        # Now we can look up its SHA1, locally
        retcode, head_revision = utils.run2("git rev-parse %s"%head_name,
                                            show_command=False)
        local_head_ref = head_revision.strip()

        if quick:
            branch_name = utils.get_cmd_data("git rev-parse --abbrev-ref HEAD")
            text = utils.get_cmd_data("git show-ref origin/%s"%branch_name)
            ref, what = text.split()
            if ref != local_head_ref:
                return '\n'.join(
                    ('After checking local HEAD against our local record of the remote HEAD',
                     '# The local repository does not match the remote:',
                     '#',
                     '#  HEAD   is %s'%head_name,
                     '#  Local  is %s'%local_head_ref,
                     '#  last known origin/%s is %s'%(branch_name, ref),
                     '#',
                     '# You probably need to push or pull.',
                     '# Use "muddle status" without "-quick" to get a better idea'))
            else:
                return None

        # So look up the remote equivalents...
        retcode, text = utils.run2("git ls-remote", show_command=False)
        lines = text.split('\n')
        if retcode:
            # Oh dear - something nasty happened
            # We know we get this if, for instance, the remote repository does
            # not actually exist
            newlines = []
            newlines.append('Whilst trying to check local HEAD against remote HEAD')
            for line in lines:
                newlines.append('# %s'%line)
            return '\n'.join(newlines)
        else:
            for line in lines[1:]:          # The first line is the remote repository
                if not line:
                    continue
                ref, what = line.split('\t')
                if what == head_name:
                    if ref != local_head_ref:
                        return '\n'.join(('After checking local HEAD against remote HEAD',
                                          '# The local repository does not match the remote:',
                                          '#',
                                          '#  HEAD   is %s'%head_name,
                                          '#  Local  is %s'%local_head_ref,
                                          '#  Remote is %s'%ref,
                                          '#',
                                          '# You probably need to pull with "muddle pull".'))

        # Should we check to see if we found HEAD?

        return None
Пример #39
0
    def _pull_or_merge(self,
                       repo,
                       options,
                       upstream=None,
                       verbose=True,
                       merge=False):
        """
        Will be called in the actual checkout's directory.
        """
        starting_revision = self._git_rev_parse_HEAD()

        if merge:
            cmd = 'merge'
        else:
            cmd = 'pull'

        if repo.revision:
            revision = expand_revision(repo.revision)
        else:
            revision = None

        if revision and revision == starting_revision:

            # XXX We really only want to grumble here if an unfettered pull
            # XXX would take us past this revision - if it would have had
            # XXX no effect, it seems a bit unfair to complain.
            # XXX So ideally we'd check here with whatever is currently
            # XXX fetched, and then (if necessary) check again further on
            # XXX after actually doing a fetch. But it's not obvious how
            # XXX to do this, so I shall ignore it for the moment...
            #
            # XXX (checking HEAD against FETCH_HEAD may or may not be
            # XXX helpful)

            # It's a bit debatable whether we should raise GiveUp or just
            # print the message and return. However, if we just printed, the
            # "report any problems" mechanism in the "muddle pull" command
            # would not know to mention this happening, which would mean that
            # for a joint pull of many checkouts, such messages might get lost.
            # So we'll go with (perhaps) slightly overkill approach.
            raise GiveUp(\
                "The build description specifies revision %s... for this checkout,\n"
                "and it is already at that revision. 'muddle %s' will not take the\n"
                "checkout past the specified revision."%(repo.revision[:8], cmd))

        # Refuse to do anything if there are any local changes or untracked files.
        self._is_it_safe()

        # Are we on the correct branch?
        this_branch = self.get_current_branch()
        if repo.branch is None:
            if this_branch != 'master':
                self.goto_branch('master')
        elif repo.branch != this_branch:
            self.goto_branch(repo.branch)

        if not upstream:
            # If we're not given an upstream repository name, assume we're
            # dealing with an "ordinary" pull, from our origin
            upstream = 'origin'

        self._setup_remote(upstream, repo, verbose=verbose)

        # Retrieve changes from the remote repository to the local repository
        # We want to get the output from this so we can put it into any exception,
        # for instance if we try to fetch a branch that does not exist.
        # This *does* mean there's a slight delay before the user sees the output,
        # though
        cmd = "git fetch %s" % upstream
        rv, out = utils.run2(cmd, show_command=verbose)
        if rv:
            raise GiveUp('Error %d running "%s"\n%s' % (rv, cmd, out))
        else:
            # The older version of this code just used utils.run_cmd(), which
            # runs the command in a sub-shell, and thus its output is always
            # presented, regardless of the "verbose" setting. For the moment
            # at least, we'll stay compatible with that.
            print out.rstrip()

        if repo.branch is None:
            remote = 'remotes/%s/master' % (upstream)
        else:
            remote = 'remotes/%s/%s' % (upstream, repo.branch)

        if repo.revision:
            # If the build description specifies a particular revision, all we
            # can really do is go to that revision (we did the fetch anyway in
            # case the user had edited the build description to refer to a
            # revision id we had not yet reached).
            #
            # This may also be a revision specified in an "unstamp -update"
            # operation, which is why the message doesn't say it's the build
            # description we're obeying
            print '++ Just changing to the revision explicitly requested for this checkout'
            utils.shell(["git", "checkout", repo.revision])
        elif merge:
            # Just merge what we fetched into the current working tree
            utils.shell(["git", "merge", remote], show_command=verbose)
        else:
            # Merge what we fetched into the working tree, but only if this is
            # a fast-forward merge, and thus doesn't require the user to do any
            # thinking.
            # Don't specify branch name: we definitely want to update our idea
            # of where the remote head points to be updated.
            # (See git-pull(1) and git-fetch(1): "without storing the remote
            # branch anywhere locally".)
            if (git_supports_ff_only()):
                utils.shell(["git", "merge", "--ff-only", remote],
                            show_command=verbose)
            else:
                utils.shell(["git", "merge", "--ff", remote],
                            show_command=verbose)

        ending_revision = self._git_rev_parse_HEAD()

        # So, did we update things?
        return starting_revision != ending_revision
Пример #40
0
    def status(self, repo, options, quick=False):
        """
        Will be called in the actual checkout's directory.

        Return status text or None if there is no interesting status.
        """
        retcode, text = utils.run2("git status --porcelain",
                                   show_command=False)
        if retcode == 129:
            print "Warning: Your git does not support --porcelain; you should upgrade it."
            retcode, text = utils.run2("git status", show_command=False)

        detached_head = self._is_detached_HEAD()

        if detached_head:
            # That's all the user really needs to know
            note = '\n# Note that this checkout has a detached HEAD'
            if text:
                text = '%s\n#%s' % (text, note)
            else:
                text = note

        if text:
            return text

        # git status will tell us if there uncommitted changes, etc., but not if
        # we are ahead of or behind (the local idea of) the remote repository,
        # or it will, but not with --porcelain

        if detached_head:
            head_name = 'HEAD'
        else:
            # First, find out what our HEAD actually is
            retcode, text = utils.run2(
                "git rev-parse --symbolic-full-name HEAD", show_command=False)
            head_name = text.strip()

        # Now we can look up its SHA1, locally
        retcode, head_revision = utils.run2("git rev-parse %s" % head_name,
                                            show_command=False)
        local_head_ref = head_revision.strip()

        if quick:
            branch_name = utils.get_cmd_data("git rev-parse --abbrev-ref HEAD")
            text = utils.get_cmd_data("git show-ref origin/%s" % branch_name)
            ref, what = text.split()
            if ref != local_head_ref:
                return '\n'.join((
                    'After checking local HEAD against our local record of the remote HEAD',
                    '# The local repository does not match the remote:', '#',
                    '#  HEAD   is %s' % head_name,
                    '#  Local  is %s' % local_head_ref,
                    '#  last known origin/%s is %s' % (branch_name, ref), '#',
                    '# You probably need to push or pull.',
                    '# Use "muddle status" without "-quick" to get a better idea'
                ))
            else:
                return None

        # So look up the remote equivalents...
        retcode, text = utils.run2("git ls-remote", show_command=False)
        lines = text.split('\n')
        if retcode:
            # Oh dear - something nasty happened
            # We know we get this if, for instance, the remote repository does
            # not actually exist
            newlines = []
            newlines.append(
                'Whilst trying to check local HEAD against remote HEAD')
            for line in lines:
                newlines.append('# %s' % line)
            return '\n'.join(newlines)
        else:
            for line in lines[1:]:  # The first line is the remote repository
                if not line:
                    continue
                ref, what = line.split('\t')
                if what == head_name:
                    if ref != local_head_ref:
                        return '\n'.join((
                            'After checking local HEAD against remote HEAD',
                            '# The local repository does not match the remote:',
                            '#', '#  HEAD   is %s' % head_name,
                            '#  Local  is %s' % local_head_ref,
                            '#  Remote is %s' % ref, '#',
                            '# You probably need to pull with "muddle pull".'))

        # Should we check to see if we found HEAD?

        return None