def pull(self): if self.git and self.link is None: # git repo, check it exists & isn't dirty if not os.path.exists(self.path): LOG.info("ERROR book {0} from {1} doesn't exist.".format( self.path, self.git)) else: # chdir to the book & run `git status` cwd = os.getcwd() os.chdir(self.path) LOG.info("# book {0}".format(self.path)) git_diff = git.diff() if git_diff: LOG.info("# book {0} had changes:".format(self.path)) LOG.info(git_diff) else: LOG.info("# book {0} is clean".format(self.path)) os.chdir(cwd) elif self.link and self.git is None: # check the link points to the correct location link_target = os.readlink(self.path) LOG.debug('book: {0} should point to {1}, it points to {2}'.format(self.path, self.link, link_target)) if link_target == self.link: LOG.info('# book {0} correctly points to {1}'.format(self.path, self.link)) else: LOG.error('{0} should point to {1}, it points to {2}'.format(self.path, self.link, self.link)) else: LOG.error('Unknown book type: {0}'.format(self.path))
def update(conf, args): '''Apply updates from the upstream repository.''' print('Checking for updates...') # fetch changes from the canonical repo git.fetch(constants.GIT_REMOTE, no_tags=True, quiet=True) # get a list of the commit messages for the incoming changes updates = git('--no-pager', 'log', '..FETCH_HEAD', oneline=True) updates = [tuple(m.split(None, 1)) for m in updates.splitlines()] # print out a list of the incoming updates if len(updates) > 0: print('Available updates:') max_updates = 10 for commit, msg in updates[:max_updates]: print(color.yellow('*'), msg) # print a special message if too many updates are available if len(updates) > max_updates: print('...and', color.green(len(updates) - max_updates), 'more!') print('Run `git log ..FETCH_HEAD` to see the full list') # bail if we have uncommitted changes (git exits non-0 in this case) if git.diff(exit_code=True, quiet=True, _ok_code=(0, 1)).exit_code != 0: raise ValueError('The repository has uncommitted changes. Handle them, ' 'then try updating again.') print('Applying the update...') # stash _all_ changes to the repo git.stash(include_untracked=True, all=True, quiet=True) # squash all the fetched commits together and merge them into master git.merge('@{u}', squash=True, quiet=True) # add a nice update commit that includes the latest upstream commit hash commit_message = 'Update dotparty to %s' % updates[0][0] git.commit(m=commit_message, quiet=True) # TODO: if squash merge failed, roll back to the pre-update state and # complain with instructions for the user to do their own update. # un-stash all our old changes git.stash('pop', quiet=True) # push our changes back up to the remote git.push(quiet=True) print('Update successful!') else: print('Already up-to-date!')
def _git_info(path): from sh import git git = git.bake('--no-pager') rev_parse = getattr(git, 'rev-parse') R = {'branch': rev_parse('--abbrev-ref', 'HEAD', _cwd=path).strip(), 'revision': rev_parse('HEAD', _cwd=path).strip(), 'untracked': getattr(git, 'ls-files')('--others', '--exclude-standard', '--directory', _cwd=path).strip(), 'status': git.status('-s', _cwd=path).strip(), 'diff': git.diff(_cwd=path).strip()} R['clean'] = len(R['diff']) == 0 return R
def diff(self, page, new, old): return git.diff("--no-color", new, old, "--", page.path).stdout.decode("utf8")
def diff(self, page, new, old): return git.diff('--no-color', new, old, '--', page.path).stdout.decode('utf8')
def manage(conf, args): ''' Move a file to the base directory and leave a link pointing to its new location in its place. ''' # bail if the file is already a link if os.path.islink(args.path): raise ValueError('Unable to manage ' + color.cyan(args.path) + " since it's already a link!") # make sure the path is a descendant of the destination directory if not util.is_descendant(args.path, conf['destination']): raise ValueError("Unable to manage files that aren't descendants of " + 'the destination directory (' + color.cyan(conf['destination']) + ')') # mark files that aren't direct descendants of the root as such unrooted = os.path.dirname(args.path) != conf['destination'] # get the path of the file if it will be copied into the repo directory dest_path = os.path.join(constants.REPO_DIR, os.path.basename(args.path)) # rename the file as appropriate to to its original name dest_path, config_file_path = config.configify_file_name(dest_path) # give unrooted files a config file path so they'll go to the correct place if unrooted and config_file_path is None: config_file_path = util.toggle_hidden(dest_path, True) # bail if the file is already managed and we're not overwriting dest_exists = os.path.exists(dest_path) config_exists = (config_file_path is not None and os.path.exists(config_file_path)) if (dest_exists or config_exists) and not args.force: raise ValueError("Can't manage " + color.cyan(args.path) + " since it already appears to be managed (use --force to override)") # create a file config if necessary # replace any existing dest file with a copy of the new one util.rm(dest_path, force=True) util.cp(args.path, dest_path, recursive=True) # replace any existing config file with our new one if config_file_path is not None: util.rm(config_file_path, force=True) # build a config for this file file_config = config.normalize_file_config({ 'paths': [args.path], }, conf['destination']) # create a config file from our config dict with open(config_file_path, 'w') as f: json.dump(file_config, f, indent=2) # create a link to the new location, overwriting the old file util.symlink(args.path, dest_path, overwrite=True) print(color.cyan(args.path), 'copied and linked') # add and commit the file to the repo if --save is specified if args.save: files = [color.cyan(os.path.basename(dest_path))] if config_file_path: files.append(color.cyan(os.path.basename(config_file_path))) files = files.join(' and ') print('Adding', files, 'to the repository...') # move us to the current repo directory so all git commands start there os.chdir(constants.REPO_DIR) # alert the user if we have uncommitted changes (git exits non-0 in this case) if git.diff(exit_code=True, quiet=True, _ok_code=(0, 1)).exit_code != 0: raise ValueError('The repository has uncommitted changes - the ' 'newly-managed file will have to be added to the repo manually.') # add the new files to the staging area git.add(dest_path) if config_file_path is not None: git.add(config_file_path) print('Successfully added', files, 'to the repository') print('Committing changes...') # commit the file to the repository commit_message = 'Manage %s' % os.path.basename(args.path) git.commit(m=commit_message, quiet=True) print('Commit successful!') print('Pushing committed changes...') # pull any changes down from upstream, then push our new addition git.pull(rebase=True, quiet=True) git.push(quiet=True) print('Push successful!')