示例#1
0
def test_basic():
    shutil.rmtree("/tmp/brigit_test", ignore_errors=True)
    git = Git("/tmp/brigit_test")
    with open("/tmp/brigit_test/file_1", "w") as f:
        f.write('1')
    git.add("/tmp/brigit_test/file_1")
    git.commit(message="Adding file_1")
    assert "Adding file_1" in git.log()
    assert len(list(git.pretty_log())) == 1
    with open("/tmp/brigit_test/file_2", "w") as f:
        f.write('2')
    git.add("/tmp/brigit_test/file_2")
    git.commit(message="Adding file_2")
    assert "Adding file_2" in git.log()
    assert len(list(git.pretty_log())) == 2
    git.reset("HEAD~1")
    assert len(list(git.pretty_log())) == 1
    assert "Untracked files:\n\tfile_2" in git.status()
    git.clean("-fdx")
    git.branch("newbranch")
    with open("/tmp/brigit_test/file_3", "w") as f:
        f.write('3')
    git.add("/tmp/brigit_test/file_3")
    git.commit(message="Adding file_3")
    assert "Adding file_3" in git.log()
    git.checkout("newbranch")
    git.cherryPick("master")
    shutil.rmtree("/tmp/brigit_test")
 def init_git(self, local_git_path, repo_url):
     try:
         self._local_git_path = local_git_path
         self._repo_url = repo_url
         self._repo = Git(self._local_git_path, self._repo_url)
         self._repo.pull()
         return True
     except:
         return False
示例#3
0
def test_basic():
    shutil.rmtree("/tmp/brigit_test", ignore_errors=True)
    git = Git("/tmp/brigit_test")
    with open("/tmp/brigit_test/file_1", "w") as f:
        f.write('1')
    git.add("/tmp/brigit_test/file_1")
    git.commit(message="Adding file_1")
    assert "Adding file_1" in git.log()
    assert len(list(git.pretty_log())) == 1
    with open("/tmp/brigit_test/file_2", "w") as f:
        f.write('2')
    git.add("/tmp/brigit_test/file_2")
    git.commit(message="Adding file_2")
    assert "Adding file_2" in git.log()
    assert len(list(git.pretty_log())) == 2
    git.reset("HEAD~1")
    assert len(list(git.pretty_log())) == 1
    assert "Untracked files:\n\tfile_2" in git.status()
    shutil.rmtree("/tmp/brigit_test")
class GitCheckpoints(CustomCheckpoints):
    """
    A Checkpoints that commits checkpoints for files to a git repo.
    """
    root_dir = Unicode(config=True)

    env = os.environ[
        'DEPLOY_ENV']  #need to pass this through to notebooks in jupyterhub config
    name = os.environ['USER'].upper()

    if 'DEBUG_HOME' in os.environ.keys():
        home = os.environ['DEBUG_HOME']
    else:
        home = os.environ['HOME']

    print('Init Git wrapper in {}'.format(home))
    git = Git(home)

    try:
        user, pw, email, = os.environ['GIT_USER'], os.environ[
            'GIT_PASS'], os.environ['GIT_EMAIL']
        repo_url = os.environ['GIT_URL']
    except KeyError:
        print('GIT Env Variables not set properly. Assuming local Git only')
        traceback.print_exc()
        user, pw, email, repo_url = '', '', '', ''

    N_CHECKPOINTS = 10

    try:
        git.status()
        init = True
        branch = git("rev-parse", '--abbrev-ref', 'HEAD').replace('\n', '')
    except GitException:
        init = False
        #default to USER-ENV for branch name.
        #when used with JupyterHub, assume users are only using branch-names they are meant to
        #TODO: restrict branch naming scheme
        branch = '{}-{}'.format(name, env)

    if not init:
        repo = 'http://{}:{}@{}'.format(user, pw, repo_url)
        git.init()
        if user and pw and repo_url:
            git.remote('add', 'origin', repo)
            print("Git checkpoints connecting to remote repo ...")
        try:
            print(
                "Assume branch already exists, fetch, link branches and checkout"
            )
            git.fetch('origin', branch)
            git.branch(branch, 'origin/{}'.format(branch))
            git.checkout(branch)
        except GitException:
            print("Create branch if not existing in remote")
            git.checkout('-b', branch)

        #Create a .gitignore to ignore hidden folders
        with open(home + '/.gitignore', 'w') as f:
            f.write('.*\n!/.gitignore')

        try:
            git.add('.')
            git.commit('-m', 'Init checkpoints for existing untracked files')
            try:
                git.push('--set-upstream', 'origin', branch)
            except:  #this might fail with no remote
                pass
            git.push()
        except GitException:  #might only have gitignore in the repo
            pass

    #Set git config
    git.config('user.name', name)
    git.config('user.email', email)
    git.config('push.default', 'matching')

    print('GitCheckpoints initialised')

    def _root_dir_default(self):
        try:
            return self.parent.root_dir
        except AttributeError:
            return getcwd()

    # ContentsManager-dependent checkpoint API
    def create_checkpoint(self, contents_mgr, path):
        """Create a checkpoint."""
        path = self.checkpoint_path('', path)
        self.log.debug(
            "Creating checkpoint %s",
            path,
        )

        self.git.add(path)
        try:
            self.git.commit('-m', 'Checkpoint {}'.format(path), path)

        except GitException:  #no changes, perhaps?
            pass

        stats = [i for i in self.git.pretty_log('-1')][0]
        checkpoint_id, datetime = stats['hash'], stats['datetime']

        self.git.push('--set-upstream', 'origin', self.branch)
        self.git.push()

        return self.checkpoint_model(checkpoint_id, datetime)

    def restore_checkpoint(self, contents_mgr, checkpoint_id, path):
        """Restore a checkpoint."""
        path = self.checkpoint_path('', path)

        self.log.debug(
            "Restoring checkpoint %s -> %s",
            checkpoint_id,
            path,
        )

        #Commit any current changes locally
        try:
            self.git.add(path)
            self.git.commit(
                '-m',
                'Committing changes and restoring to {}'.format(checkpoint_id),
                path)
        except GitException:
            pass

        #Restore file to commit hash
        self.git.checkout(checkpoint_id, path)

    # ContentsManager-independent checkpoint API
    def rename_checkpoint(self, checkpoint_id, old_path, new_path):
        """Rename a checkpoint from old_path to new_path."""
        old_cp_path = self.checkpoint_path(checkpoint_id, old_path)
        new_cp_path = self.checkpoint_path(checkpoint_id, new_path)

        self.log.debug(
            "Renaming checkpoint %s -> %s",
            old_cp_path,
            new_cp_path,
        )

        try:
            self.git.add(old_cp_path, new_cp_path)
            self.git.commit(
                '-m', 'Renaming {} -> {}'.format(old_cp_path, new_cp_path),
                old_cp_path, new_cp_path)
        except GitException:  #Fresh notebooks with no save
            self.git.add(new_cp_path)
            self.git.commit(
                '-m', 'Renaming unsaved notebook -> {}'.format(new_cp_path),
                new_cp_path)
        self.git.push()

    def delete_checkpoint(self, checkpoint_id, path):
        """delete a file's checkpoint"""

        #in our Git context, this will just be committing the deleted file
        cp_path = self.checkpoint_path(checkpoint_id, path)

        self.log.debug(
            "Deleting checkpoint %s @ %s",
            cp_path,
            checkpoint_id,
        )

        self.git.add(cp_path)
        self.git.commit('-m', 'Deleting {}'.format(cp_path), cp_path)
        self.git.push()

    def list_checkpoints(self, path):
        """
            list the latest checkpoints for a given file. 
            N_CHECKPOINTS is set globally above
        """
        path = self.checkpoint_path('', path)

        self.log.debug('List checkpoints for {}'.format(path))
        try:
            commit_log = self.git.pretty_log('-{}'.format(self.N_CHECKPOINTS),
                                             '--', path)
            stats = [(i['hash'], i['datetime']) for i in commit_log]
        except:
            traceback.print_exc()
            stats = []

        return [
            self.checkpoint_model(checkpoint_id, datetime)
            for (checkpoint_id, datetime) in stats
        ]

    # Checkpoint-related utilities
    def checkpoint_path(self, checkpoint_id, path):
        """find the path to a checkpoint"""
        return os.path.join(self.home, path.strip('/'))

    def checkpoint_model(self, checkpoint_id, datetime):
        """construct the info dict for a given checkpoint"""
        local = pytz.timezone(PYTZ_TIMEZONE)
        local_dt = local.localize(datetime, is_dst=True)
        last_modified = local_dt.astimezone(pytz.utc)

        info = dict(
            id=checkpoint_id,
            last_modified=last_modified,
        )
        return info

    # Error Handling
    def no_such_checkpoint(self, path, checkpoint_id):
        raise HTTPError(
            404, u'Checkpoint does not exist: %s@%s' % (path, checkpoint_id))