Пример #1
0
    def checkout(self):
        shallow = self.conf.get("shallow", False)

        if self.ref_type == 'commit' and shallow:
            # We cannot straight clone a shallow repo using a commit hash (-b doesn't support it)
            # do the dance described at https://stackoverflow.com/a/43136160/214671
            os.mkdir(self.directory)
            with cd(self.directory):
                fork(['git', 'init'])
                fork(['git', 'remote', 'add', 'origin', self.url.geturl()])
                fork([
                    'git', 'fetch', '--depth', '1', 'origin',
                    self.noremote_ref()
                ])
                fork(['git', 'checkout', self.ref, '--'])
        else:
            # Regular case
            extra_opts = []
            if shallow:
                extra_opts += ["--depth", "1"]
            # Needed essentially for the shallow case, as for full clones the
            # git clone -n + git checkout would suffice
            if shallow and self.ref_type != 'commit' and self.ref != 'origin/HEAD':
                extra_opts += ['-b', self.noremote_ref()]
            fork(['git', 'clone', '-n'] + extra_opts +
                 ['--', self.url.geturl(), self.directory])
            with cd(self.directory):
                opts = [self.ref]
                # If it's a branch, create a remote-tracking one
                if self.ref_type == 'branch' and not shallow:
                    # Find out a sensible local branch name (needed for origin/HEAD)
                    local_branch = self.symbolic_full_name(self.ref).split(
                        '/origin/', 1)[1]
                    opts = [local_branch]
                fork(['git', 'checkout'] + opts + ['--'])
Пример #2
0
    def update(self, clean=False):
        if not exists(self.directory):
            self.checkout()
        elif not exists(self.directory + "/.svn"):
            not_a_project(self.directory, "Subversion")
        else:
            with cd(self.directory):
                if self.has_local_edit():
                    if clean:
                        fork(['svn', 'revert', '-R', '.'])
                        fork(['svn', 'cleanup', '--remove-unversioned', '.'])
                    else:
                        logger.warning(
                            "Directory '%s' contains local modifications" %
                            self.directory)
                # svn switch _would be ok_ even just to perform an update, but,
                # unlike svn up, it touches the timestamp of all the files,
                # forcing full rebuilds; so, if we are already on the correct
                # url just use svn up

                # Notice that, unlike other svn commands, -r in svn up works as
                # a peg revision (the @ syntax), so it takes the URL of the
                # current working copy and looks it up in the repository _as it
                # was at the requested revision_ (or HEAD if none is specified)
                target_base, target_rev = (self.url.geturl().split('@') +
                                           [''])[:2]
                if target_base == self.url_from_checkout(include_commit=False):
                    fork(['svn', 'up'] +
                         (["-r" + target_rev] if target_rev else []))
                else:
                    fork(['svn', 'switch', self.url.geturl()])
Пример #3
0
    def mirror(self, dst_dir):
        source_dir = self.directory

        def mkdir_p(path):
            if path.strip() != '' and not os.path.exists(path):
                os.makedirs(path)

        env = os.environ.copy()
        env['LC_MESSAGES'] = 'C'

        def tracked_files():
            p = Popen(['git', 'ls-tree', '-r', '--name-only', 'HEAD'],
                      stdout=PIPE,
                      env=env)
            out = p.communicate()[0]
            if p.returncode != 0 or not out.strip():
                return None
            return [e.strip() for e in out.splitlines() if os.path.exists(e)]

        def cp(src, dst):
            r, f = os.path.split(dst)
            mkdir_p(r)
            shutil.copy2(src, dst)

        with cd(source_dir):
            for t in tracked_files():
                cp(t, os.path.join(dst_dir, t.decode()))
Пример #4
0
 def update(self):
     if not exists(self.directory):
         self.checkout()
     elif self.has_local_edit():
         logger.warning("Directory '%s' contains local modifications" % self.directory)
     else:
         with cd(self.directory):
             fork(['svn', 'switch', self.url.geturl()])
Пример #5
0
 def url_from_directory(directory, include_commit = True):
     with cd(directory):
         origin = check_output(['git', 'remote', 'get-url', 'origin'], universal_newlines=True)[:-1]
         commit = check_output(['git', 'log', '-1', '--format=%H'], universal_newlines=True)[:-1]
     ret = 'git+%s' % (origin,)
     if include_commit:
         ret += '#commit=%s' % (commit,)
     return ret
Пример #6
0
 def checkout(self):
     fork(['git', 'clone', '-n', '--', self.url.geturl(), self.directory])
     with cd(self.directory):
         try:
             fork(['git', 'checkout', self.ref])
         except CalledProcessError:
             fork(['git', 'branch', '--remote'])
             raise
Пример #7
0
 def update(self):
     if not exists(self.directory):
         self.checkout()
     elif self.has_local_edit():
         logger.warning("Directory '%s' contains local modifications" % self.directory)
     else:
         with cd(self.directory):
             fork(['git', 'fetch'])
             fork(['git', 'checkout', self.ref, '--'])
Пример #8
0
        def actualUpdate():
            with cd(self.directory):
                try:
                    current_origin = log_check_output(['git', 'config', '--get', 'remote.origin.url']).strip().decode('utf-8')
                except CalledProcessError:
                    current_origin = None
                if current_origin != self.url.geturl():
                    # For now we just throw a fit; it shouldn't happen often,
                    # and in this case there's no "right" answer - the repo may
                    # have just moved, or we may be dealing with a completely
                    # unrelated repo.
                    # In future, it would be nice to be a bit more interactive
                    raise QuarkError("""

Directory '%s' is a git repository,
but its remote 'origin' (%r)
does not match what we expect (%r).

Please either remove the local clone, or fix its remote.""" % (self.directory, current_origin, self.url.geturl()))
                if self.conf.get("shallow", False):
                    # Fetch just the commit we need
                    fork(['git', 'fetch', '--depth', '1', 'origin', self.noremote_ref()])
                    # Notice that we need FETCH_HEAD, as the shallow clone does not recognize
                    # origin/HEAD & co.
                    fork(['git', 'checkout', 'FETCH_HEAD', '--'])
                else:
                    fork(['git', 'fetch'])
                    # If we want to go on a branch, try to find a local branch that tracks it
                    # and use it (possibly with a fast-forward)
                    if self.ref_type == 'branch':
                        # Resolve the remote ref
                        remote_fullref = self.symbolic_full_name(self.ref)
                        # Get a sensible local branch name to try
                        local_ref = remote_fullref.split('/origin/', 1)[1]
                        # Check if it is actually tracking our target
                        try:
                            local_fulltrackref = self.symbolic_full_name(local_ref + "@{u}")
                        except CalledProcessError:
                            # It's fine if it fails - we may not have a local-tracking branch,
                            # so git checkout will do the right thing here
                            local_fulltrackref = remote_fullref

                        if remote_fullref == local_fulltrackref:
                            try:
                                # Checkout and fast-forward
                                fork(['git', 'checkout', local_ref, '--'])
                                fork(['git', 'merge', '--ff-only', self.ref, '--'])
                                # Final sanity check
                                if log_check_output(['git', 'rev-parse', self.ref, '--']) != log_check_output(['git', 'rev-parse', local_ref, '--']):
                                    logger.warning("Warning: your local branch is ahead of required remote branch!")
                                return
                            except CalledProcessError:
                                logger.warning("Couldn't fast-forward local branch, fallback to detached head mode...")
                    # General case: plain checkout of the origin ref (going in detached HEAD)
                    fork(['git', 'checkout', self.ref, '--'])
Пример #9
0
 def check_origin(self):
     with cd(self.directory):
         if check_output(['git', 'config', '--get', 'remote.origin.url']) != self.url:
             if not self.has_local_edit():
                 logger.warning("%s is not a clone of %s "
                                "but it hasn't local modifications, "
                                "removing it..", self.directory, self.url.geturl())
                 rmtree(self.directory)
                 self.checkout()
             else:
                 raise ValueError(
                     "'%s' is not a clone of '%s' and has local"
                     " modifications, I don't know what to do with it..." %
                     self.directory, self.url.geturl())
Пример #10
0
 def symbolic_full_name(self, ref):
     with cd(self.directory):
         return log_check_output(
             ['git', 'rev-parse', '--symbolic-full-name', ref,
              '--']).split(b'\n')[0].strip().decode('utf-8')
Пример #11
0
 def has_local_edit(self):
     with cd(self.directory):
         return log_check_output(['git', 'status', '--porcelain']) != b""
Пример #12
0
 def clean_all(self):
     with cd(self.directory):
         fork(['git', 'clean', '-fd'])
Пример #13
0
 def pop(self):
     with cd(self.directory):
         fork(['git', 'stash', 'pop'])
Пример #14
0
 def stash(self):
     with cd(self.directory):
         fork(['git', 'stash', '--all'])
Пример #15
0
        def actualUpdate():
            with cd(self.directory):
                try:
                    current_origin = log_check_output(
                        ['git', 'config', '--get',
                         'remote.origin.url']).strip().decode('utf-8')
                except CalledProcessError:
                    current_origin = None
                if current_origin != self.url.geturl():
                    # For now we just throw a fit; it shouldn't happen often,
                    # and in this case there's no "right" answer - the repo may
                    # have just moved, or we may be dealing with a completely
                    # unrelated repo.
                    # In future, it would be nice to be a bit more interactive
                    raise QuarkError(
                        """

Directory '%s' is a git repository,
but its remote 'origin' (%r)
does not match what we expect (%r).

Please either remove the local clone, or fix its remote.""" %
                        (self.directory, current_origin, self.url.geturl()))
                if self.conf.get("shallow", False):
                    # git fetch with shallow clones isn't very smart, and
                    # re-fetches stuff that we already have; try to avoid this

                    # FIXME: the current approach means that shallow clones
                    # will be in detached HEAD state to a commit pretty much
                    # always.
                    # Probably this is not a big problem because shallow repos
                    # in Quark aren't meant to be used for actual repo work,
                    # and the previous implementation always had FETCH_HEAD
                    # checked out; however, in future it would be nice to have
                    # a cleaner solution.

                    if self.ref_type == 'commit':
                        # Easy case: we already know exactly the commit we need
                        remote_commit = self.ref
                    else:
                        # Ask the remote what we are expected to have here
                        remote_commit = log_check_output([
                            'git', 'ls-remote', 'origin',
                            self.noremote_ref()
                        ]).split(b'\t')[0].strip().decode('utf-8')

                    try:
                        # Try to check it out; in the common case (nothing
                        # changed) this should be a no-op
                        fork(['git', 'checkout', remote_commit, '--'])
                    except CalledProcessError:
                        # Probably we don't have the commit; fetch it
                        fork([
                            'git', 'fetch', '--depth', '1', 'origin',
                            remote_commit
                        ])
                        # Try again
                        fork(['git', 'checkout', remote_commit, '--'])
                else:
                    fork(['git', 'fetch'])
                    # If we want to go on a branch, try to find a local branch that tracks it
                    # and use it (possibly with a fast-forward)
                    if self.ref_type == 'branch':
                        # Resolve the remote ref
                        remote_fullref = self.symbolic_full_name(self.ref)
                        # Get a sensible local branch name to try
                        local_ref = remote_fullref.split('/origin/', 1)[1]
                        # Check if it is actually tracking our target
                        try:
                            local_fulltrackref = self.symbolic_full_name(
                                local_ref + "@{u}")
                        except CalledProcessError:
                            # It's fine if it fails - we may not have a local-tracking branch,
                            # so git checkout will do the right thing here
                            local_fulltrackref = remote_fullref

                        if remote_fullref == local_fulltrackref:
                            try:
                                # Checkout and fast-forward
                                fork(['git', 'checkout', local_ref, '--'])
                                fork([
                                    'git', 'merge', '--ff-only', self.ref, '--'
                                ])
                                # Final sanity check
                                if log_check_output([
                                        'git', 'rev-parse', self.ref, '--'
                                ]) != log_check_output(
                                    ['git', 'rev-parse', local_ref, '--']):
                                    logger.warning(
                                        "Warning: your local branch is ahead of required remote branch!"
                                    )
                                return
                            except CalledProcessError:
                                logger.warning(
                                    "Couldn't fast-forward local branch, fallback to detached head mode..."
                                )
                    # General case: plain checkout of the origin ref (going in detached HEAD)
                    fork(['git', 'checkout', self.ref, '--'])