class Repo(object): def __init__(self,path,username,password): self.repo = Repository(path) self.username = username self.password = password self.path=path def commit(self,file,refspec,email,author_username,message): self.repo.index.add(file) index = self.repo.index index.write() tree = self.repo.index.write_tree() author = Signature(author_username,email) self.repo.create_commit(refspec, author, author,message,tree, [self.repo.head.get_object().hex] ) def commit_fallback(self,message): return subprocess.check_output(["git","commit","-m",message],cwd=self.path) def push(self,refspec): credentials = UserPass(self.username,self.password) remoteCall = RemoteCallbacks(credentials) remo = self.repo.remotes["origin"] remo.push(refspec,remoteCall)
def get_new_articles(): blog = PelicanBlog() content_dir = blog.get_content_directory() repo = Repository(os.path.abspath(os.path.dirname(__file__))) diff = repo.revparse_single("HEAD").tree.diff_to_tree() existing_articles = set(os.path.relpath(obj.old_file_path, content_dir) for obj in diff if obj.old_file_path.startswith(content_dir)) all_articles = set(blog.get_posts()) new_articles = {art for art in all_articles - existing_articles if blog.get_post_lang(art) in (TWITTER_LANGUAGE, "")} new_titles = [] repo.index.read() for newart in new_articles: title = blog.get_post_title(newart) yield Article(title, blog.get_post_url(newart), blog.get_post_authors(newart)) new_titles.append(title) repo.index.add(os.path.join(content_dir, newart)) blogger = Signature(repo.config["user.name"], repo.config["user.email"]) repo.create_commit("HEAD", blogger, blogger, "[BLOG] %s" % ", ".join(new_titles), repo.index.write_tree(), [repo.head.peel().oid]) repo.index.write() # TODO(v.markovtsev): implement git push using pygit2 subprocess.call(("git", "push", "origin", repo.head.shorthand))
class GitRepositoryTestCase(unittest.TestCase): def setUp(self): self.repo_path = mkdtemp() init_repository(self.repo_path, False) self.repo = Repository(self.repo_path) def tearDown(self): try: rmtree(self.repo_path) except: pass def test_create_repo(self): repo_path = mkdtemp() try: GitRepository.create_repo(repo_path) for f in [os.path.join('content', 'attachments', 'mercurial.png'), os.path.join('content', 'post', 'example-post.rst'), os.path.join('content', 'post', 'lorem-ipsum.rst'), os.path.join('content', 'about.rst'), os.path.join('static', 'screen.css'), os.path.join('templates', 'base.html'), os.path.join('templates', 'posts.html'), os.path.join('templates', 'post_list.html'), 'config.yaml', '.gitignore', '.git']: self.assertTrue(os.path.exists(os.path.join(repo_path, f)), 'Not found: %s' % f) finally: rmtree(repo_path) def test_get_changectx_rev_default(self): git_repo = GitRepository(self.repo_path) with codecs.open(os.path.join(self.repo_path, 'foo.rst'), 'w', encoding='utf-8') as fp: fp.write('foo') sign = Signature('foo', '*****@*****.**') tree = self.repo.TreeBuilder().write() self.repo.index.add('foo.rst') self.repo.create_commit('refs/heads/master', sign, sign, 'foo', tree, []) self.assertTrue(isinstance(git_repo.get_changectx(REVISION_DEFAULT), ChangeCtxDefault), 'changectx object is not an instance of ' 'ChangeCtxDefault') def test_get_changectx_rev_working_dir(self): git_repo = GitRepository(self.repo_path) with codecs.open(os.path.join(self.repo_path, 'foo.rst'), 'w', encoding='utf-8') as fp: fp.write('foo') sign = Signature('foo', '*****@*****.**') tree = self.repo.TreeBuilder().write() self.repo.index.add('foo.rst') self.repo.create_commit('refs/heads/master', sign, sign, 'foo', tree, []) self.assertTrue( isinstance(git_repo.get_changectx(REVISION_WORKING_DIR), ChangeCtxWorkingDir), 'changectx object is not an instance of ChangeCtxWorkingDir')
def commit_new_tickets_to_git(queue, repo_path): global last_push_ts #repo repo = Repository(os.path.join(repo_path, '.git')) index = repo.index author = Signature('yourname', 'youremail') while True: #write tickets into file ticket = queue.get(block=True) #filename format is yyyy-mm-dd d = datetime.utcnow().date() filename = '%s.txt' % d.strftime('%Y-%m-%d') f = open(os.path.join(repo_path, filename), 'ab') f.write('%s\n' % ticket.toJson()) f.close() #commit index.add(filename) index.write() oid = index.write_tree() repo.create_commit('HEAD', author, author, ticket.toJson(), oid, [repo.head.oid]) #push d_ts = datetime.utcnow() if last_push_ts is None or d_ts > (last_push_ts + timedelta(seconds = 60)): push_to_github(repo_path) last_push_ts = datetime.utcnow()
def createcommit(self): """Prepare a git repository with one existing commit. Create a directory, initialize a git Repository, add and commit a file. Returns: A list containing the directory and file """ self.addfile() # Create commit repo = Repository(self.dir.name) index = repo.index index.read() tree = index.write_tree() message = "First commit of temporary test repo" repo.create_commit('HEAD', self.author, self.comitter, message, tree, [])
def merge(dir, reference, msg): repo = Repository(dir) reference = "refs/heads/" + reference print reference other_branch_ref = repo.lookup_reference(reference) other_branch_tip = other_branch_ref.target # repo.merge(other_branch_tip) print('merge complete!') user = repo.default_signature tree = repo.index.write_tree() message = msg new_commit = repo.create_commit('HEAD', user, user, message, tree, [repo.head.target, other_branch_tip]) print(new_commit)
def shift(amount, repo_path): repo = Repository(repo_path) head = repo.lookup_reference('HEAD').resolve() adder = partial(add, amount=amount) changelog = dict() reference = REF_FMT.format(time=time(), pid=getpid()) for commit in repo.walk(head.oid, GIT_SORT_REVERSE | GIT_SORT_TOPOLOGICAL): newmsg, nsubs = ISSUE_RE.subn(adder, commit.message) if nsubs != 0 or any(pnt.oid in changelog for pnt in commit.parents): parents = [changelog.get(c.oid, c.oid) for c in commit.parents] new_oid = repo.create_commit(reference, commit.author, commit.committer, newmsg, commit.tree.oid, parents) changelog[commit.oid] = new_oid return changelog, reference
def commit( repo: pygit2.Repository, message: str, pathspecs: typing.Optional[typing.List[typing.Union[str, pathlib.Path]]] = None, ) -> None: if pathspecs is None: pathspecs = [] pathspecs = [ ( str( pathspec.relative_to(repo.workdir) if pathspec.is_absolute() else pathspec ) if isinstance(pathspec, pathlib.Path) else pathspec ) for pathspec in pathspecs ] repo.index.add_all(pathspecs=pathspecs) repo.index.write() tree = repo.index.write_tree() try: parent, ref = repo.resolve_refish(refish=repo.head.name) except pygit2.GitError: parents = [] ref_name = 'refs/heads/master' else: parents = [parent.oid] ref_name = ref.name repo.create_commit( ref_name, repo.default_signature, repo.default_signature, message, tree, parents )
def commitAll(repo: Repository, message: Optional[str] = None) -> None: """ Creates commit with all changes in the working tree. There is a lack of eny error handling. Arguments: repo {Repository} -- repository object Keyword Arguments: message {Optional[str]} -- in None passed the message will be autogenerated: `Updated: datetime.now().strftime("%Y-%m-%d %H:%M:%S")` (default: {None}) Returns: None -- No result returned. """ if message is None: message = "Updated: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S") # checking if this is the root commit. Parent commit should be HEAD otherwise. if repo.head_is_unborn: parent = [] else: parent = [repo.head.target] # todo: investigate this # Make sure it was written to disk before moving on. # test_fp.flush() # os.fsync(test_fp.fileno()) # test_fp.close() repo.index.add_all() user = repo.default_signature tree = repo.index.write_tree() repo.create_commit('HEAD', user, user, message, tree, parent) # Apparently the index needs to be written after a write tree to clean it up. # https://github.com/libgit2/pygit2/issues/370 repo.index.write()
def commit_to_working_copy_tag(repo: pygit2.Repository) -> pygit2.Oid: repo.index.read() repo.index.add_all( ) # TODO: add a binding for update_all to pygit2 and use it. tree = repo.index.write_tree() signature = repo.default_signature message = 'whats up with me' commit_oid = repo.create_commit(None, signature, signature, message, tree, [repo.head.get_object().hex]) try: tag = get_working_copy_tag(repo) tag.set_target(commit_oid) except KeyError: repo.create_tag(WORKING_COPY_TAG_NAME, commit_oid, pygit2.GIT_OBJ_COMMIT, signature, message) return commit_oid
def copy_template(repo: Repository, name: str, commit_message: str, parents: List[str] = None) -> str: template_path = os.path.join(os.path.dirname(__file__), 'data', name) # Use distuils implementation instead of shutil to allow for copying into # a destination with existing files. See: https://stackoverflow.com/a/31039095/724251 copy_tree(template_path, repo.workdir) # Stage the template changes repo.index.add_all() repo.index.write() tree = repo.index.write_tree() # Construct a commit with the staged changes. return repo.create_commit(None, repo.default_signature, repo.default_signature, commit_message, tree, parents or [])
def advance_master( repo: git.Repository, parents: List[git.Oid], tree: git.Oid, when: Optional[int] = None, msg: Optional[str] = None, ) -> git.Oid: if when is None: when = int(time()) if msg is None: msg = "" return repo.create_commit( "refs/heads/master", git.Signature(name="untrustix", email="*****@*****.**", time=when), git.Signature(name="untrustix", email="*****@*****.**", time=when), msg, tree, parents, )
class vgit_repo(metaclass=singleton): def __init__(self): self.repo = None def vgit_load_repo(self, path): if path is None: self.repo = None return try: self.repo = Repository(path) except ValueError: self.repo = None except Exception: self.repo = None def vgit_clone(self, path, url, log_cb): """Wraper method to clone a repository from a given `url' to a given `path'""" try: pygit2.clone_repository(url, path) except ValueError as err: log_cb(str(err)) return False except Exception as err: log_cb(str(err)) return False return True def vgit_init(self, path, log_cb, bare=False): """Wraper method to init a repository in given `path'""" try: pygit2.init_repository(path, bare) except ValueError as err: log_cb(str(err)) return False except Exception as err: log_cb(str(err)) return False return True def vgit_add_all(self, log_cb): """Wraper method to add all files to a commit.""" try: if self.repo is None: log_cb( "No .git in current directory. Use `init' to crete a new repository." ) return False self.repo.index.add_all() self.repo.index.write() except ValueError as err: log_cb(str(err)) return False except Exception as err: log_cb(str(err)) return False return True def vgit_commit(self, user_name, email, branch, message, log_cb): """Wraper method for the commit -m "message" git command. `user_name' and `email' informations of the autor of the commit `user_name_author' and email_author: is the same idea of the commiter but for the author `branch' branch which the user want's to do the commit `message': message of the commit""" try: if self.repo is None: log_cb( "No .git in current directory. Use `init' to crete a new repository." ) return False reference = 'refs/heads/' + branch author = pygit2.Signature(user_name, email) # usando author e commiter como o mesmo ser self.repo.create_commit(reference, author, author, message, self.repo.TreeBuilder().write(), [self.repo.head.target]) except ValueError as err: log_cb(str(err)) return False except Exception as err: log_cb(str(err)) return False return True def vgit_commits(self, log_cb): if self.repo is None: log_cb("No commits...") else: for commit in self.repo.walk(self.repo[self.repo.head.target].id, pygit2.GIT_SORT_TIME): log_cb('\n'.join([ 'Commit: #{}'.format(commit.tree_id.hex), 'Author: {} <{}>'.format(commit.author.name, commit.author.email), 'Message: ', commit.message, '' ])) def vgit_push(self, path, message, user_name_commiter, user_name_author, email_commiter, email_author, branch, user_name_pusher, user_passoword_pusher, log_cb): try: repo = Repository(path) index = repo.index reference = 'refs/heads/' + branch tree = index.write_tree() author = pygit2.Signature(user_name_author, email_author) commiter = pygit2.Signature(user_name_commiter, email_commiter) oid = repo.create_commit(reference, author, commiter, message, tree, [repo.head.target]) credentials = pygit2.UserPass(user_name_pusher, user_passoword_pusher) remo = repo.remotes["origin"] remo.credentials = credentials aux = remo.url repo.remotes.set_push_url(user_name_pusher, aux) callbacks = pygit2.RemoteCallbacks(credentials=credentials) remo.push([reference], callbacks=callbacks) except ValueError as err: log_cb(str(err)) return False except Exception as err: log_cb(str(err)) return False return True
class GitStorage(Storage): """ Git file storage backend. """ def __init__(self, path): """ Initialize repository. :param path: Absolute path to the existing Git repository. :type path: str """ super(GitStorage, self).__init__() self.repo = Repository(path) self.index = self.repo.index self.index.read() @classmethod def create_storage(cls, path): """ Create repository, and return GitStorage object on it :param path: Absolute path to the Git repository to create. :type path: str :returns: GitStorage """ init_repository(path, False) return cls(path) def commit(self, user, message): """ Save previous changes in a new commit. :param user: The commit author/committer. :type user: django.contrib.auth.models.User :param message: The commit message. :type message: unicode :returns: pygit2.Commit """ # Refresh index before committing index = self.repo.index index.read() # Check the status of the repository status = self.repo.status() for filename, flags in status.items(): # the file was deleted if flags in (GIT_STATUS_INDEX_DELETED, GIT_STATUS_WT_DELETED): # remove it from the tree del index[filename] # or the file was modified/added elif flags in (GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_INDEX_NEW, GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_NEW): # add it to the tree index.add(filename) treeid = index.write_tree() # Now make the commit author = Signature(u'{0} {1}'.format( user.first_name, user.last_name).encode('utf-8'), user.email.encode('utf-8') ) committer = author try: parents = [self.repo.head.oid] except GitError: parents = [] commit = self.repo.create_commit( 'refs/heads/master', author, committer, message, treeid, parents ) # Write changes to disk index.write() # and refresh index. self.index.read() # Return commit object return self.repo[commit] def log(self, name=None, limit=10): """ Get history of the repository, or of a file if name is not None. :param name: File name within the repository. :type name: unicode or None :param limit: Maximal number of commits to get (default: 10), use a negative number to get all. :type limit: int :returns: list of pygit2.Commit """ commits = [] if not name: # Look for `limit` commits for commit in self.repo.walk(self.repo.head.oid, GIT_SORT_TIME): commits.append(commit) limit = limit - 1 if limit == 0: break else: # For each commits for commit in self.repo.walk(self.repo.head.oid, GIT_SORT_TIME): # Check the presence of the file in the tree if commit.parents: # If the commit has parents, check if the file is present # in the diff diff = commit.tree.diff(commit.parents[0].tree) for patch in diff: # If the filename is the patch's filename... if name.encode('utf-8') == patch.new_file_path: # ... then we can add the commit to the list # and leave the loop commits.append(commit) limit = limit - 1 break else: # But if the commit has no parents (root commit) # Simply check in its tree try: commit.tree[name] # no error raised, it means the entry exists, so add the # commit to the list commits.append(commit) limit = limit - 1 # If the file is not in the tree, then it raises a KeyError, # so, just ignore it. except KeyError: pass # If the limit is reached, leave the loop if limit == 0: break return commits def diffs(self, name=None, limit=10): """ Get diffs between commits. Return the following dict : {"diffs": [ { "msg": unicode(<commit message>), "date": datetime.fromtimestamp(<commit date>), "author": unicode(<author name>), "sha": unicode(<commit SHA>), "parent_sha": unicode(<parent commit SHA>), # optional }, # ... ]} :param name: File name within the repository. :type name: unicode or None :param limit: Maximal number of diffs to get (default: 10), use a negative number to get all. :type limit: int :returns: dict """ commits = self.log(name=name, limit=limit) diffs = {'diffs': []} # For each commit for commit in commits: # Create a JSON object containing informations about the commit diff = { 'msg': commit.message, 'date': datetime.datetime.fromtimestamp(commit.commit_time), 'author': commit.author.name, 'sha': commit.hex, } if commit.parents: diff['parent_sha'] = commit.parents[0].hex # The SHA and parent SHA will be used to get the diff via AJAX. diffs['diffs'].append(diff) return diffs def diff(self, asha, bsha, name=None): """ Get diff between two commits. :param asha: SHA of commit A. :type asha: unicode :param bsha: SHA of commit B. :type bsha: unicode :param name: File name within the repository. :type name: unicode or None :returns: unicode """ c1 = self.repo[asha] c2 = self.repo[bsha] d = c1.tree.diff(c2.tree) if name: diff = u'' # For each patch in the diff for patch in d: # Check if the patch is our file if name.encode('utf-8') == patch.new_file_path: # Format the patch for hunk in patch.hunks: p = u'\n'.join(hunk.lines) # And add the diff to the final diff diff = u'{0}{1}'.format(diff, p) return diff # For a global diff, just return the full patch else: return d.patch def search(self, pattern, exclude=None): """ Search pattern in GIT repository. :param pattern: Pattern to search. :type pattern: unicode :param exclude: Exclude some files from the search results :type exclude: regex :returns: list of tuple containing the filename and the list of matched lines. """ entries = [] self.index.read() # For each files in the index for ientry in self.index: # If the filename match the exclude_file regex, then ignore it if exclude and re.match(exclude, ientry.path.decode('utf-8')): continue # Get the associated blob blob = self.repo[ientry.oid] # Create entry entry = (ientry.path.decode('utf-8'), []) # Add matched lines to the entry for line in blob.data.decode('utf-8').splitlines(): if pattern in line: entry[1].append(line) # If the entry has no matched lines, then ignore if entry[1]: entries.append(entry) return entries def is_dir(self, name): """ Check if name refers to a directory. :param name: File name within the repository. :type name: unicode :returns: True, False """ # Check if the path exists, if not returns default value. if not self.exists(name): return False # Get the TreeEntry associated to name tentry = self.repo.head.tree[name] # Convert it to its pygit2 representation obj = tentry.to_object() # If it's a Tree, then we can return True if isinstance(obj, Tree): return True # The instance is a Blob, so it's a file, return False else: return False def mimetype(self, name): """ Get the mimetype of a file. :param name: File name within the repository. :type name: unicode :returns: str """ # If the file is a directory if self.is_dir(name): return 'inode/directory' # Or doesn't exist elif not self.exists(name): return 'unknown' # The file exists, check its mimetype else: import urllib import mimetypes url = urllib.pathname2url(name.encode('utf-8')) return mimetypes.guess_type(url)[0] or 'unknown' def walk(self): """ Walk through the repository. """ self.index.read() for entry in self.index: yield entry # Storage API def accessed_time(self, name): """ Get last accessed time of a file. :param name: File name within the repository. :type name: unicode :returns: datetime :raises: IOError """ if not self.exists(name): raise IOError(u"{0}: Not found in repository".format(name)) abspath = os.path.join(self.repo.workdir, name) stats = os.stat(abspath) return datetime.datetime.fromtimestamp(stats.st_atime) def created_time(self, name): """ Get creation time of a file. :param name: File name within the repository. :type name: unicode :returns: datetime :raises: IOError """ if not self.exists(name): raise IOError(u"{0}: Not found in repository".format(name)) abspath = os.path.join(self.repo.workdir, name) stats = os.stat(abspath) return datetime.datetime.fromtimestamp(stats.st_ctime) def modified_time(self, name): """ Get last modified time of a file. :param name: File name within the repository. :type name: unicode :returns: datetime :raises: IOError """ if not self.exists(name): raise IOError(u"{0}: Not found in repository".format(name)) abspath = os.path.join(self.repo.workdir, name) stats = os.stat(abspath) return datetime.datetime.fromtimestamp(stats.st_mtime) def size(self, name): """ Get file's size. :param name: File name within the repository. :type name: unicode :returns: int :raises: IOError """ if not self.exists(name): raise IOError(u"{0}: Not found in repository".format(name)) e = self.index[name] blob = self.repo[e.oid] return blob.size def exists(self, path): """ Check if ``path`` exists in the Git repository. :param path: Path within the repository of the file to check. :type param: unicode :returns: True if the file exists, False if the name is available for a new file. """ # If the head is orphaned (does not point to any commit), returns False # because there is nothing in the repository. if self.repo.head_is_orphaned: return False # Try getting the path via the tree try: entry = self.repo.head.tree[path] return True # If it raises a KeyError, then the path doesn't exist except KeyError: return False def listdir(self, path=None): """ Lists the contents of the specified path. :param path: Path of the directory to list (or None to list the root). :type path: unicode or None :returns: a 2-tuple of lists; the first item being directories, the second item being files. """ abspath = os.path.join(self.repo.workdir, path) if path else self.repo.workdir dirs = [] files = [] for e in os.listdir(abspath): entry_fullpath = os.path.join(abspath, e) if os.path.isdir(entry_fullpath): if e != '.git': dirs.append(e.decode('utf-8')) else: files.append(e.decode('utf-8')) return (dirs, files) def open(self, name, mode='rb'): """ Opens the file given by name. :param name: Name of the file to open. :type name: unicode :param mode: Flags for openning the file (see builtin ``open`` function). :type mode: str :returns: GitFile """ abspath = os.path.join(self.repo.workdir, name) dirname = os.path.dirname(abspath) if 'w' in mode and not os.path.exists(dirname): os.makedirs(dirname) return GitFile(open(abspath, mode)) def path(self, name): """ Return the absolute path of the file ``name`` within the repository. :param name: Name of the file within the repository. :type name: unicode :returns: str :raises: IOError """ if not self.exists(name): raise IOError(u"{0}: Not found in repository".format(name)) e = self.index[name] return os.path.join(self.repo.workdir, e.path).decode('utf-8') def save(self, name, content): """ Saves a new file using the storage system, preferably with the name specified. If there already exists a file with this name, the storage system may modify the filename as necessary to get a unique name. The actual name of the stored file will be returned. :param name: Name of the new file within the repository. :type name: unicode :param content: Content to save. :type content: django.core.files.File :returns: str """ new_name = self.get_available_name(name) abspath = os.path.join(self.repo.workdir, new_name) dirname = os.path.dirname(abspath) if not os.path.exists(dirname): os.makedirs(dirname) with open(abspath, 'wb') as f: for chunk in content.chunks(): f.write(chunk) def delete(self, name): """ Deletes the file referenced by name. :param name: Name of the file within the repository to delete :type name: unicode :raises: IOError """ if not self.exists(name): raise IOError(u"{0}: Not found in repository".format(name)) abspath = os.path.join(self.repo.workdir, name) os.remove(abspath)
class PyGitEngine(GitContentDatabaseEngine): def __init__(self, config): super(PyGitEngine, self).__init__(config) self.repo = None def connect(self): """Create content directory""" if not isdir(self.content_path): init_repository(self.content_path, bare=True) self.repo = Repository(self.content_path) self.create_initial_commit() else: self.repo = Repository(self.content_path) @staticmethod def do_put(content_path, object_hashes, content, filename): """Perform put operation. This is used in the distributed wrapper""" content_hash = Repository(content_path).create_blob(content) result = object_hashes[filename] = str(content_hash) return result def put_attr(self, content, filename): """Return attributes for the do_put operation""" filename = self._inc_name(filename) return (self.content_path, self.object_hashes, content, filename) def put(self, content, filename="generic"): # pylint: disable=method-hidden """Put content in the content database""" return self.do_put(*self.put_attr(content, filename)) def get(self, content_hash): # pylint: disable=method-hidden """Get content from the content database""" return_data = self.repo[content_hash].data return return_data def find_subhash(self, content_hash): """Find hash in git""" try: blob = self.repo.revparse_single(content_hash) return str(blob.id) except KeyError: return None def create_initial_commit(self): """Create the initial commit of the git repository""" empty_tree = self.repo.TreeBuilder().write() self.create_commit_object(self._initial_message, empty_tree) def create_commit_object(self, message, tree): """Create a commit object""" references = list(self.repo.references) master_ref = self.repo.lookup_reference( self._commit_ref) if len(references) > 0 else None parents = [] if master_ref is not None: parents = [master_ref.peel().id] author = Signature(self._commit_name, self._commit_email) return self.repo.create_commit(self._commit_ref, author, author, message, tree, parents) def new_tree(self, parent): """Create new git tree""" return self.repo.TreeBuilder() def insert_blob(self, tree, basename, value): """Insert blob into tree""" tree.insert(basename, value, GIT_FILEMODE_BLOB) def insert_tree(self, tree, basename, value): """Insert tree into tree""" tree.insert(basename, value, GIT_FILEMODE_TREE) def write_tree(self, tree): """Write tree to git directory""" return tree.write()
pass_path = credentials_mapping[glob]["target"] if not args.wip_force and not is_idle_enough("xprintidle-ng"): sys.exit(0) diff_size = get_diff_size(repo) if diff_size == 0: log_info("no changes to commit") sys.exit(0) if diff_size > lines_changed or args.wip_force: branch = f"{repo.references['HEAD'].resolve().split('/')[-1]}: " if args.wip_add_branch_name else "" wip_message = f"{branch}WIP {datetime.now().strftime('%a %d/%m/%Y %H:%M:%S')}" index = repo.index index.read() index.add_all() index.write() # user = repo.default_signature name = list(config.get_multivar('user.name'))[0] email = list(config.get_multivar('user.email'))[0] author = committer = Signature(name, email) parents = [repo.references['HEAD'].resolve().target] tree = index.write_tree() wip_commit = repo.create_commit("HEAD", author, committer, wip_message, tree, parents) if args.wip_push: if is_main_branch_active(repo) and is_main_branch_protected(): log_info("main branch is untouchable") sys.exit(1) else: remote = resolve_remote(repo, args.remote) remote.push(specs=["HEAD"], callbacks=build_auth_callbacks(repo, pass_path))
class GitRepo: """A class that manages a git repository. This class enables versiong via git for a repository. You can stage and commit files and checkout different commits of the repository. """ path = '' pathspec = [] repo = None callback = None author_name = 'QuitStore' author_email = '*****@*****.**' gcProcess = None def __init__(self, path, origin=None, gc=False): """Initialize a new repository from an existing directory. Args: path: A string containing the path to the repository. origin: The remote URL where to clone and fetch from and push to """ logger = logging.getLogger('quit.core.GitRepo') logger.debug('GitRepo, init, Create an instance of GitStore') self.path = path self.gc = gc if not exists(path): try: makedirs(path) except OSError as e: raise Exception('Can\'t create path in filesystem:', path, e) try: self.repo = Repository(path) except KeyError: pass except AttributeError: pass if origin: self.callback = QuitRemoteCallbacks() if self.repo: if self.repo.is_bare: raise QuitGitRepoError('Bare repositories not supported, yet') if origin: # set remote self.addRemote('origin', origin) else: if origin: # clone self.repo = self.cloneRepository(origin, path, self.callback) else: self.repo = init_repository(path=path, bare=False) def cloneRepository(self, origin, path, callback): try: repo = clone_repository(url=origin, path=path, bare=False, callbacks=callback) return repo except Exception as e: raise QuitGitRepoError( "Could not clone from: {} origin. {}".format(origin, e)) def addall(self): """Add all (newly created|changed) files to index.""" self.repo.index.read() self.repo.index.add_all(self.pathspec) self.repo.index.write() def addfile(self, filename): """Add a file to the index. Args: filename: A string containing the path to the file. """ index = self.repo.index index.read() try: index.add(filename) index.write() except Exception as e: logger.info( "GitRepo, addfile, Could not add file {}.".format(filename)) logger.debug(e) def addRemote(self, name, url): """Add a remote. Args: name: A string containing the name of the remote. url: A string containing the url to the remote. """ try: self.repo.remotes.create(name, url) logger.info("Successfully added remote: {} - {}".format(name, url)) except Exception as e: logger.info("Could not add remote: {} - {}".format(name, url)) logger.debug(e) try: self.repo.remotes.set_push_url(name, url) self.repo.remotes.set_url(name, url) except Exception as e: logger.info("Could not set push/fetch urls: {} - {}".format( name, url)) logger.debug(e) def checkout(self, commitid): """Checkout a commit by a commit id. Args: commitid: A string cotaining a commitid. """ try: commit = self.repo.revparse_single(commitid) self.repo.set_head(commit.oid) self.repo.reset(commit.oid, GIT_RESET_HARD) logger.info("Checked out commit: {}".format(commitid)) except Exception as e: logger.info("Could not check out commit: {}".format(commitid)) logger.debug(e) def commit(self, message=None): """Commit staged files. Args: message: A string for the commit message. Raises: Exception: If no files in staging area. """ if self.isstagingareaclean(): # nothing to commit return index = self.repo.index index.read() tree = index.write_tree() try: author = Signature(self.author_name, self.author_email) comitter = Signature(self.author_name, self.author_email) if len(self.repo.listall_reference_objects()) == 0: # Initial Commit if message is None: message = 'Initial Commit from QuitStore' self.repo.create_commit('HEAD', author, comitter, message, tree, []) else: if message is None: message = 'New Commit from QuitStore' self.repo.create_commit('HEAD', author, comitter, message, tree, [self.repo.head.get_object().hex]) logger.info('Updates commited') except Exception as e: logger.info('Nothing to commit') logger.debug(e) if self.gc: self.garbagecollection() def commitexists(self, commitid): """Check if a commit id is part of the repository history. Args: commitid: String of a Git commit id. Returns: True, if commitid is part of commit log False, else. """ if commitid in self.getids(): return True else: return False def garbagecollection(self): """Start garbage collection. Args: commitid: A string cotaining a commitid. """ try: # Check if the garbage collection process is still running if self.gcProcess is None or self.gcProcess.poll() is not None: # Start garbage collection with "--auto" option, # which imidietly terminates, if it is not necessary self.gcProcess = Popen(["git", "gc", "--auto", "--quiet"], cwd=self.path) logger.debug('Spawn garbage collection') except Exception as e: logger.debug('Git garbage collection failed to spawn') logger.debug(e) def getpath(self): """Return the path of the git repository. Returns: A string containing the path to the directory of git repo """ return self.path def getcommits(self): """Return meta data about exitsting commits. Returns: A list containing dictionaries with commit meta data """ commits = [] if len(self.repo.listall_reference_objects()) > 0: for commit in self.repo.walk(self.repo.head.target, GIT_SORT_REVERSE): commits.append({ 'id': str(commit.oid), 'message': str(commit.message), 'commit_date': datetime.fromtimestamp( commit.commit_time).strftime('%Y-%m-%dT%H:%M:%SZ'), 'author_name': commit.author.name, 'author_email': commit.author.email, 'parents': [c.hex for c in commit.parents], }) return commits def getids(self): """Return meta data about exitsting commits. Returns: A list containing dictionaries with commit meta data """ ids = [] if len(self.repo.listall_reference_objects()) > 0: for commit in self.repo.walk(self.repo.head.target, GIT_SORT_REVERSE): ids.append(str(commit.oid)) return ids def isgarbagecollectionon(self): """Return if gc is activated or not. Returns: True, if activated False, if not """ return self.gc def isstagingareaclean(self): """Check if staging area is clean. Returns: True, if staginarea is clean False, else. """ status = self.repo.status() for filepath, flags in status.items(): if flags != GIT_STATUS_CURRENT: return False return True def pull(self, remote='origin', branch='master'): """Pull if possible. Return: True: If successful. False: If merge not possible or no updates from remote. """ try: self.repo.remotes[remote].fetch() except Exception as e: logger.info("Can not pull: Remote {} not found.".format(remote)) logger.debug(e) ref = 'refs/remotes/' + remote + '/' + branch remoteid = self.repo.lookup_reference(ref).target analysis, _ = self.repo.merge_analysis(remoteid) if analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE: # Already up-to-date pass elif analysis & GIT_MERGE_ANALYSIS_FASTFORWARD: # fastforward self.repo.checkout_tree(self.repo.get(remoteid)) master_ref = self.repo.lookup_reference('refs/heads/master') master_ref.set_target(remoteid) self.repo.head.set_target(remoteid) elif analysis & GIT_MERGE_ANALYSIS_NORMAL: self.repo.merge(remoteid) tree = self.repo.index.write_tree() msg = 'Merge from ' + remote + ' ' + branch author = Signature(self.author_name, self.author_email) comitter = Signature(self.author_name, self.author_email) self.repo.create_commit('HEAD', author, comitter, msg, tree, [self.repo.head.target, remoteid]) self.repo.state_cleanup() else: logger.debug('Can not pull. Unknown merge analysis result') def push(self, remote='origin', branch='master'): """Push if possible. Return: True: If successful. False: If diverged or nothing to push. """ ref = ['refs/heads/' + branch] try: remo = self.repo.remotes[remote] except Exception as e: logger.info( "Can not push. Remote: {} does not exist.".format(remote)) logger.debug(e) return try: remo.push(ref, callbacks=self.callback) except Exception as e: logger.info("Can not push to {} with ref {}".format( remote, str(ref))) logger.debug(e) def getRemotes(self): remotes = {} try: for remote in self.repo.remotes: remotes[remote.name] = [remote.url, remote.push_url] except Exception as e: logger.info('No remotes found.') logger.debug(e) return {} return remotes
class GitBareBackend(object): nb_transactions = 0 def __init__(self, path): self.path = abspath(path) + '/' # Open database self.path_data = '%s/database/' % self.path if not lfs.is_folder(self.path_data): error = '"%s" should be a folder, but it is not' % path raise ValueError, error # Open repository self.repo = Repository(self.path_data) # Read index try: tree = self.repo.head.peel(GIT_OBJ_TREE) self.repo.index.read_tree(tree.id) except: pass # Check git commiter try: _, _ = self.username, self.useremail except: print '=========================================' print 'ERROR: Please configure GIT commiter via' print ' $ git config --global user.name' print ' $ git config --global user.email' print '=========================================' raise @classmethod def init_backend(cls, path, init=False, soft=False): init_repository('{0}/database'.format(path), bare=True) ####################################################################### # Internal utility functions ####################################################################### def _call(self, command): """Interface to cal git.git for functions not yet implemented using libgit2. """ popen = Popen(command, stdout=PIPE, stderr=PIPE, cwd=self.path_data) stdoutdata, stderrdata = popen.communicate() if popen.returncode != 0: raise EnvironmentError, (popen.returncode, stderrdata) return stdoutdata @lazy def username(self): cmd = ['git', 'config', '--get', 'user.name'] try: username = self._call(cmd).rstrip() except EnvironmentError: raise ValueError( "Please configure 'git config --global user.name'") return username @lazy def useremail(self): cmd = ['git', 'config', '--get', 'user.email'] try: useremail = self._call(cmd).rstrip() except EnvironmentError: raise ValueError( "Please configure 'git config --global user.email'") return useremail def _resolve_reference(self, reference): """This method returns the SHA the given reference points to. For now only HEAD is supported. FIXME This is quick & dirty. TODO Implement references in pygit2 and use them here. """ # Case 1: SHA if len(reference) == 40: return reference # Case 2: reference reference = self.repo.lookup_reference(reference) try: reference = reference.resolve() except KeyError: return None return reference.target def normalize_key(self, path, __root=None): # Performance is critical so assume the path is already relative to # the repository. key = __root.resolve(path) if key and key[0] == '.git': err = "bad '{0}' path, access to the '.git' folder is denied" raise ValueError(err.format(path)) return '/'.join(key) def handler_exists(self, key): tree = self.repo.head.peel(GIT_OBJ_TREE) try: tree[key] except: return False return True def get_handler_names(self, key): try: tree = self.repo.head.peel(GIT_OBJ_TREE) if key: tree_entry = tree[key] if tree_entry.type == 'blob': raise ValueError tree = self.repo[tree_entry.id] except: yield None else: for item in tree: yield item.name def get_handler_data(self, key): tree = self.repo.head.peel(GIT_OBJ_TREE) tree_entry = tree[key] blob = self.repo[tree_entry.id] return blob.data def get_handler_mimetype(self, key): data = self.get_handler_data(key) return magic_from_buffer(data) def handler_is_file(self, key): return not self.handler_is_folder(key) def handler_is_folder(self, key): repository = self.repo if key == '': return True else: tree = repository.head.peel(GIT_OBJ_TREE) tree_entry = tree[key] return tree_entry.type == 'tree' def get_handler_mtime(self, key): # FIXME return datetime.utcnow().replace(tzinfo=fixed_offset(0)) def traverse_resources(self): tree = self.repo.head.peel(GIT_OBJ_TREE) yield self.get_resource('/') for name in self.get_names(tree): if name[-9:] == '.metadata' and name != '.metadata': yield self.get_resource('/' + name[:-9]) def get_names(self, tree, path=''): for entry in tree: base_path = '{0}/{1}'.format(path, entry.name) yield base_path if entry.filemode == GIT_FILEMODE_TREE: sub_tree = self.repo.get(entry.hex) for x in self.get_names(sub_tree, base_path): yield x def do_transaction(self, commit_message, data, added, changed, removed, handlers): self.nb_transactions += 1 # Get informations git_author, git_date, git_msg, docs_to_index, docs_to_unindex = data git_msg = commit_message or git_msg or 'no comment' # List of Changed added_and_changed = list(added) + list(changed) # Build the tree from index index = self.repo.index for key in added_and_changed: handler = handlers.get(key) blob_id = self.repo.create_blob(handler.to_str()) entry = IndexEntry(key, blob_id, GIT_FILEMODE_BLOB_EXECUTABLE) index.add(entry) for key in removed: index.remove(key) git_tree = index.write_tree() # Commit self.git_commit(git_msg, git_author, git_date, tree=git_tree) def git_commit(self, message, author=None, date=None, tree=None): """Equivalent to 'git commit', we must give the message and we can also give the author and date. """ # Tree if tree is None: #tree = self.index.write_tree() raise ValueError('Please give me a tree') # Parent parent = self._resolve_reference('HEAD') parents = [parent] if parent else [] # Committer when_time = time.time() when_offset = -(time.altzone if time.daylight else time.timezone) when_offset = when_offset / 60 name = self.username email = self.useremail committer = Signature(name, email, when_time, when_offset) # Author if author is None: author = (name, email) if date: if date.tzinfo: from pytz import utc when_time = date.astimezone(utc) # To UTC when_time = when_time.timetuple() # As struct_time when_time = timegm(when_time) # To unix time when_offset = date.utcoffset().seconds / 60 else: err = "Worktree.git_commit doesn't support naive datatime yet" raise NotImplementedError, err author = Signature(author[0], author[1], when_time, when_offset) # Create the commit return self.repo.create_commit('HEAD', author, committer, message, tree, parents) def abort_transaction(self): # TODO: Remove created blobs pass
index.add(device_name) # Prepare to commit author = Signature(git_parameters['author_name'], git_parameters['author_email']) committer = Signature(git_parameters['committer_name'], git_parameters['committer_email']) # reference = 'refs/HEAD' reference = 'refs/heads/master' # Build the message year = datetime.now().strftime('%Y') month = datetime.now().strftime('%m') day = datetime.now().strftime('%d') hour = datetime.now().strftime('%H') message = 'Commit repository changes at ' + year + month + day + hour # Save the new index of repository (git add) index.write() tree_oid = index.write_tree() # tree_oid = repo.TreeBuilder().write() if not git_init: # Get previous commit (parent) parent_commit = repo.revparse_single('HEAD') commit_oid = repo.create_commit(reference, author, committer, message, tree_oid, [parent_commit.oid]) else: commit_oid = repo.create_commit(reference, author, committer, message, tree_oid, [])
class DictRepository(object): """The :class:`DictRepository <DictRepository>` object. :param repo_or_path: The path to a repository, or an existing pygit2.Repository object. If it is a path that does not exist, a new bare git repository will be initialized there. If it is a path that does exist, then the directory will be used as a bare git repository. :type repo_or_path: string or pygit2.Repository """ def __init__(self, repo_or_path=None): self._default_author = get_default_author() if isinstance(repo_or_path, Repository): self._repo = repo_or_path elif os.path.isdir(repo_or_path): self._repo = Repository(repo_or_path) else: self._repo = init_repository(repo_or_path, True) # bare repo def _key_to_ref(self, key): return "refs/%s/HEAD" % key def get_commit_oid_for_key(self, key): return self._repo[self._repo.lookup_reference(self._key_to_ref(key)).oid].oid def get_raw_dict_for_commit_oid(self, commit_oid): return json.loads(self._repo[self._repo[commit_oid].tree[DATA].oid].data) def get_parent_oids_for_commit_oid(self, commit_oid): return [parent.oid for parent in self._repo[commit_oid].parents] def raw_commit(self, key, raw_dict, author, committer, message, parents): """Commit a dict to this :class:`DictRepository <DictRepository>`. It is recommended that you use the :class:`GitDict <GitDict>` commit method instead. :param raw_dict: the data to commit. :type raw_dict: dict :param author: The author of the commit. If None, will be replaced with default. :type author: pygit2.Signature :param committer: The committer of this commit. If None, will be replaced with author. :type committer: pygit2.Signature :param message: The commit message. :type message: string :param parents: A list of 20-byte object IDs of parent commits. An empty list means this is the first commit. :return: The oid of the new commit. :rtype: 20 bytes """ if not isinstance(raw_dict, dict): raise ValueError("%s is not a dict" % raw_dict) author = author or self._default_author.signature() committer = committer or author blob_id = self._repo.write(GIT_OBJ_BLOB, json.dumps(raw_dict)) # TreeBuilder doesn't support inserting into trees, so we roll our own tree_id = self._repo.write(GIT_OBJ_TREE, "100644 %s\x00%s" % (DATA, blob_id)) return self._repo.create_commit(self._key_to_ref(key), author, committer, message, tree_id, parents) def create(self, key, dict={}, autocommit=False, message="first commit", author=None, committer=None): """Create a new :class:`GitDict <GitDict>` :param key: The key of the new :class:`GitDict <GitDict>` :type key: :class:`GitDict <GitDict>` :param dict: (optional) The value of the dict. Defaults to empty. :type dict: dict :param autocommit: (optional) Whether the :class:`GitDict <GitDict>` should automatically commit. Defaults to false. :type autocommit: boolean :param message: (optional) Message for first commit. Defaults to "first commit". :type message: string :param author: (optional) The signature for the author of the first commit. Defaults to global author. :type author: pygit2.Signature :param committer: (optional) The signature for the committer of the first commit. Defaults to author. :type author: pygit2.Signature :returns: the GitDict :rtype: :class:`GitDict <GitDict>` """ self.raw_commit(key, dict, author, committer, message, []) return self.get(key, autocommit=autocommit) def has(self, key): """Determine whether there is an entry for key in this repository. :param key: The key to check :type key: string :returns: whether there is an entry :rtype: boolean """ try: self._repo.lookup_reference(self._key_to_ref(key)) return True except KeyError: return False def get(self, key, autocommit=False): """Obtain the :class:`GitDict <GitDict>` for a key. :param key: The key to look up. :type key: string :param autocommit: (optional) Whether the :class:`GitDict <GitDict>` should automatically commit. Defaults to false. :type autocommit: boolean :returns: the GitDict :rtype: :class:`GitDict <GitDict>` :raises: KeyError if there is no entry for key """ return GitDict(self, key, autocommit=autocommit) def fast_forward(self, from_dict, to_dict): """Fast forward a :class:`GitDict <GitDict>`. :param from_dict: the :class:`GitDict <GitDict>` to fast forward. :type from_dict: :class:`GitDict <GitDict>` :param to_dict: the :class:`GitDict <GitDict>`to fast forward to. :type to_dict: :class:`GitDict <GitDict>` """ from_ref = self._key_to_ref(from_dict.key) self._repo.lookup_reference(from_ref).delete() self._repo.create_reference(from_ref, self.get_commit_oid_for_key(to_dict.key)) def clone(self, original, key): """Clone a :class:`GitDict <GitDict>`. :param original: the :class:`GitDict <GitDict>` to clone :type original: :class:`GitDict <GitDict>` :param key: where to clone to :type key: string :raises: ValueError if to_key already exists. """ try: self._repo.create_reference(self._key_to_ref(key), self.get_commit_oid_for_key(original.key)) return self.get(key, autocommit=original.autocommit) except GitError: raise ValueError("Cannot clone to %s, there is already a dict there." % key)
class GitRepositoryTestCase(unittest.TestCase): def setUp(self): self.repo_path = mkdtemp() init_repository(self.repo_path, False) self.repo = Repository(self.repo_path) def tearDown(self): try: rmtree(self.repo_path) except: pass def test_create_repo(self): repo_path = mkdtemp() try: GitRepository.create_repo(repo_path) for f in [ os.path.join('content', 'attachments', 'mercurial.png'), os.path.join('content', 'post', 'example-post.rst'), os.path.join('content', 'post', 'lorem-ipsum.rst'), os.path.join('content', 'about.rst'), os.path.join('static', 'screen.css'), os.path.join('templates', 'base.html'), os.path.join('templates', 'posts.html'), os.path.join('templates', 'post_list.html'), 'config.yaml', '.gitignore', '.git' ]: self.assertTrue(os.path.exists(os.path.join(repo_path, f)), 'Not found: %s' % f) finally: rmtree(repo_path) def test_get_changectx_rev_default(self): git_repo = GitRepository(self.repo_path) with codecs.open(os.path.join(self.repo_path, 'foo.rst'), 'w', encoding='utf-8') as fp: fp.write('foo') sign = Signature('foo', '*****@*****.**') tree = self.repo.TreeBuilder().write() self.repo.index.add('foo.rst') self.repo.create_commit('refs/heads/master', sign, sign, 'foo', tree, []) self.assertTrue( isinstance(git_repo.get_changectx(REVISION_DEFAULT), ChangeCtxDefault), 'changectx object is not an instance of ' 'ChangeCtxDefault') def test_get_changectx_rev_working_dir(self): git_repo = GitRepository(self.repo_path) with codecs.open(os.path.join(self.repo_path, 'foo.rst'), 'w', encoding='utf-8') as fp: fp.write('foo') sign = Signature('foo', '*****@*****.**') tree = self.repo.TreeBuilder().write() self.repo.index.add('foo.rst') self.repo.create_commit('refs/heads/master', sign, sign, 'foo', tree, []) self.assertTrue( isinstance(git_repo.get_changectx(REVISION_WORKING_DIR), ChangeCtxWorkingDir), 'changectx object is not an instance of ChangeCtxWorkingDir')
class GitBlack: def __init__(self): self.repo = Repository(".") self.patchers = {} def get_blamed_deltas(self, patch): filename = patch.delta.old_file.path self.patchers[filename] = Patcher(self.repo, filename) hb = HunkBlamer(self.repo, patch) return hb.blames() def group_blame_deltas(self, blames): for delta_blame in blames: commits = tuple(sorted(delta_blame.commits)) self.grouped_deltas.setdefault(commits, []).append(delta_blame.delta) self.progress += 1 now = time.monotonic() if now - self.last_log > 0.04: sys.stdout.write("Reading file {}/{} \r".format( self.progress, self.total)) sys.stdout.flush() self.last_log = now def commit_changes(self): start = time.monotonic() self.grouped_deltas = {} for path, status in self.repo.status().items(): if status & index_statuses: raise GitIndexNotEmpty patches = [] self._file_modes = {} diff = self.repo.diff(context_lines=0, flags=GIT_DIFF_IGNORE_SUBMODULES) for patch in diff: if patch.delta.status != GIT_DELTA_MODIFIED: continue self._file_modes[ patch.delta.old_file.path] = patch.delta.old_file.mode patches.append(patch) self.progress = 0 self.last_log = 0 self.total = len(patches) executor = ThreadPoolExecutor(max_workers=8) tasks = set() for patch in patches: tasks.add(executor.submit(self.get_blamed_deltas, patch)) if len(tasks) > 8: done, not_done = wait(tasks, return_when=FIRST_COMPLETED) for task in done: self.group_blame_deltas(task.result()) tasks -= set(done) for task in tasks: self.group_blame_deltas(task.result()) secs = time.monotonic() - start sys.stdout.write("Reading file {}/{} ({:.2f} secs).\n".format( self.progress, self.total, secs)) start = time.monotonic() self.total = len(self.grouped_deltas) self.progress = 0 self.last_log = 0 for commits, deltas in self.grouped_deltas.items(): blobs = self._create_blobs(deltas) self._commit(commits, blobs) secs = time.monotonic() - start print("Making commit {}/{} ({:.2f} secs).".format( self.progress, self.total, secs)) def _create_blobs(self, deltas): filenames = set() for delta in deltas: self.patchers[delta.filename].apply(delta) filenames.add(delta.filename) blobs = {} for filename in filenames: blob_id = self.repo.create_blob(self.patchers[filename].content()) blobs[filename] = blob_id return blobs def _commit(self, original_commits, blobs): for filename, blob_id in blobs.items(): file_mode = self._file_modes[filename] index_entry = IndexEntry(filename, blob_id, file_mode) self.repo.index.add(index_entry) commits = [self.repo.get(h) for h in original_commits] main_commit = commits[0] if len(commits) > 1: # most recent commit main_commit = sorted(commits, key=commit_datetime)[-1] commit_message = main_commit.message commit_message += "\n\nautomatic commit by git-black, original commits:\n" commit_message += "\n".join( [" {}".format(c) for c in original_commits]) committer = Signature( name=self.repo.config["user.name"], email=self.repo.config["user.email"], ) self.repo.index.write() tree = self.repo.index.write_tree() head = self.repo.head.peel() self.repo.create_commit("HEAD", main_commit.author, committer, commit_message, tree, [head.id]) self.progress += 1 now = time.monotonic() if now - self.last_log > 0.04: sys.stdout.write("Making commit {}/{} \r".format( self.progress, self.total)) sys.stdout.flush() self.last_log = now
from pygit2 import Repository, Signature import os from datetime import datetime, date from subprocess import call d = datetime.utcnow().date() path = '/home/xubin/workspace/tickets_history/' name = '%s.txt' % d.strftime('%Y-%m-%d') abspath = os.path.join(path, name) f = open(abspath, 'ab') f.write('test\n'); f.close() repo = Repository(os.path.join(path, '.git')) index = repo.index index.add(name) index.write() oid = index.write_tree() author = Signature('icefreedom', '*****@*****.**') cm = repo.create_commit('HEAD', author, author, 'test', oid, [repo.head.oid]) username = '******' passwd = '87650872ice' remote_repo = 'https://%s:%[email protected]/icefreedom/tickets_history.git' % (username, passwd) call(['cd "' + path + '" && git push ' + remote_repo] , shell=True)
open(x64_binary_path, 'rb').read()).hexdigest()] = 'DSpellCheck.dll' str_after = json.dumps(validate_data['DSpellCheck']) validate_text = validate_text.replace(str_before[1:-1], str_after[1:-1]) with open(validate_path, "w", encoding='utf-8') as file: file.write(validate_text) print('Creating commit in npp-plugins-x64 repository...') repo = Repository(plugins_x64_path) index = repo.index index.add_all() index.write() config = Config.get_global_config() author = Signature(config['user.name'], config['user.email']) commiter = author tree = index.write_tree() repo.create_commit('refs/heads/master', author, commiter, 'Update DSpellCheck to {}'.format(ver), tree, [repo.head.get_object().hex]) else: print('%NPP_PLUGINS_X64_PATH% is not set up, nothing to update') successString = 'Success!' try: from colorama import init, Fore, Style init() successString = Fore.GREEN + Style.BRIGHT + successString except: pass print(successString)
class GitHandler(object): def __init__(self, path, repo_path=None, update_working_copy=True): """ Start a git handler in given repository. `update_working_copy`: wether also to update the working copy. By default, the git handler will only work on the git database. Updating the working copy can take a lot of time in large repositories. """ self.path = path if repo_path is None: repo_path = self.path self.repo_path = repo_path self.update_working_copy = update_working_copy self.repo = Repository(self.repo_path) self.working_tree = self.get_last_tree() self.tree_modifier = TreeModifier(self.repo, self.working_tree) self.messages = [] print("Started libgit2 git handler in ", self.path) def get_last_tree(self): if self.repo.head_is_unborn: tree_id = self.repo.TreeBuilder().write() return self.repo[tree_id] commit = self.repo[self.getCurrentCommit()] return commit.tree def insert_into_working_tree(self, blob_id, filename): self.tree_modifier.insert_blob(blob_id, filename) def remove_from_working_tree(self, filename): self.tree_modifier.remove_blob(filename) def write_file(self, filename, content): # TODO: combine writing many files assert isinstance(content, text_type) data = content.encode('utf-8') existing_entry = get_tree_entry(self.repo, self.working_tree, filename) if existing_entry: type = 'M' if existing_entry.id == git_hash(data): return else: type = 'A' blob_id = self.repo.create_blob(data) self.insert_into_working_tree(blob_id, filename) if not self.repo.is_bare and self.update_working_copy: real_filename = os.path.join(self.path, filename) mkdir_p(os.path.dirname(real_filename)) with codecs.open(real_filename, 'w', encoding='utf-8') as outfile: outfile.write(content) self.messages.append(' {} {}'.format(type, filename)) def remove_file(self, filename): existing_entry = get_tree_entry(self.repo, self.working_tree, filename) if existing_entry: self.remove_from_working_tree(filename) if not self.repo.is_bare and self.update_working_copy: remove_file_with_empty_parents(self.path, filename) self.messages.append(' D {}'.format(filename)) def move_file(self, old_filename, new_filename): self.tree_modifier.move(old_filename, new_filename) if not self.repo.is_bare and self.update_working_copy: real_old_filename = os.path.join(self.path, old_filename) real_new_filename = os.path.join(self.path, new_filename) mkdir_p(os.path.dirname(real_new_filename)) os.rename(real_old_filename, real_new_filename) remove_file_with_empty_parents(self.path, old_filename) self.messages.append(' R {} -> {}'.format(old_filename, new_filename)) def commit(self): if self.tree_modifier.tree.oid != self.get_last_tree().oid: raise Exception("The repository was modified outside of this process. For safety reasons, we cannot commit!") self.working_tree = self.tree_modifier.apply() self.tree_modifier = TreeModifier(self.repo, self.working_tree) if self.repo.head_is_unborn: parents = [] else: commit = self.repo[self.getCurrentCommit()] if commit.tree.id == self.working_tree.id: return parents = [commit.id] config = self.repo.config author = Signature(config['user.name'], config['user.email']) committer = Signature(config['user.name'], config['user.email']) tree_id = self.working_tree.id message = '\n'.join(self.messages) self.repo.create_commit('refs/heads/master', author, committer, message, tree_id, parents) self.saveCurrentCommit() self.messages = [] if not self.repo.is_bare and self.update_working_copy: self.repo.index.read_tree(self.working_tree) self.repo.index.write() def reset(self): self.working_tree = self.get_last_tree() self.tree_modifier = TreeModifier(self.repo, self.working_tree) self.messages = [] def getCurrentCommit(self): return self.repo.head.target def saveCurrentCommit(self): with open(os.path.join(self.path, 'dbcommit'), 'w') as dbcommit_file: dbcommit_file.write(self.getCurrentCommit().hex+'\n')