GitMonitor.vala
[gitlive] / GitRepo.vala
index 79734ee..e51d150 100644 (file)
  *
  *
  */
+static GitRepo  _GitRepo; 
 public class GitRepo : Object
 {
-    
+     
     public Array<GitMonitorQueue> cmds;
 
-
+    public string name;
     public string gitdir;
+    public string git_working_dir;
     public bool debug = false;
+    
+    public Gee.HashMap<string,bool> ignore_files;
+    public GitBranch currentBranch;
+
+       public RooTicket? activeTicket;
+
+       public static GitRepo singleton()
+    {
+        if (_GitRepo == null) {
+            _GitRepo = new GitRepo.single();
+            _GitRepo.cache = new Gee.HashMap<string,GitRepo>();
+        }
+        return _GitRepo;
+    }
 
     /**
     * index of.. matching gitpath..
     */
-     public static int indexOfAdd( Array<GitRepo> repos, string gitpath) {
+    public static int indexOf( Array<GitRepo> repos, string gitpath) {
+        // make a fake object to compare against..
+        var test_repo = GitRepo.get(gitpath);
+        
         for(var i =0; i < repos.length; i++) {
-            if (repos.index(i).gitdir == gitpath) {
+            if (repos.index(i).gitdir == test_repo.gitdir) {
                 return i;
             }
         }
         return -1;
     
     }
+    
+    public  Gee.HashMap<string,GitRepo> cache;
+    
+    
+    
+    public static   Array<GitRepo> list()
+    {
 
-
-
-   
+        //if (GitRepo.list_cache !=  null) {
+        //    unowned  Array<GitRepo>    ret = GitRepo.list_cache;
+         //   return ret;
+        //}
+        var cache = GitRepo.singleton().cache;
+        var list_cache = new Array<GitRepo>();
+        
+        var dir = Environment.get_home_dir() + "/gitlive";
+        
+        var f = File.new_for_path(dir);
+        FileEnumerator file_enum;
+        try {
+            file_enum = f.enumerate_children(
+                FileAttribute.STANDARD_DISPLAY_NAME + ","+ 
+                FileAttribute.STANDARD_TYPE,
+                FileQueryInfoFlags.NONE,
+                null);
+        } catch (Error e) {
+            
+            return list_cache;
+            
+        }
+        
+        FileInfo next_file; 
+        
+        while (true) {
+            
+            try {
+                next_file = file_enum.next_file(null);
+                if (next_file == null) {
+                    break;
+                }
+                
+            } catch (Error e) {
+                GLib.debug("Error: %s",e.message);
+                break;
+            }
+         
+            //print("got a file " + next_file.sudo () + '?=' + Gio.FileType.DIRECTORY);
+            
+            if (next_file.get_file_type() !=  FileType.DIRECTORY) {
+                next_file = null;
+                continue;
+            }
+            
+            if (next_file.get_file_type() ==  FileType.SYMBOLIC_LINK) {
+                next_file = null;
+                continue;
+            }
+            
+            if (next_file.get_display_name()[0] == '.') {
+                next_file = null;
+                continue;
+            }
+            var sp = dir+"/"+next_file.get_display_name();
+           
+            var gitdir = dir + "/" + next_file.get_display_name() + "/.git";
+            
+            if (!FileUtils.test(gitdir, FileTest.IS_DIR)) {
+                continue;
+            }
+            
+               var rep =  GitRepo.get(  sp );
+               list_cache.append_val(rep);             
+            
+        }
+    
+        return list_cache;
+        
+         
+          
+       }
+       
+       public static GitRepo get(string path) 
+       {
+               var cache = GitRepo.singleton().cache;
+               if (cache.has_key(path)) {
+                       return cache.get(path);
+               }
+               return new GitRepo(path);
+       }
+       
+    private GitRepo.single() {
+               // used to create the signleton
+       }
     /**
      * constructor:
      * 
@@ -40,38 +149,145 @@ public class GitRepo : Object
      *
      */
      
-    public GitRepo(string path) {
+    private GitRepo(string path) {
         // cal parent?
+        this.name =   File.new_for_path(path).get_basename();
+        this.ignore_files = new Gee.HashMap<string,bool>();
         
+        this.git_working_dir = path;
         this.gitdir = path + "/.git";
         if (!FileUtils.test(this.gitdir , FileTest.IS_DIR)) {
             this.gitdir = path; // naked...
         }
         this.cmds = new  Array<GitMonitorQueue> ();
-        //Repo.superclass.constructor.call(this,cfg);
         
+               var cache = GitRepo.singleton().cache;
+        //Repo.superclass.constructor.call(this,cfg);
+               if ( !cache.has_key(path) ) {
+                       cache.set( path, this);
+       }
+       this.loadBranches();
     } 
+    
+    public bool is_wip_branch()
+    {
+       return this.currentBranch.name.has_prefix("wip_")
+               
+    }
+    
+    public bool is_autocommit ()
+    {
+       return !FileUtils.test(this.gitdir + "/.gitlive-disable-autocommit" , FileTest.EXISTS);
+    }
+    public bool is_autopush ()
+    {
+       return !FileUtils.test(this.gitdir + "/.gitlive-disable-autopush" , FileTest.EXISTS);
+    }
+    
+    Gee.HashMap<string,GitBranch> branches;
+    
+    public void loadBranches()
+    {
+       this.branches = new Gee.HashMap<string,GitBranch>();
+       
+       string[] cmd = { "branch",   "--no-color", "--verbose", "--no-abbrev" , "-a"  };
+        var res = this.git( cmd );
+        var lines = res.split("\n");
+        for (var i = 0; i < lines.length ; i++) {
+               var br = new GitBranch(this);
+               if (!br.parseBranchListItem(lines[i])) {
+                       continue;
+               }
+               GLib.debug("add branch %s", br.realName());
+                
+               branches.set(br.realName(), br);
+               if (br.active) {
+                       this.currentBranch = br;
+               }
+        }
+    
+    }
+     
+    
+    
+    
+    public string branchesToString()
+    {
+       var ret = "";
+               foreach( var br in this.branches.values) {
+                       if (br.name == "") {
+                               continue; 
+                       }
+                       ret += ret.length > 0 ? ","  : "";
+                       ret += br.name;
+               
+               }
+               return ret;
+       
+    }
+    RooTicket? ticket = null;
+    
+    public void setActiveTicket(RooTicket ticket, string branchname)
+    {
+       this.createBranchNamed(branchname);
+       FileUtils.set_contents(this.gitdir + "/.gitlive-active-ticket" , ticket.id);
+       this.activeTicket = ticket;
+    }
+    
+    public void createBranchNamed(string branchname)
+    {
+        string[] cmd = { "checkout", "-b" , branchname  };
+        this.git(cmd);
+        this.loadBranches(); // update branch list...
+    }
+    
+    
     /**
      * add:
      * add files to track.
      *
      * @argument {Array} files the files to add.
      */
-    public string add ( Array<GitMonitorQueue> files )
+    public string add ( Array<GitMonitorQueue> files ) throws Error, SpawnError
     {
         // should really find out if these are untracked files each..
         // we run multiple versions to make sure that if one failes, it does not ignore the whole lot..
         // not sure if that is how git works.. but just be certian.
+        var ret = "";
         for (var i = 0; i < files.length;i++) {
             var f = files.index(i).vname;
             try {
-                this.git( { "add" } + f);
+                string[] cmd = { "add",    f  };
+                this.git( cmd );
             } catch (Error e) {
                 ret += e.message  + "\n";
             }        
 
         }
+        return ret;
     }
+        
+    public bool is_ignore(string fname) throws Error, SpawnError
+    {
+               if (fname == ".gitignore") {
+                       this.ignore_files.clear();
+               }
+               
+               if (this.ignore_files.has_key(fname)) {
+                       return this.ignore_files.get(fname);
+               }
+               
+               try {
+                       var ret = this.git( { "check-ignore" , fname } );
+                       this.ignore_files.set(fname, ret.length >  0);
+                       return ret.length > 0;
+               } catch (SpawnError e) {
+                       this.ignore_files.set(fname, false);
+                       return false;
+               }
+                
+    } 
+    
     
       /**
      * remove:
@@ -79,7 +295,7 @@ public class GitRepo : Object
      *
      * @argument {Array} files the files to add.
      */
-    public string remove  ( Array<GitMonitorQueue> files )
+    public string remove  ( Array<GitMonitorQueue> files ) throws Error, SpawnError
     {
         // this may fail if files do not exist..
         // should really find out if these are untracked files each..
@@ -90,7 +306,8 @@ public class GitRepo : Object
         for (var i = 0; i < files.length;i++) {
             var f = files.index(i).vname;
             try {
-                this.git( { "rm",  "-f" } + f  );
+                string[] cmd = { "rm",  "-f" ,  f  };
+                this.git( cmd );
             } catch (Error e) {
                 ret += e.message  + "\n";
             }        
@@ -100,6 +317,7 @@ public class GitRepo : Object
 
     }
     
+    
     /**
      * commit:
      * perform a commit.
@@ -114,7 +332,7 @@ public class GitRepo : Object
      * 
      */
      
-    public string commit ( string message, Array<GitMonitorQueue> files  )
+    public string commit ( string message, Array<GitMonitorQueue> files  ) throws Error, SpawnError
     {
         
 
@@ -136,13 +354,13 @@ public class GitRepo : Object
             
         }
         */
-        var args = { "commit", "-m", };
-        arg +=  (message.length > 0  ? message : "Changed" );
+        string[] args = { "commit", "-m" };
+        args +=  (message.length > 0  ? message : "Changed" );
         for (var i = 0; i< files.length ; i++ ) {
             args += files.index(i).vname; // full path?
         }
          
-        return this.git(args, env);
+        return this.git(args);
     }
     
     /**
@@ -153,13 +371,25 @@ public class GitRepo : Object
      * -- maybe later it will have a few options and do more stuff..
      *
      */
-    public string pull ()
+    public string pull () throws Error, SpawnError
     {
         // should probably hand error conditions better... 
-        return this.git({ "pull" });
-        
+        string[] cmd = { "pull" , "--no-edit" };
+        return this.git( cmd );
+
         
     }
+    
+    public delegate void GitAsyncCallback (GitRepo repo, int err, string str);
+    public void pull_async(GitAsyncCallback cb) 
+    {
+    
+        string[] cmd = { "pull" , "--no-edit" };
+         this.git_async( cmd , cb);
+         
+    
+    }
+    
     /**
      * push:
      * Send local changes to remote repo(s)
@@ -168,12 +398,15 @@ public class GitRepo : Object
      * -- maybe later it will have a few options and do more stuff..
      *
      */
-    public string push ()
+    public string push () throws Error, SpawnError
     {
         // should 
-        return this.git({ "push" });
+        return this.git({ "push", "origin", "HEAD" });
         
     }
+    
+    
+    
      /**
      * git:
      * The meaty part.. run spawn.. with git..
@@ -181,17 +414,16 @@ public class GitRepo : Object
      *
      */
     
-    public string git(string[] args_in, string[] env = {}) throws Error, SpawnError
+    public string git(string[] args_in ) throws Error, SpawnError
     {
         // convert arguments.
         
-
         string[]  args = { "git" };
-        args +=  "--git-dir";
-        args +=  this.gitdir;
+        //args +=  "--git-dir";
+        //args +=  this.gitdir;
         args +=  "--no-pager";
-
-
         //if (this.gitdir != this.repopath) {
         //    args +=   "--work-tree";
          //   args += this.repopath; 
@@ -201,27 +433,86 @@ public class GitRepo : Object
         }            
 
         //this.lastCmd = args.join(" ");
-        if(this.debug) {
-         
-            print(  string.joinv (", ", args)); 
-        }
-        
-        env +=  ("HOME=" + Environment.get_home_dir() );
+        //if(this.debug) {
+            GLib.debug( "CWD=%s",  this.git_working_dir ); 
+            GLib.debug( "cmd: %s", string.joinv (" ", args)); 
+        //}
+
+        string[]   env = {};
+        string  home = "HOME=" + Environment.get_home_dir() ;
+        env +=  home ;
         // do not need to set gitpath..
         //if (File.exists(this.repo + '/.git/config')) {
             //env.push("GITPATH=" + this.repo );
         //}
         
 
-        var cfg = new SpawnConfig(this.repopath, args , env);
+        var cfg = new SpawnConfig(this.git_working_dir , args , env);
         
 
        // may throw error...
         var sp = new Spawn(cfg);
-                
-        //print("GOT: " + output)
+      
+
+        GLib.debug( "GOT: %s" , sp.output);
         // parse output for some commands ?
         return sp.output;
     }
+       
+   unowned GitAsyncCallback git_async_on_callback;
+       public void  git_async( string[] args_in,   GitAsyncCallback cb ) throws Error, SpawnError
+    {
+        // convert arguments.
+       this.git_async_on_callback = cb;
+        string[]  args = { "git" };
+        //args +=  "--git-dir";
+        //args +=  this.gitdir;
+        args +=  "--no-pager";
+        //if (this.gitdir != this.repopath) {
+        //    args +=   "--work-tree";
+         //   args += this.repopath; 
+        //}
+        for (var i = 0; i < args_in.length;i++) {
+            args += args_in[i];
+        }            
 
+        //this.lastCmd = args.join(" ");
+        //if(this.debug) {
+            GLib.debug( "CWD=%s",  this.git_working_dir ); 
+            //print( "cmd: %s\n", string.joinv (" ", args)); 
+        //}
+
+        string[]   env = {};
+        string  home = "HOME=" + Environment.get_home_dir() ;
+        env +=  home ;
+        // do not need to set gitpath..
+        //if (File.exists(this.repo + '/.git/config')) {
+            //env.push("GITPATH=" + this.repo );
+        //}
+        
+
+        var cfg = new SpawnConfig(this.git_working_dir , args , env);
+        cfg.async = true;
+       
+
+       // may throw error...
+        var sp = new Spawn(cfg);
+               //sp.ref();
+        //this.ref();
+        sp.run(this.git_async_on_complete); 
+         
+    }
+    
+    void git_async_on_complete(int err, string output)
+    {
+               GLib.debug("GOT %d : %s", err, output);
+               this.git_async_on_callback(this, err, output);
+//             this.unref();   
+       //      sp.unref();             
+    
+    
+    }
+    
 }