def add_file(self, filename): binstore_filename = self.get_binstore_filename(filename) # TODO: make hash algorithm configurable # relative link is needed, here, so it points from the file directly to # the .git directory relative_link = os.path.relpath(binstore_filename, os.path.dirname(filename)) # create only a link if file already exists in binstore if os.path.exists(binstore_filename): print('WARNING: File with that hash already exists in binstore.') if filecmp.cmp(filename, binstore_filename): print(' Creating a link to existing file') commands = cmd.CompoundCommand( cmd.SafeRemoveCommand(filename), cmd.LinkToFileCommand(filename, relative_link), cmd.GitAddCommand(self.gitrepo, filename), ) else: raise ValueError('hash collision found between %s and %s', filename, binstore_filename) else: commands = cmd.CompoundCommand( cmd.SafeMoveFileCommand(filename, binstore_filename), cmd.LinkToFileCommand(filename, relative_link), cmd.ChmodCommand(stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH, binstore_filename), cmd.GitAddCommand(self.gitrepo, filename), ) commands.execute()
def checkout(self, filenames): """ Revert local modifications to a list of files """ printv("GitBin.checkout(%s)" % filenames) for filename in filenames: # if the filename is a directory, recurse into it. # TODO: maybe make recursive directory crawls optional/configurable if os.path.isdir(filename): printv("\trecursing into %s" % filename) for root, dirs, files in os.walk(filename): # first checkout_dashdash all directories recursively len(dirs) and self.checkout( [os.path.join(root, dn) for dn in dirs]) # now checkout_dashdash all the files len(files) and self.checkout( [os.path.join(root, fn) for fn in files]) continue status = self.gitrepo.status(filename) if (status & git.STATUS_STAGED_MASK) == git.STATUS_STAGED: # staged, skip it. print "you probably meant to do: git bin reset %s" % filename continue if not (status & git.STATUS_CHANGED_MASK): # the file hasn't changed, skip it. continue # The first two cases can just be passed through to regular git # checkout --. # {1} (GBAdded[MSs] -> Reset[MS]) # {2} (GBEdit[TF]) # In the third case, there is some local modification that we should # save 'just in case' first. # {3} (GBEdit[TF] -> Modified[TF]) (*) if (status & git.STATUS_TYPECHANGED ) and not self.binstore.has(filename): justincase_filename = os.path.join( "/tmp", "%s.%s.justincase" % (filename, utils.md5_file(filename))) commands = cmd.CompoundCommand( cmd.CopyFileCommand( self.binstore.get_binstore_filename(filename), filename, justincase_filename), ) commands.execute() self.gitrepo.restore(filename)
def edit_file(self, filename): printv("edit_file(%s)" % filename) printv("binstore_filename: %s" % self.get_binstore_filename(filename)) temp_filename = os.path.join(os.path.dirname(filename), ".tmp_%s" % os.path.basename(filename)) printv("temp_filename: %s" % temp_filename) commands = cmd.CompoundCommand( cmd.CopyFileCommand(self.get_binstore_filename(filename), temp_filename), cmd.SafeMoveFileCommand(temp_filename, filename, noprogress=True), cmd.ChmodCommand( stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH, filename), ) commands.execute()
def reset(self, filenames): """ Unstage a list of files """ printv("GitBin.reset(%s)" % filenames) for filename in filenames: # if the filename is a directory, recurse into it. # TODO: maybe make recursive directory crawls optional/configurable if os.path.isdir(filename): printv("\trecursing into %s" % filename) for root, dirs, files in os.walk(filename): # first reset all directories recursively len(dirs) and self.reset( [os.path.join(root, dn) for dn in dirs]) # now reset all the files len(files) and self.reset( [os.path.join(root, fn) for fn in files]) continue status = self.gitrepo.status(filename) if not status & git.STATUS_STAGED_MASK == git.STATUS_STAGED: # not staged, skip it. print "you probably meant to do: git bin checkout -- %s" % filename continue # unstage the file: self.gitrepo.unstage(filename) # key: F=real file; S=symlink; T=typechange; M=modified; s=staged # {1} ([F] -> GBAdded[Ss]) -> Untracked[S] # {2} ([S] -> GBEdit[TF] -> Modified[TF] -> GBAdded[MSs]) # -> Modified[MS] new_status = self.gitrepo.status(filename) if self.binstore.has(filename) and ( new_status & git.STATUS_UNTRACKED or new_status & git.STATUS_MODIFIED): # TODO: in case {1} it's possible that we might be leaving an # orphan unreferenced file in the binstore. We might want to # deal with this. commands = cmd.CompoundCommand( cmd.CopyFileCommand( self.binstore.get_binstore_filename(filename), filename), ) commands.execute()
def init(self, binstore_base): self.localpath = os.path.join(self.gitrepo.path, ".git", "binstore") self.path = os.path.join(binstore_base, self.gitrepo.reponame) if not os.path.exists(binstore_base): raise BinstoreException(( "The binstore base (%s) is inaccessible. Did you forget to mount" + " something?") % binstore_base) if not os.path.exists(self.localpath): print "creating" commands = cmd.CompoundCommand( cmd.MakeDirectoryCommand(self.path), cmd.LinkToFileCommand(self.localpath, self.path), ) commands.execute() # self.gitrepo.config.set("binstore", "path", self.path) if not os.path.exists(self.path): raise BinstoreException( "A binstore.path is set (%s), but it doesn't exist. Weird." % self.path)