fixed branch view with GLOBALS
[GITManagedWebpage.git] / GITManagedWebpage.class.php
index 9093b8a4e2c0795848076be94474529ae703db01..0522cc8ea39ac14b08691be4753000d503ef912a 100644 (file)
 
 class GITManagedWebpage {
     const ERROR_CRITICAL = 1;
-    
+    const SESSION_PREFIX = "GITManagedWebpage_";
 
     private $giturl;
-    private $workdir;
+    private $workdir, $localdir;
+    private $ready = false;
+    private $loopedcall = false;
+    private $config = null;
+    private $config_changed = true;
+    private $activeSession = null;
     
-    public function __construct($giturl, $workdir = null) {
+    public function __construct($giturl, $workdir = null, $localdir = null) {
+        if(session_status() != PHP_SESSION_ACTIVE) {
+            session_start();
+        }
+        
+        if(defined("GITMANAGED_EXECUTED")) {
+            $this->loopedcall = true;
+            return;
+        }
+        
         if($workdir === null) {
             $workdir = dirname(__FILE__);
-            if(!substr($workdir, -1) == "/")
+            if(substr($workdir, -1) != "/")
                 $workdir .= "/";
             $workdir .= ".gitmanaged/";
         }
-        else if(!substr($workdir, -1) == "/")
+        else if(substr($workdir, -1) != "/")
             $workdir .= "/";
         
+        if($localdir === null) {
+            $localdir = dirname(__FILE__);
+            if(substr($localdir, -1) != "/")
+                $localdir .= "/";
+        }
+        else if(substr($localdir, -1) != "/")
+            $localdir .= "/";
+        
         $this->giturl = $giturl;
         $this->workdir = $workdir;
+        $this->localdir = $localdir;
         
-        //if(!file_exists($this->workdir) || !is_dir($this->workdir)) {
+        if(!file_exists($this->workdir) || !is_dir($this->workdir)) {
             if(file_exists($this->workdir) && !is_dir($this->workdir)) {
                 $this->error(self::ERROR_CRITICAL, "local workdir (".htmlspecialchars($this->workdir).") is not a directory.");
                 return;
             }
             $this->setupWorkdir();
-        //}
+        }
+        $this->ready = true;
     }
     
     /* private function gitcmd(...)
@@ -66,11 +90,59 @@ class GITManagedWebpage {
             } else
                 $argstr .= " ".escapeshellarg($arg);
         }
-        $gitcmd = 'git '.escapeshellarg('--git-dir='.$this->workdir.'/repository').$argstr;
+        $gitcmd = 'git '.escapeshellarg('--git-dir='.$this->workdir.'repository/.git').' '.escapeshellarg('--work-tree='.$this->workdir.'repository').$argstr;
         $output = shell_exec($gitcmd);
         return $output;
     }
     
+    /* private function setConfig($name, $value)
+    * store a option in the configuration
+    */
+    private function setConfig($name, $value) {
+        $this->config[strtolower($name)] = $value;
+        $this->config_changed = true;
+    }
+    
+    private function checkConfigIntegrity() {
+        foreach($this->config as $key => $value) {
+            if(substr($key, 0, strlen('branch_')) == 'branch_') {
+                if(!file_exists($this->workdir.$key)) {
+                    unset($this->config['key']);
+                }
+            }
+        }
+    }
+    
+    /* private function getConfig($name)
+    * get an option from the configuration
+    */
+    private function getConfig($name) {
+        if($this->config == null) {
+            if(!$this->ready)
+                return null;
+            if(file_exists($this->workdir."config.txt")) {
+                $config_txt = @file_get_contents($this->workdir."config.txt");
+                $this->config = unserialize($config_txt);
+                $this->checkConfigIntegrity();
+            } else {
+                $this->config = array();
+                return null;
+            }
+        }
+        if(array_key_exists(strtolower($name), $this->config))
+            return $this->config[strtolower($name)];
+        else
+            return null;
+    }
+    
+    private function saveConfig() {
+        if($this->config_changed && $this->ready) {
+            $fp = fopen($this->workdir."config.txt", "w");
+            fwrite($fp, serialize($this->config));
+            fclose($fp);
+        }
+    }
+    
     /* private function setupWorkdir()
     * Setup local GITManagedWebpage Work directory with git repository
     */
@@ -83,24 +155,186 @@ class GITManagedWebpage {
         }
         
         mkdir($this->workdir);
-        mkdir($this->workdir.'/repository');
-        shell_exec('git clone '.escapeshellarg($this->giturl).' '.escapeshellarg($this->workdir.'/repository'));
+        mkdir($this->workdir.'repository');
+        shell_exec('git clone '.escapeshellarg($this->giturl).' '.escapeshellarg($this->workdir.'repository'));
         $gitok = $this->gitcmd("status");
         if(preg_match("#Not a git repository#", $gitok)) {
-            rmdir($this->workdir.'/repository');
+            rmdir($this->workdir.'repository');
             rmdir($this->workdir);
             $this->error(self::ERROR_CRITICAL, "error cloning git repository.");
             return;
         }
+        $this->ready = true;
         
+        $default_branch = str_replace(array("\r", "\n"), array("", ""), $this->gitcmd("rev-parse", "--abbrev-ref", "HEAD"));
+        $this->setConfig("defaultbranch", $default_branch);
+        $this->saveConfig();
+    }
+    
+    private function getActiveBranch() {
+        if($this->activeSession)
+            return $this->activeSession;
+        else if(isset($_SESSION[self::SESSION_PREFIX.'branch']))
+            return $_SESSION[self::SESSION_PREFIX.'branch'];
+        else
+            return $this->getConfig("defaultbranch");
+    }
+    
+    private function setActiveBranch($branch, $remember) {
+        $this->activeSession = $branch;
+        if($remember)
+            $_SESSION[self::SESSION_PREFIX.'branch'] = $branch;
+    }
+    
+    private function getLocalUntrackedFiles() {
+        $default_branch = $this->getConfig("defaultbranch");
+        $tracked_files = $this->gitcmd("ls-tree", $default_branch, "--full-name", "--name-only");
+        $local_files = shell_exec("find ".escapeshellarg($this->localdir));
+        $untracked_files = array();
         
+        $tracked_files = explode("\n", str_replace(array("\r"), array(""), $tracked_files));
+        $local_files = explode("\n", str_replace(array("\r"), array(""), $local_files));
+        
+        foreach($local_files as $local_file) {
+            if(!$local_file)
+                continue;
+            if($strip_local || (($strip_local = strlen($this->localdir)) && substr($local_file, 0, $strip_local) == $this->localdir)) {
+                $local_file = substr($local_file, $strip_local);
+            }
+            $tracked = false;
+            foreach($tracked_files as $tracked_file) {
+                if($tracked_file == $local_file) {
+                    $tracked = true;
+                    break;
+                }
+            }
+            if(!$tracked) {
+                $untracked_files[] = $local_file;
+            }
+        }
+        return $untracked_files;
+    }
+    
+    private function branchExists($branch) {
+        //check if branch exists
+        $gitret = $this->gitcmd("rev-list", "--max-count=1", $branch);
+        if(!preg_match("#([a-z0-9]{40})#", $gitret, $match))
+            return false;
+        else
+            return $match[1];
+    }
+    
+    private function localBranchPath($branch, $create = false) {
+        $default_branch = $this->getConfig("defaultbranch");
+        if($branch == $default_branch)
+            $dir = $this->localdir;
+        else
+            $dir = $this->workdir.'branch_'.str_replace(array('/'), array('_'), $branch).'/';
+        if(file_exists($dir))
+            return $dir;
+        else if($create) {
+            mkdir($dir);
+            return $dir;
+        } else 
+            return false;
+    }
+    
+    private function updateBranch($branch, $path, $force = false) {
+        if(substr($path, -1) != '/')
+            $path .= '/';
+        $current_branch = str_replace(array("\r", "\n"), array("", ""), $this->gitcmd("rev-parse", "--abbrev-ref", "HEAD"));
+        if($current_branch != $branch)
+            $this->gitcmd("checkout", $branch);
+        $this->gitcmd("pull");
+        $gitret = $this->gitcmd("rev-list", "--max-count=1", $branch);
+        preg_match("#([a-z0-9]{40})#", $gitret, $match);
+        $newest_version = $match[1];
+        
+        $deleted_files = array();
+        if(($current_version = $this->getConfig('version_'.$branch))) {
+            if($current_version == $newest_version && !$force)
+                return;
+            else {
+                $override_all = true;
+                $delfiles = $this->gitcmd("diff", "--diff-filter=D", "--name-only", $current_version, $newest_version);
+                $delfiles = explode("\n", str_replace(array("\r"), array(""), $delfiles));
+                foreach($delfiles as $file) {
+                    if(!$file)
+                        continue;
+                    $deleted_files[] = $file;
+                }
+            }
+        } else
+            $override_all = true;
+        if($override_all) {
+            $rsync_present = preg_match("#rsync#", `which rsync`);
+            if($rsync_present)
+                shell_exec('rsync -avz --exclude ".git" '.escapeshellarg($this->workdir."repository/").' '.escapeshellarg($path));
+            else
+                shell_exec('tar -c --exclude ".git" -C '.escapeshellarg($this->workdir."repository").' . | tar -x -C '.escapeshellarg($path));
+            
+            // remove deleted files
+            foreach($deleted_files as $file) {
+                unlink($path.$file);
+            }
+        }
+        $this->setConfig('version_'.$branch, $newest_version);
+        $this->saveConfig();
     }
     
     /* public function update()
-    * Pulls latest commit of active branch and overwrites local files
+    * Pulls latest commit of active branch and overwrites files in branch folder
     */
     public function update() {
+        if($this->loopedcall)
+            return;
+        
+        $active_branch = $this->getActiveBranch();
+        
+        if(!$this->branchExists($active_branch))
+            return false;
+        $dir = $this->localBranchPath($active_branch, true);
+        $this->updateBranch($active_branch, $dir);
+    }
+    
+    public function setBranch($branch, $remember = false) {
+        if($this->loopedcall)
+            return;
+        
+        if(!$this->branchExists($branch)) {
+            if(!$this->branchExists('origin/'.$branch))
+                return false;
+        }
+        $this->setActiveBranch($branch, $remember);
+        
+        if(!$this->localBranchPath($branch)) {
+            $dir = $this->localBranchPath($branch, true);
+            $this->updateBranch($branch, $dir, true);
+        }
+    }
     
+    public function getExecFile($file = null) {
+        if($this->loopedcall)
+            return;
+        define("GITMANAGED_EXECUTED", true);
+        
+        if(!$file)
+            $file = $_SERVER['PHP_SELF'];
+        if($file[0] == '/')
+            $file = substr($file, 1);
+        
+        $default_branch = $this->getConfig("defaultbranch");
+        $active_branch = $this->getActiveBranch();
+        if($active_branch != $default_branch) {
+            if(!($dir = $this->localBranchPath($branch))) {
+                $dir = $this->localBranchPath($active_branch, true);
+                $this->updateBranch($active_branch, $dir, true);
+            }
+            chdir($dir);
+            return $dir.$file;
+        } else {
+            return $file;
+        }
     }
     
 }