def pull(remote: git.Remote): """ Performs a Git pull from a given remote repository. :param remote: git.Remote object. :return: """ log.info("Pulling data from remote '{}'...".format(remote)) try: remote.pull() except git.exc.GitCommandError as git_exc: log.exception("Failed to pull from remote '{}'!".format(remote), exc_info=git_exc) sys.stderr.write("Failed to pull from remote '{}', " "check that git host is online, then restart or perform a manual pull!".format(remote))
def checkout_branch(repo, branch_name, no_pull=False): """Checkout a branch and optionally pull updates. :param repo: Repo object :param branch_name: Name of the branch to checkout :param no_pull: (Default: False) If True, don't pull changes to branch :return: Head object for the checked out branch """ base_head = Head(repo, f'refs/heads/{branch_name}') if repo.active_branch != base_head: print(f'Checking out {branch_name}.') base_head.checkout() if not no_pull and base_head.tracking_branch(): print(f'Pulling updates to {branch_name}...') remote_name = base_head.tracking_branch().remote_name remote = Remote(repo, remote_name) base_commit = base_head.commit for fetch_info in remote.pull(): if fetch_info.ref == base_head.tracking_branch(): if fetch_info.commit != base_commit: print( f'Updated {branch_name} to {fetch_info.commit.hexsha}') else: print(f'{branch_name} already up to date.') print('') return base_head
def _pull(self, origin: git.Remote): if self.git_enabled: if self.verbose: print('Pulling dot file repository') origin.pull()
def main(): """Entrypoint to 'gitsync' command-line tool """ parser = argparse.ArgumentParser( description='File-sync integrated with Git system solution tool') parser.add_argument('-d', '--debug', action='store_true', default=False, help='show more message for debugging') parser.add_argument( '--config_file', help= 'Specify the location of the settings (Default value: settings.json)', type=str, default=os.path.join(os.getcwd(), 'settings.json')) parser.add_argument('--init', help='Create a template configuration file', action='store_true', default=False) parser.add_argument('--version', help='Print the GitSync version number', action='version', version=__version__) # Read a set of arguments args = parser.parse_args() DEBUG = args.debug CONFIG_FILE = args.config_file INIT = args.init # Set Logging Level if DEBUG: for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) logging.basicConfig(level=logging.DEBUG, format=FORMAT) logger.debug('Config file: {0}'.format(CONFIG_FILE)) if INIT: logger.info('Generate a template setting.json') with open('settings_default.json', 'w') as f: f.writelines(json.dumps(DEFAULT_SETTING, indent=4)) sys.exit() # Load config try: logger.debug('Load config...') config = load_config(CONFIG_FILE) repo_dir = config['repo_dir'] files_mapping = config['files'] dirs_mapping = config['dirs'] ignore_files = config['ignore']['patterns'] ignore_files.extend(IGNORE_PATTERNS) ignore_files = sorted(list(set(ignore_files))) except Exception as error: logger.error('Can\'t load config: {0}'.format(error)) sys.exit(1) # Read and check repo has been initialized logger.debug('Trying to read repo...') try: repo = Repo(repo_dir) remote = Remote(repo, 'origin') if not remote.exists(): logger.error( 'Can\'t find \'origin\' remote url. Please set a \'origin\' remote and upstream branch at first to proceed!' ) sys.exit(1) logger.debug('Repo has been loaded successfully') logger.info('Pulling from repo...') remote.pull() except InvalidGitRepositoryError as error: logger.error('Invalid repo. Please check it again!') sys.exit(1) except NoSuchPathError as error: logger.error( 'No directory \'.git\' found. Did you initialize git project?!') sys.exit(1) if repo.bare: logger.error('Repo can\'t be a bare!') sys.exit(1) # initialize runtime files/variables init_files(repo_dir, ignore_files) changed = False logger.info('Repo Initialization completed') logger.debug('Performing prechecks...') try: precheck(files_mapping.keys(), dirs_mapping.keys()) except Exception as error: logger.error('Prechecks failed!') logger.error(error) sys.exit(1) logger.debug('Perform cleanup task on repo...') clean_up_repo(files_mapping.values(), dirs_mapping.values(), repo_dir, ignore=ignore_files) logger.debug('Proceed to check file changes') logger.debug('Detect if the sync list changes...') prev_config = NO_LAST_SYNC_STATE if check_last_sync(repo_dir): logger.debug('Last sync record found!') prev_config = load_last_sync(repo_dir) # Check whether folder states are identical logger.info('Check files whether if updated') src_files_to_be_copied, src_dirs_to_be_copied, dst_files_to_be_deleted, dst_dirs_to_be_deleted = check_sync_state( prev_config, config, repo_dir) logger.debug( 'Sync state: \n\t\tFiles be copied {0}\n\t\tDirs be copied {1}\n\t\tFiles be deleted {2}\n\t\tDirs be deleted {3}' .format(src_files_to_be_copied, src_dirs_to_be_copied, dst_files_to_be_deleted, dst_dirs_to_be_deleted)) # Start to perform sync task (overwrite dst-file / delete dst-file / copy entire src-folder(src-file) to dst-folder(dst-file)) change_indicator = 0 if (dst_files_to_be_deleted): for file_path in dst_files_to_be_deleted: try: logger.debug('Deleting file {0}'.format(file_path)) delete_file(file_path) logger.debug(' ... Successfully') except Exception as error: logger.debug(' ... Failed') raise error logger.debug('Files deletion finished') change_indicator += 1 if (dst_dirs_to_be_deleted): for dir_path in dst_dirs_to_be_deleted: try: logger.debug('Deleting directory {0}'.format(dir_path)) delete_dir(dir_path) logger.debug(' ... Successfully') except Exception as error: logger.debug(' ... Failed') raise error logger.debug('Dirs deletion finished') change_indicator += 1 if (src_files_to_be_copied): for src_path, dst_path in src_files_to_be_copied.items(): try: logger.debug('Copying file {0} to {1}'.format( src_path, dst_path)) copy_file(src_path, dst_path) logger.debug(' ... Successfully') except Exception as error: logger.debug(' ... Failed') raise error logger.debug('Files addition finished') change_indicator += 1 if (src_dirs_to_be_copied): for src_path, dst_path in src_dirs_to_be_copied.items(): try: logger.debug('Copying directory {0} to {1}'.format( src_path, dst_path)) copy_dir(src_path, dst_path, ignore=ignore_files) logger.debug(' ... Successfully') except Exception as error: logger.debug(' ... Failed') raise error logger.debug('Dirs addition finished') change_indicator += 1 if change_indicator == 0: logger.info('All is up to date') sys.exit(0) logger.debug('Staging files...') logger.debug('Reset current staging') repo.index.reset() logger.info('Stage modified files into repo...') repo.git.add(A=True) logger.info('Commit to repo...') repo.index.commit('[(auto-git) leave it here for later editing]') logger.info('Push to remote origin server...') remote.push() logger.debug('Saving current sync state...') try: save_current_sync(repo_dir, config) except Exception as error: logger.error('Failed to save current sync state! {0}'.format(error)) sys.exit(1) logger.info('Finished')