require "proto" require "ustring" local git = Object:clone() -- chdirs into the scm root dir, takes some docroot paths, returns scm relative paths function git:enter(...) assert(not self.olddir, "ESYS:Already entered") self.olddir = lfs.currentdir() lfs.chdir(uwiki_conf.prefix .. uwiki_conf.docroot .. self.path) local rpaths = {} for _,path in ipairs({...}) do rpaths[#rpaths+1] = path:match(self.path:sanitize('pat') .. "(.*)") end return unpack(rpaths) end -- back to toplevel dir, cleanup needed? function git:leave() assert(lfs.chdir(self.olddir), "ESYS:chdir failed") self.olddir = nil end -- commit a single file with comment, TODO: do we ever need multiple files? make path a table then function git:commit(session, path, comment) assert(self.olddir, "ESYS:Not Entered") assert(os.execute( [[ export GIT_COMMITTER_NAME=%name% export GIT_COMMITTER_EMAIL=%email% export GIT_AUTHOR_NAME=%name% export GIT_AUTHOR_EMAIL=%email% git add %file% >&/dev/null git commit -m %comment% >&/dev/null ]] % { name = session.user.name:sanitize('shquote'), email = session.user.email:sanitize('shquote'), comment = comment:sanitize('shquote'), file = path:sanitize('shquote') }) == 0, "ECOMMIT:couldnt git commit") end -- Merge machinery: -- is a file versioned under revision control? function git:versioned(path) assert(self.olddir, "ESYS:scm not entered") local command = "git rev-parse %file% >&/dev/null" % { file = ("HEAD:"..path):sanitize('shquote')} return os.execute(command) == 0 end -- get a unique id describing the revision of a single file function git:id(path) assert(self.olddir, "ESYS:scm not entered") if self:versioned(path) then local file = io.popen("git rev-parse %file%" % { file = ("HEAD:"..path):sanitize('shquote')}) local id = file:read("*l") file:close() return id else return "" end end -- id identifier of the original file (common base) -- newfile file uploaded by the user (ours) -- currentfile current existing file (theirs) -- return merged_file, conflict_bool function git:merge(id, newfile, currentfile) local currentid = self:id(currentfile) if id == currentid then return newfile, false else -- checkout the base local basefile = os.tmpname() assert(os.execute("git cat-file blob %id% >%basefile%" % { id = id:sanitize('shquote'), basefile = basefile:sanitize('shquote') } ) == 0, "ENFILE:could not checkout original file") -- git merge-file local merged = os.tmpname() local conflicts = os.execute( [[ git merge-file -p -q -L 'YOUR:%currentfile%' -L 'BASE:%currentfile%' -L 'OTHER:%currentfile%' \ '%newfile%' '%basefile%' '%currentfile%' >'%merged%' ]] % { currentfile = currentfile, newfile = newfile, basefile = basefile, merged = merged } ) assert(conflicts >= 0, "EMERGE:error when merging files") return merged, conflicts == 0 and true or false end end return git -- Local Variables: -- mode: lua -- lua-indent-level: 4 -- indent-tabs-mode: nil -- End: