*
*
*/
+static GitRepo _GitRepo;
+
public class GitRepo : Object
{
-
- public Array<GitMonitorQueue> cmds;
-
+
+ public Gee.ArrayList<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:
*
*
*/
- 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);
+ this.cmds = new Gee.ArrayList<GitMonitorQueue> ();
+ 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;
+
+ }
+ public static void doMerges(string action, string ticket_id, string commit_message)
+ {
+ GitMonitor.gitmonitor.stop();
+
+ var commitrevs = "";
+ var sucess = true;
+ foreach(var repo in GitRepo.singleton().cache.values) {
+ if (repo.activeTicket != null && repo.activeTicket.id == ticket_id) {
+ var res = repo.doMerge(action,ticket_id, commit_message);
+ if (!res) {
+ sucess = false;
+ continue;
+ }
+ commitrevs += commitrevs.length > 0 ? " " : "";
+ commitrevs += repo.currentBranch.lastrev;
+ }
+ }
+ if (sucess && action == "CLOSE") {
+ RooTicket.singleton().getById(ticket_id).close(commitrevs);
+ }
+ GitMonitor.gitmonitor.start();
+ }
+
+
+
+
+ public bool doMerge(string action, string ticket_id, string commit_message)
+ {
+ // in theory we should check to see if other repo's have got the same branch and merge all them at the same time.
+ // also need to decide which branch we will merge into?
+ var ret = "";
+ if (action == "CLOSE" || action == "LEAVE") {
+
+
+ try {
+ var oldbranch = this.currentBranch.name;
+ this.setActiveTicket(null, "master");
+ string [] cmd = { "merge", "--squash", oldbranch };
+ this.git( cmd );
+ cmd = { "commit", "--m", commit_message };
+ this.git( cmd );
+ this.loadBranches(); // updates lastrev..
+
+ var notification = new Notify.Notification(
+ "Merged branch %s to master".printf(oldbranch),
+ "",
+ "dialog-information"
+
+ );
+
+ notification.set_timeout(5);
+ notification.show();
+
+ // close ticket..
+ return true;
+
+ } catch (Error e) {
+
+ GitMonitor.gitmonitor.pauseError(e.message);
+ return false;
+ }
+ // error~?? -- show the error dialog...
+ return false;
+ }
+ if (action == "MASTER") {
+ // merge master into ours..
+ try {
+ string[] cmd = { "merge", "master" };
+ this.git( cmd );
+ var notification = new Notify.Notification(
+ "Merged code from master to %s".printf(this.currentBranch.name),
+ "",
+ "dialog-information"
+
+ );
+ notification.set_timeout(5);
+ notification.show();
+
+ return true;
+ } catch (Error e) {
+ GitMonitor.gitmonitor.pauseError(e.message);
+ return false;
+ }
+ }
+ if (action == "EXIT") {
+ try {
+ var oldbranch = this.currentBranch.name;
+ this.setActiveTicket(null, "master");
+ this.loadBranches();
+ var notification = new Notify.Notification(
+ "Left branch %s".printf(oldbranch),
+ "",
+ "dialog-information"
+
+ );
+ notification.set_timeout(5);
+ notification.show();
+
+ return true;
+ } catch (Error e) {
+ GitMonitor.gitmonitor.pauseError(e.message);
+
+ return false;
+ }
+ // error~?? -- show the error dialog...
+
+ }
+ return false;
+ }
+
+
+ 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)
+ {
+ if (this.branches.has_key(branchname)) {
+ string[] cmd = { "checkout", branchname };
+ this.git(cmd);
+ } else {
+ string[] cmd = { "checkout", "-b" , branchname };
+ this.git(cmd);
+ }
+ this.loadBranches(); // update branch list...
+ GitMonitor.gitmonitor.runQueue(); // commit any outstanding...
+ }
+
+
/**
* add:
* add files to track.
*
* @argument {Array} files the files to add.
*/
- public string add ( Array<GitMonitorQueue> files )
+ public string add ( Gee.ArrayList<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.
- for (var i = 0; i < files.length;i++) {
- var f = files.index(i).vname;
+ var ret = "";
+ for (var i = 0; i < files.size;i++) {
+ var f = files.get(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:
*
* @argument {Array} files the files to add.
*/
- public string remove ( Array<GitMonitorQueue> files )
+ public string remove ( Gee.ArrayList<GitMonitorQueue> files ) throws Error, SpawnError
{
// this may fail if files do not exist..
// should really find out if these are untracked files each..
// 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;
+ for (var i = 0; i < files.size;i++) {
+ var f = files.get(i).vname;
try {
- this.git( { "rm", "-f" } + f );
+ string[] cmd = { "rm", "-f" , f };
+ this.git( cmd );
} catch (Error e) {
ret += e.message + "\n";
}
}
+
/**
* commit:
* perform a commit.
*
*/
- public string commit ( string message, Array<GitMonitorQueue> files )
+ public string commit ( string message, Gee.ArrayList<GitMonitorQueue> files ) throws Error, SpawnError
{
}
*/
- var args = { "commit", "-m", };
- arg += (message.length > 0 ? message : "Changed" );
- for (var i = 0; i< files.length ; i++ ) {
- args += files.index(i).vname; // full path?
+ string[] args = { "commit", "-m" };
+ args += (message.length > 0 ? message : "Changed" );
+ for (var i = 0; i< files.size ; i++ ) {
+ args += files.get(i).vname; // full path?
}
- return this.git(args, env);
+ return this.git(args);
}
/**
* -- 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)
* -- 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..
*
*/
- 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",
- "--git-dir", this.gitdir,
- "--no-pager",
- };
-
-
- if (this.gitdir != this.repopath) {
- args += "--work-tree";
- args += this.repopath;
- }
+ 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) {
-
- print( string.joinv (", ", args));
- }
-
- env += ("HOME=" + Enviroment.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();
+
+
+ }
+
}