Example #1
0
    def deploy(self, builder, label, target_base):
        for asm in self.assemblies:
            src = os.path.join(asm.get_source_dir(builder), asm.from_rel)
            dst = os.path.join(target_base, asm.to_name)

            if not os.path.exists(src):
                if asm.fail_on_absent_source:
                    raise GiveUp("Deployment %s: source object %s does not"
                                 " exist."%(label.name, src))
                # Else no one cares :-)
            else:
                # If this is a file, just copy it.
                if (not os.path.isdir(src)):
                    if (asm.using_rsync):
                        utils.shell("rsync -avz \"%s\" \"%s\""%(src,dst))
                    else:
                        utils.copy_file(src,dst,object_exactly=asm.copy_exactly)
                elif asm.using_rsync: # Rsync for great speed!
                    try:
                        os.makedirs(dst)
                    except OSError:
                        pass

                    xdst = dst
                    if xdst[-1] != "/":
                        xdst = xdst + "/"

                    utils.run0("rsync -avz \"%s/.\" \"%s\""%(src,xdst))
                elif asm.recursive:
                    utils.recursively_copy(src, dst, object_exactly=asm.copy_exactly)
                else:
                    utils.copy_file(src, dst, object_exactly=asm.copy_exactly)
Example #2
0
    def push(self, repo, options, upstream=None, verbose=True):
        """
        Will be called in the actual checkout's directory.
        """

        utils.shell("bzr push %s"%self._normalised_repo(repo.url),
                   env=self._derive_env(), show_command=verbose)
Example #3
0
    def merge(self, other_repo, options, verbose=True):
        """
        Merge 'other_repo' into the local repository and working tree,

        'bzr merge' will not (by default) merge if there are uncommitted changes
        in the destination (i.e., local) tree. This is what we want.

        Will be called in the actual checkout's directory.
        """
        if other_repo.branch:
            raise utils.GiveUp("Bazaar does not support branch (in the muddle sense)"
                               " in 'merge' (branch='%s')"%other_repo.branch)

        # Refuse to pull if there are any local changes
        env = self._derive_env()
        self._is_it_safe(env)

        rspec = self._r_option(other_repo.revision)

        starting_revno = self._just_revno()

        utils.shell("bzr merge %s %s"%(rspec, self._normalised_repo(other_repo.url)),
                   env=env, show_command=verbose)

        ending_revno = self._just_revno()
        # Did we update anything?
        return starting_revno != ending_revno
Example #4
0
    def push(self, repo, options, upstream=None, verbose=True):
        """
        Will be called in the actual checkout's directory.

        XXX Should we grumble if the 'effective' branch is not the same as
        XXX the branch that is currently checked out?
        """
        self._shallow_not_allowed(options)

        if self._is_detached_HEAD():
            raise GiveUp('This checkout is in "detached HEAD" state, it is not\n'
                         'on any branch, and thus "muddle push" is not alllowed.\n'
                         'If you really want to push, first choose a branch,\n'
                         'e.g., "git checkout -b <new-branch-name>"')

        # Push this branch to the branch of the same name, whether it exists
        # yet or not
        effective_branch = 'HEAD'

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

        # For an upstream, we won't necessarily have the remote in our
        # configuration (unless we already did an upstream pull from the same
        # repository...)
        self._setup_remote(upstream, repo, verbose=verbose)

        utils.shell(["git", "push", upstream, effective_branch], show_command=verbose)
Example #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)
Example #6
0
    def checkout(self, repo, co_leaf, options, verbose=True):
        """
        Clone a given checkout.

        Will be called in the parent directory of the checkout.

        Expected to create a directory called <co_leaf> therein.
        """
        if repo.branch:
            args = ["-b", repo.branch]
        else:
            # Explicitly use master if no branch specified - don't default
            args = ["-b", "master"]

        if options.get('shallow_checkout'):
            args += ["--depth", "1"]

        utils.shell(["git", "clone"] + args +
                    [repo.url, str(co_leaf)],
                    show_command=verbose)

        if repo.revision:
            with Directory(co_leaf):
                # Are we already at the correct revision?
                actual_revision = self._git_rev_parse_HEAD()
                if actual_revision != expand_revision(repo.revision):
                    # XXX Arguably, should use '--quiet', to suppress the warning
                    # XXX that we are ending up in 'detached HEAD' state, since
                    # XXX that is rather what we asked for...
                    # XXX Or maybe we want to leave the message, as the warning
                    # XXX it is meant to be
                    utils.shell(["git", "checkout", repo.revision])
Example #7
0
    def checkout(self, repo, co_leaf, options, verbose=True):
        """
        Clone a given checkout.

        Will be called in the parent directory of the checkout.

        Expected to create a directory called <co_leaf> therein.
        """
        if repo.branch:
            args = ["-b", repo.branch]
        else:
            # Explicitly use master if no branch specified - don't default
            args = ["-b", "master"]

        if options.get('shallow_checkout'):
            args += ["--depth", "1"]

        utils.shell(["git", "clone"] + args + [repo.url, str(co_leaf)],
                   show_command=verbose)

        if repo.revision:
            with Directory(co_leaf):
                # Are we already at the correct revision?
                actual_revision = self._git_rev_parse_HEAD()
                if actual_revision != expand_revision(repo.revision):
                    # XXX Arguably, should use '--quiet', to suppress the warning
                    # XXX that we are ending up in 'detached HEAD' state, since
                    # XXX that is rather what we asked for...
                    # XXX Or maybe we want to leave the message, as the warning
                    # XXX it is meant to be
                    utils.shell(["git", "checkout", repo.revision])
Example #8
0
    def push(self, repo, options, upstream=None, verbose=True):
        """
        Will be called in the actual checkout's directory.

        XXX Should we grumble if the 'effective' branch is not the same as
        XXX the branch that is currently checked out?
        """
        self._shallow_not_allowed(options)

        if self._is_detached_HEAD():
            raise GiveUp(
                'This checkout is in "detached HEAD" state, it is not\n'
                'on any branch, and thus "muddle push" is not alllowed.\n'
                'If you really want to push, first choose a branch,\n'
                'e.g., "git checkout -b <new-branch-name>"')

        # Push this branch to the branch of the same name, whether it exists
        # yet or not
        effective_branch = 'HEAD'

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

        # For an upstream, we won't necessarily have the remote in our
        # configuration (unless we already did an upstream pull from the same
        # repository...)
        self._setup_remote(upstream, repo, verbose=verbose)

        utils.shell(["git", "push", upstream, effective_branch],
                    show_command=verbose)
Example #9
0
    def merge(self, other_repo, options, verbose=True):
        """
        Merge 'other_repo' into the local repository and working tree,

        'bzr merge' will not (by default) merge if there are uncommitted changes
        in the destination (i.e., local) tree. This is what we want.

        Will be called in the actual checkout's directory.
        """
        if other_repo.branch:
            raise utils.GiveUp(
                "Bazaar does not support branch (in the muddle sense)"
                " in 'merge' (branch='%s')" % other_repo.branch)

        # Refuse to pull if there are any local changes
        env = self._derive_env()
        self._is_it_safe(env)

        rspec = self._r_option(other_repo.revision)

        starting_revno = self._just_revno()

        utils.shell("bzr merge %s %s" %
                    (rspec, self._normalised_repo(other_repo.url)),
                    env=env,
                    show_command=verbose)

        ending_revno = self._just_revno()
        # Did we update anything?
        return starting_revno != ending_revno
Example #10
0
    def pull(self, repo, options, upstream=None, verbose=True):
        """
        Will be called in the actual checkout's directory.

        This runs Subversion's "update", but only if no merging will be
        needed. That is, first it runs "svn status", and if any lines
        contain a "C" (for Conflict) in columns 0, 1 or 6, then it will not
        perform the update.

          ("svn help status" would call those columns 1, 2 and 7)
        """
        if repo.branch:
            raise utils.GiveUp("Subversion does not support branch"
                               " in 'pull' (branch='%s')"%repo.branch)
        text = utils.get_cmd_data("svn status")
        for line in text:
            if 'C' in (line[0], line[1], line[6]):
                raise utils.GiveUp("%s: 'svn status' says there is a Conflict,"
                                    " refusing to pull:\n%s\nUse 'muddle merge'"
                                    " if you want to merge"%(utils.indent(text,'    ')))

        starting_revno = self._just_revno()

        utils.shell(["svn", "update"] + self._r_option(repo.revision), show_command=verbose)

        # We could try parsing the output of 'svn update' instead, but this is
        # simpler to do...
        ending_revno = self._just_revno()
        # Did we update anything?
        return starting_revno != ending_revno
Example #11
0
    def add_files(self, files=None, verbose=True):
        """
        If files are given, add them, but do not commit.

        Will be called in the actual checkout's directory.
        """
        if files:
            utils.shell(["svn", "add"] + list(files), show_command=verbose)
Example #12
0
    def push(self, repo, options, upstream=None, verbose=True):
        """
        Will be called in the actual checkout's directory.

        This actually does a "svn commit", i.e., committing to the remote
        repository (which is the only one subversion has).
        """
        utils.shell(["svn", "commit"], show_command=verbose)
Example #13
0
    def commit(self, repo, options, verbose=True):
        """
        Will be called in the actual checkout's directory.

        Does 'git commit -a' - i.e., this implicitly does 'git add' for you.
        This is a contentious choice, and needs review.
        """
        utils.shell(["git", "commit", "-a"], show_command=verbose)
Example #14
0
 def commit(self, repo, options, verbose=True):
     """
     Will be called in the actual checkout's directory.
     """
     # Options: --strict means it will not commit if there are unknown
     # files in the working tree
     utils.shell("bzr commit", allowFailure=True,
                env=self._derive_env(), show_command=verbose)
Example #15
0
    def push(self, repo, options, upstream=None, verbose=True):
        """
        Will be called in the actual checkout's directory.
        """

        utils.shell("bzr push %s" % self._normalised_repo(repo.url),
                    env=self._derive_env(),
                    show_command=verbose)
Example #16
0
    def add_files(self, files=None, verbose=True):
        """
        If files are given, add them, but do not commit.

        Will be called in the actual checkout's directory.
        """
        if files:
            utils.shell("bzr add %s" % ' '.join(files))
Example #17
0
    def add_files(self, files=None, verbose=True):
        """
        If files are given, add them, but do not commit.

        Will be called in the actual checkout's directory.
        """
        if files:
            utils.shell(["git", "add"] + list(files), show_command=verbose)
Example #18
0
    def add_files(self, files=None, verbose=True):
        """
        If files are given, add them, but do not commit.

        Will be called in the actual checkout's directory.
        """
        if files:
            utils.shell("bzr add %s"%' '.join(files))
Example #19
0
    def commit(self, repo, options, verbose=True):
        """
        Will be called in the actual checkout's directory.

        Does 'git commit -a' - i.e., this implicitly does 'git add' for you.
        This is a contentious choice, and needs review.
        """
        utils.shell(["git", "commit", "-a"], show_command=verbose)
Example #20
0
    def init_directory(self, verbose=True):
        """
        If the directory does not appear to have had '<vcs> init' run in it,
        then do so first.

        Will be called in the actual checkout's directory.
        """
        # This is *really* hacky...
        if not os.path.exists('.git'):
            utils.shell(["git", "init"])
Example #21
0
    def init_directory(self, verbose=True):
        """
        If the directory does not appear to have had '<vcs> init' run in it,
        then do so first.

        Will be called in the actual checkout's directory.
        """
        # This is *really* hacky...
        if not os.path.exists('.bzr'):
            utils.shell("bzr init", env=self._derive_env(), show_command=verbose)
Example #22
0
 def commit(self, repo, options, verbose=True):
     """
     Will be called in the actual checkout's directory.
     """
     # Options: --strict means it will not commit if there are unknown
     # files in the working tree
     utils.shell("bzr commit",
                 allowFailure=True,
                 env=self._derive_env(),
                 show_command=verbose)
Example #23
0
    def init_directory(self, verbose=True):
        """
        If the directory does not appear to have had '<vcs> init' run in it,
        then do so first.

        Will be called in the actual checkout's directory.
        """
        # This is *really* hacky...
        if not os.path.exists('.git'):
            utils.shell(["git", "init"])
Example #24
0
    def init_directory(self, verbose=True):
        """
        If the directory does not appear to have had '<vcs> init' run in it,
        then do so first.

        Will be called in the actual checkout's directory.
        """
        # This is *really* hacky...
        if not os.path.exists('.bzr'):
            utils.shell("bzr init",
                        env=self._derive_env(),
                        show_command=verbose)
Example #25
0
    def checkout(self, repo, co_leaf, options, verbose=True):
        """
        Clone a given checkout.

        Will be called in the parent directory of the checkout.

        Expected to create a directory called <co_leaf> therein.
        """
        if repo.branch:
            raise utils.GiveUp("Subversion does not support branch"
                               " in 'checkout' (branch='%s')"%repo.branch)
        utils.shell(["svn", "checkout"] + self._r_option(repo.revision) +
                   [repo.url, co_leaf], show_command=verbose)
Example #26
0
    def checkout(self, repo, co_leaf, options, verbose=True):
        """
        Checkout (clone) a given checkout.

        Will be called in the parent directory of the checkout.

        Expected to create a directory called <co_leaf> therein.
        """
        # Remember that 'bzr checkout' does something different - it produces
        # a checkout that is "bound" to the remote repository, so that doing
        # 'bzr commit' will behave like SVN, and commit/push to the remote
        # repository. We don't want that behaviour.

        if repo.branch:
            raise utils.GiveUp("Bazaar does not support branch (in the muddle sense)"
                               " in 'checkout' (branch='%s')"%repo.branch)
        utils.shell("bzr branch %s %s %s"%(self._r_option(repo.revision),
                                          self._normalised_repo(repo.url),
                                          co_leaf),
                   env=self._derive_env(), show_command=verbose)
Example #27
0
    def checkout(self, repo, co_leaf, options, verbose=True):
        """
        Checkout (clone) a given checkout.

        Will be called in the parent directory of the checkout.

        Expected to create a directory called <co_leaf> therein.
        """
        # Remember that 'bzr checkout' does something different - it produces
        # a checkout that is "bound" to the remote repository, so that doing
        # 'bzr commit' will behave like SVN, and commit/push to the remote
        # repository. We don't want that behaviour.

        if repo.branch:
            raise utils.GiveUp(
                "Bazaar does not support branch (in the muddle sense)"
                " in 'checkout' (branch='%s')" % repo.branch)
        utils.shell("bzr branch %s %s %s" % (self._r_option(
            repo.revision), self._normalised_repo(repo.url), co_leaf),
                    env=self._derive_env(),
                    show_command=verbose)
Example #28
0
    def merge(self, other_repo, options, verbose=True):
        """
        Merge 'other_repo' into the local repository and working tree,

        This runs Subversion's "update" - its "merge" command does something
        different.

        Will be called in the actual checkout's directory.
        """
        if other_repo.branch:
            raise utils.GiveUp("Subversion does not support branch"
                               " in 'merge' (branch='%s')"%other_repo.branch)

        starting_revno = self._just_revno()

        utils.shell(["svn", "update"] +  self._r_option(other_repo.revision),
                   show_command=verbose)

        ending_revno = self._just_revno()
        # Did we update anything?
        return starting_revno != ending_revno
Example #29
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)
Example #30
0
    def deploy(self, builder, label, target_base):
        for asm in self.assemblies:
            src = os.path.join(asm.get_source_dir(builder), asm.from_rel)
            dst = os.path.join(target_base, asm.to_name)

            if not os.path.exists(src):
                if asm.fail_on_absent_source:
                    raise GiveUp("Deployment %s: source object %s does not"
                                 " exist." % (label.name, src))
                # Else no one cares :-)
            else:
                # If this is a file, just copy it.
                if (not os.path.isdir(src)):
                    if (asm.using_rsync):
                        utils.shell("rsync -avz \"%s\" \"%s\"" % (src, dst))
                    else:
                        utils.copy_file(src,
                                        dst,
                                        object_exactly=asm.copy_exactly)
                elif asm.using_rsync:  # Rsync for great speed!
                    try:
                        os.makedirs(dst)
                    except OSError:
                        pass

                    xdst = dst
                    if xdst[-1] != "/":
                        xdst = xdst + "/"

                    utils.run0("rsync -avz \"%s/.\" \"%s\"" % (src, xdst))
                elif asm.recursive:
                    utils.recursively_copy(src,
                                           dst,
                                           object_exactly=asm.copy_exactly)
                else:
                    utils.copy_file(src, dst, object_exactly=asm.copy_exactly)
Example #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)
Example #32
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)
Example #33
0
def git(cmd):
    """Run a git command
    """
    shell('%s %s' % ('git', cmd))
Example #34
0
def svn(cmd):
    """Run a subversion command
    """
    shell('%s %s' % ('svn', cmd))
Example #35
0
def bzr(cmd):
    """Run a bazaar command
    """
    shell('%s %s' % ('bzr', cmd))
Example #36
0
def svn(cmd):
    """Run a subversion command
    """
    shell('%s %s'%('svn',cmd))
Example #37
0
def bzr(cmd):
    """Run a bazaar command
    """
    shell('%s %s'%('bzr',cmd))
Example #38
0
def git(cmd):
    """Run a git command
    """
    shell('%s %s'%('git',cmd))
Example #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
Example #40
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