revert code that did not affect memory
[gitlive] / GitMonitor.vala
1 /**
2
3 The monitor suffers from various issues - basically event flows while it's running...
4
5 normall operations
6
7 - monitors for file changes
8  -- adds to QUEUE when occurs.
9  
10 - queue runs in background.
11  - if it's got stuff in it..
12  - COMMIT (normally)
13  - now?? - if on master - try and branch
14    == do user selection, until we have branched
15    == then start monitoring again..
16    
17
18
19
20 */
21
22
23
24
25 public class GitMonitor : Monitor
26 {
27
28         public static GitMonitor gitmonitor;
29         /**
30          * @property {String} the "gitlive" directory, normally ~/gitlive
31          *  dset by OWNER... - we should do this as a CTOR.
32          *  
33          */
34         public static string gitlive;
35         
36         
37         private Gee.ArrayList<GitMonitorQueue> queue ;
38         public bool queueRunning = false;
39         
40         public DateTime lastAdd;
41
42  
43
44         public GitMonitor ()
45         {
46         
47                 this.queue = new Gee.ArrayList<GitMonitorQueue>();
48                 GitMonitor.gitmonitor = this;
49                 Timeout.add_full(Priority.LOW, 500, () => {
50
51                         //GLib.debug("TIMEOUT queue length = %d, is_runing = %s\n", (int)this.queue.length , this.queueRunning ? "Y" : "N");
52
53                         //stdout.printf("QL %u: QR: %d\n", this.queue.length, this.queueRunning ? 1 : 0);
54                         if (this.queue.size < 1  || this.queueRunning) {
55                                 return true;
56                         }
57                         var first = this.queue.get(0);
58                         var delay = true;
59                         
60                         // eg. on master.... and is_auto branch
61                         if (first.repo.is_master_branch() && first.repo.is_auto_branch()) {
62                                 delay = false;
63                         }
64                         
65                         
66                         var last = -1 * this.lastAdd.difference(new DateTime.now(new TimeZone.local()));
67  
68                         // stdout.printf("LAST RUN: %s (expect %s) \n" ,
69                         //         last.to_string(),   (5 * TimeSpan.SECOND).to_string() );
70  
71                         if (delay && last < 5 * TimeSpan.SECOND) { // wait 5 seconds before running. ????
72                                 return true;
73                         }
74                         //_this.lastAdd = new Date();
75                         //return 1;
76                 
77                         this.runQueue();
78                         return true; //
79                 });
80                 
81         }
82
83         public new void pauseError(string failure) 
84         {
85         
86                 var notification = new Notify.Notification(
87                           "Git Live ERROR!!",
88                         failure,
89                         "dialog-information"
90                         
91                 );
92                         
93                 notification.set_timeout(60); // show errros for longer
94                 notification.show();
95                 
96         Canberra.Context context;
97         Canberra.Proplist props;
98
99         Canberra.Context.create (out context);
100         Canberra.Proplist.create (out props);
101
102         props.sets (Canberra.PROP_EVENT_ID, "phone-outgoing-busy");
103         props.sets (Canberra.PROP_EVENT_DESCRIPTION, "Gitlive stopped on error");
104  
105
106         context.play_full (0, props, null);
107
108
109                         
110         
111                 this.paused = true;
112                 this.queueRunning = false;
113                 // what does this do to the old one...
114                 //this.queue = new Gee.ArrayList<GitMonitorQueue> ();
115                 this.stop();
116                 StatusIconA.statusicon.pauseError();
117                 
118                 var m = new Gtk.MessageDialog(null, Gtk.DialogFlags.MODAL,Gtk.MessageType.ERROR,Gtk.ButtonsType.CLOSE,
119                         "A Serious problem occured running git, you will probably have to resolve this manually\n" + 
120                         "Committing is paused now, so fix the problem, close this window, then press start again\n\n\n" + 
121                         failure
122                 );
123                 m.set_keep_above(true);
124                 m.show();
125                 m.set_position(Gtk.WindowPosition.CENTER);
126                 m.response.connect( (id_pressed) => {
127                         m.hide();
128                 });
129                 
130         }
131  
132         public new void pause() {
133                 this.paused = true;
134                 // what does this do to the old one...
135                 //this.queue = new Gee.ArrayList<GitMonitorQueue> ();
136                 StatusIconA.statusicon.pause();
137
138         }
139         
140         public void restoreQueue( Gee.ArrayList<GitMonitorQueue> queue)
141         {
142                 //used to restore teh queue after switch branches?/ - breaks our privte queue idea..
143                 this.queue = queue;
144         }
145         
146         /*
147         public new void resume () {
148                 this.paused = false;
149                 this.queue = new Array<GitMonitorQueue> ();
150                 StatusIconA.statusicon.resume();
151                 
152                 
153         }
154         */
155         /**
156          * Start the monitoring
157          * and run the queue every 500 milliseconds..
158          *
159          */
160         public new void start() 
161         {
162                 StatusIconA.statusicon.refreshing();
163                 
164                  
165                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
166                 
167                 Timeout.add_full(Priority.LOW, 500, () => {
168                         //stdout.printf("GitMonitor.start :: top.length = %u\n", this.top.length);
169                         // call this.monitor on each of 'top'
170                         for(int i = 0; i < this.top.length ; i++) {
171
172                                 this.monitor(this.top.index(i) );
173                         }
174                         
175                         
176                         return false; // do not keep doing this..
177
178                 });
179                 
180
181                 
182                  
183         }
184         int scanning = 0;
185         int scancomplete = 0;
186         public void updateMonitorsComplete()
187         {
188                 StatusIconA.statusicon.resume();
189             this.paused = false;
190            
191                 
192                 try {
193
194                   
195                         var notification = new Notify.Notification(
196                                 "Git Live",
197                                 "%s\nMonitoring %u Directories".printf(GitMonitor.gitlive, this.monitors.length), 
198                                  "dialog-information"
199                         );
200         
201                         notification.set_timeout(5);
202                         notification.show();
203                 } catch(Error e) {
204                         GLib.debug("Error sending notification to screen: %s",e.message);
205                 }
206         }
207
208         public new void stop() {
209                 StatusIconA.statusicon.pause();
210                 base.stop();
211         }
212         
213         
214         // this is called recursively until we end..
215         
216         public override void monitor (string path,  int depth = 0)
217         {
218                 
219                 //GLib.debug("GitMonitor : monitor %d %s", depth, path);
220                 //var depth = typeof(depth) == 'number'  ? depth *1 : 0;
221                 
222                 if (depth == 0) {
223                         this.scancomplete = 0;  
224                         this.scanning = 0;      
225                 }
226                  
227                 // if we are not at top level.. and there is a .git directory  (it's a submodule .. ignore) 
228                 if (depth > 1 && FileUtils.test(path + "/.git" , FileTest.IS_DIR)) {
229                         return;
230                 }
231                 
232                 if (depth == 1) {
233                 
234                         if (!FileUtils.test(path + "/.git" , FileTest.IS_DIR)) {
235                                 return; // skip non-git directories..
236                         }
237                         this.scanning++;
238                         GitRepo.get.begin(path, (ojb,res) => {
239                                 
240                                 GitRepo.get.end(res);
241                                 base.monitor(path, depth);
242                                 this.scanning--;
243                                 if (this.scanning == 0 && this.scancomplete == 1) {
244                                         this.updateMonitorsComplete();
245                                 }
246                         });
247                         return;
248                         // FIXME - check if repo is flagged as not autocommit..
249                         //var repo = imports.Scm.Repo.Repo.get(path);
250                         //if (!repo || !repo.autocommit()) {
251                         //    return;
252                         //} 
253                 }
254                 
255                 
256                 // check if the repo is to be monitored.
257                 //print("PATH : " + path);
258                 
259                 
260                 base.monitor(path, depth);
261                 if (depth == 0) {
262                         this.scancomplete = 1;          
263                 }
264                 
265                 
266         }
267
268         
269
270         /**
271          * run the queue.
272          * - pulls the items off the queue 
273          *    (as commands run concurrently and new items may get added while it's running)
274          * - runs the queue items
275          * - pushes upstream.
276          * 
277          */
278         public void runQueue()
279         {
280                 
281                 if (this.paused || this.queue.size < 1 ) {
282                         return;
283                 }
284                 
285                 foreach(var q in this.queue) {
286                         if (!q.shouldIgnore() && q.repo.is_master_branch() && q.repo.is_auto_branch()) {
287                                 var oldq = this.queue;
288                                 this.queue =  new Gee.ArrayList<GitMonitorQueue>(); 
289                                 NewBranch.singleton().show(q.repo, oldq);
290                                 
291                                 return;
292                         }
293                 
294                 }
295                 
296                  
297                 
298                 GLib.debug("GitMonitor.runQueue size =%d\n", this.queue.size);
299
300                 this.queueRunning = true;
301
302                 var cmds = new Gee.ArrayList<GitMonitorQueue>();
303
304                 for(var i = 0; i < this.queue.size; i++) {
305                         cmds.add(this.queue.get(i));
306                 }
307
308                 this.queue = new Gee.ArrayList<GitMonitorQueue>();// empty queue!
309  
310                 string[] success = {};
311                 string[] failure = {};
312            //var repos = new Array<GitRepo>(); //??
313                 //var done = new Array<GitMonitorQueue>();
314                 
315                 // first build a array of repo's to work with
316                 var repo_list = new Array<GitRepo>();
317                 
318                 // pull and group.
319                 
320                 //print(JSON.stringify(cmds));
321                 // make sure nothing get's added to the queue where we are doing this..
322
323                 this.paused = true;
324
325                 var leave_queued = new Gee.ArrayList<GitMonitorQueue>();
326                 GLib.debug("GitMonitor.runQueue - creating repos");
327                 
328                 for(var i = 0; i < cmds.size; i++) {
329                    
330                         var cmd = cmds.get(i);
331                 
332                         var gitpath = cmd.gitpath; 
333                         
334                         var repo = GitRepo.get_sync( gitpath );
335                         if ( repo.is_master_branch() && repo.is_auto_branch()) {
336                                 leave_queued.add(cmd);
337                                 continue;
338                         }
339                         
340                         GLib.debug("GitMonitor.runQueue - finding %s", cmd.gitpath);
341                 
342                         var ix  = GitRepo.indexOf(repo_list,   gitpath);
343                         if (ix < 0) {
344                                 repo_list.append_val( GitRepo.get_sync( gitpath ));
345                                 ix = GitRepo.indexOf(repo_list,  cmd.gitpath);
346                         }
347                         GLib.debug("GitMonitor.runQueue - adding to repolist %d", ix);
348
349                         //if (typeof(repo_list[gitpath]) == 'undefined') {
350                         //    repo_list[gitpath] = new imports.Scm.Git.Repo.Repo( { repopath : gitpath });
351                         //    repo_list[gitpath].cmds = [];
352                          //   repo_list[gitpath].pull();
353                         //}
354                         repo_list.index(ix).cmds.add(cmd);
355
356                 }
357                 this.queue = leave_queued;
358                 
359                 this.paused = false;
360                 // build add, remove and commit message list..
361
362                 GLib.debug("GitMonitor.runQueue - creating actions");
363                 
364                 for(var i = 0;i < repo_list.length;i++) {
365          
366                         var repo = repo_list.index(i);
367
368                         var add_files = new Gee.ArrayList<GitMonitorQueue>();
369                         var add_files_f = new Gee.ArrayList<GitMonitorQueue>();
370                         var remove_files = new Gee.ArrayList<GitMonitorQueue>();
371                         var messages = new Gee.ArrayList<GitMonitorQueue>();
372                         //print(JSON.stringify(repo.cmds,null,4));
373                         
374                         for(var ii = 0;ii < repo.cmds.size;ii++) {
375                                 var cmd = repo.cmds.get(ii);
376         
377                                 if (repo.is_ignore(cmd.vname)) {
378                                         continue;
379                                 }
380         
381                                 
382                                 switch(cmd.action) {
383                                         case "add" :
384                                                 
385                                                 if (GitMonitorQueue.indexOfAdd(add_files, cmd.vname) > -1) {
386                                                    break;
387                                                 }
388                                                 
389                                                 add_files.add(cmd);
390                                                 break;
391                                         
392                                         case "rm":
393                                                 if (GitMonitorQueue.indexOfAdd(remove_files, cmd.vname) > -1 ) {
394                                                    break;
395                                                 }
396                                                 
397                                                 // if file exists, do not try and delete it.
398                                                 if (FileUtils.test(cmd.fullpath(), FileTest.EXISTS)) {
399                                                         break;
400                                                 }
401                                                 
402                                                 remove_files.add(cmd);
403                                                 break;
404                                         
405                                         case "commit" :
406                                                 if (GitMonitorQueue.indexOfMessage(messages, cmd.message) > -1 ) {
407                                                    break;
408                                                 }
409                                                 messages.add(cmd);
410                                                 
411                                                 break;
412                                         default:
413                                                 stdout.printf("Opps unmatched action %s\n", cmd.action);
414                                                 break;
415                                 } 
416                         }
417                         
418                         repo.cmds.clear(); // reset the repo's command list..
419                         
420                         GLib.debug( "ADD : %s", GitMonitorQueue.queueArrayToString(add_files));
421                         GLib.debug( "REMOVE FILES: %s", GitMonitorQueue.queueArrayToString(remove_files));
422                         
423                         //repo.debug = 1;
424                         // these can fail... at present... as we wildcard stuff.
425                    
426                         // make sure added files do not get removed.. ?? 
427                         /*
428                         var remove_files_f = new Array<GitMonitorQueue>();
429                         for(var ii = 0;ii < remove_files.length;ii++) {
430                                 if (GitMonitorQueue.indexOfAdd(add_files,  remove_files.index(ii).vname) > -1 ) {
431                                          continue;
432                                 }
433                                 remove_files_f.append_val(remove_files.index(ii));
434                         };
435                         stdout.printf("REMOVE : %u files\n"  , remove_files_f.length);
436                         */
437                         
438                         // if file was added, then removed, 
439                         var remove_files_f = new Gee.ArrayList<GitMonitorQueue>();
440                         for(var ii = 0;ii < remove_files.size;ii++) {
441                                 
442                                 
443                                 
444                                 if (GitMonitorQueue.indexOfAdd(add_files,  remove_files.get(ii).vname) > -1 ) {
445                                         // in add and remove - do not remvove
446                                         continue;
447                                 }
448                                 remove_files_f.add(remove_files.get(ii));
449                         };
450                         for(var ii = 0;ii < add_files.size;ii++) {
451                                 if (GitMonitorQueue.indexOfAdd(remove_files,  add_files.get(ii).vname) > -1 ) {
452                                         // the add file is in the remove list, and it does not exist - do not add it..
453                                         print("check exists ? %s\n",add_files.get(ii).fullpath());
454                                         
455                                         if (!FileUtils.test(add_files.get(ii).fullpath(), FileTest.EXISTS)) {
456                                                         continue;
457                                         }
458                                          
459                                 } 
460                                 
461                                 add_files_f.add(add_files.get(ii));
462                         };
463                         
464                         GLib.debug( "ADD : %s", GitMonitorQueue.queueArrayToString(add_files_f));
465                         GLib.debug( "REMOVE FILES: %s", GitMonitorQueue.queueArrayToString(remove_files_f));
466                     
467                     if (add_files_f.size < 1 && remove_files_f.size < 1) {
468                                 continue;
469                     }
470                     
471                         // make sure monitoring is paused so it does not recursively pick up
472                         // deletions
473                         this.paused = true; 
474                           
475                           
476                         try {
477                                 
478                                 repo.pull();
479                         } catch(Error e) {
480                                 this.pauseError(e.message);
481                                 //failure +=  e.message;
482                                 //print("Pull failed:\n");
483                                 return;
484                         }
485                                                                         
486                         // -- DO STUFF..            
487                         try {
488                                 repo.add(add_files_f);
489                         } catch(Error e) {
490                                 this.pauseError(e.message);
491                                 return;
492                                 failure +=  e.message;
493                                 GLib.debug("Add failed:");
494                         }  
495                         try {
496                                  repo.remove(remove_files_f);
497                         } catch(Error e) {
498                                 this.pauseError(e.message);
499                                 return;
500                                 failure +=  e.message;
501                                 GLib.debug("Remove failed:");
502                         }  
503  
504                         try { 
505                                 success += repo.commit(
506                                         GitMonitorQueue.messageToString(messages),
507                                         add_files_f
508                                 );
509                                 success += repo.push();
510
511
512                         } catch(Error e) {
513                         
514                                 // if the error is 'nothing to commit, working tree clean'
515                                 // then it's not an error, - just continue;
516                                 if (/nothing to commit, working tree clean/.match(e.message)) {
517                                         GLib.debug("%s",e.message);
518                                         success += e.message;
519                                         this.paused = false; 
520                                         continue;
521                                 }
522                                 
523                         
524                         
525                                 this.paused = false;                    
526                                 this.pauseError(e.message);
527                                 
528                                 return;
529                                 //failure += e.message;
530                                 //print("Push failed:\n");
531                                 
532                         }   
533                         this.paused = false;                                            
534                 }
535                 
536                 
537                 // finally merge all the commit messages.
538                  
539                 try {
540                         // catch notification failures.. so we can carry on..
541                         if (success.length > 0) {
542
543                                 
544                                 var notification = new Notify.Notification(
545                                         "Git Live Commited",
546                                         string.joinv("\n",success),
547                                          "dialog-information"
548                                         
549                                 );
550         
551                                 notification.set_timeout(5);
552                                 notification.show();   
553                         }
554                         
555                         //if (failure.length > 0) {
556
557                                 // should never get this far...
558                         //      this.pauseError();   
559                         //}
560                 } catch(Error e) {
561                         GLib.debug(e.message);
562                         
563                 }
564                 this.queueRunning = false;
565         }
566         
567
568
569         
570
571         //string[] just_created;
572  
573  
574
575
576
577    
578
579
580         public override  void onChanged(MonitorNamePathDir src) 
581         { 
582                 //print("GitMonitor.onChanged\n");        
583                 return; // always ignore this..?
584                 //this.parsePath(src);
585         }
586         
587
588  
589         /**
590          *  results in  git add  + git commit..
591          *
592          */
593         public override void onChangesDoneHint(MonitorNamePathDir src)  
594         { 
595
596                 if (this.paused) {
597                         return;
598                 }
599                 GLib.debug("GitMonitor.onChangedHint");                         
600
601                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
602                 var cmd = new GitMonitorQueue(src);
603                 if (cmd.shouldIgnore()) {
604                         GLib.debug("GitMonitor.onChangedHint - ignored");
605                         return;
606                 }
607                 
608                 //var add_it = false;
609                 /*
610                 if (this.is_just_created(cmd.path)) {
611                         
612                 if (typeof(this.just_created[src.path]) !='undefined') {
613                         delete this.just_created[src.path];
614                         
615                         this.queue.push( 
616                                 [ src.gitpath,  'add', src.vpath ],
617                                 [ src.gitpath,  'commit',    { message: src.vpath} ] 
618                                 
619                         );
620                  
621                         return;
622                 }
623                 */
624                 cmd.action = "add";
625                 this.queue.add(cmd);
626
627                 cmd = new GitMonitorQueue(src);
628                 cmd.action = "commit";
629                 cmd.message = cmd.vname;
630                 this.queue.add(cmd);
631  
632                  
633         }
634         public override  void onDeleted(MonitorNamePathDir src) 
635    { 
636
637                 if (this.paused) {
638                         return;
639                 }
640                 GLib.debug("GitMonitor.onDeleted");                     
641                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
642                 var cmd = new GitMonitorQueue(src);
643                 if (cmd.shouldIgnore()) {
644                         return;
645                 }
646                 // should check if monitor needs removing..
647                 // it should also check if it was a directory.. - so we dont have to commit all..
648                 cmd.action = "rm";
649                 this.queue.add(cmd);
650
651                 cmd = new GitMonitorQueue(src);
652                 cmd.action = "commit";
653                 cmd.message = cmd.vname;
654                 cmd.commit_all = true;
655
656                 this.queue.add(cmd);
657  
658         }
659         public override  void onCreated(MonitorNamePathDir src) {
660
661                 if (this.paused) {
662                         return;
663                 }
664                 GLib.debug("GitMonitor.onCreated");                     
665                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
666                 var cmd = new GitMonitorQueue(src);
667                 if (cmd.shouldIgnore()) {
668                         return;
669                 }
670
671                 if (!FileUtils.test(src.path, GLib.FileTest.IS_DIR)) {
672                    // this.just_created[src.path] = true;
673                         return; // we do not handle file create flags... - use done hint.
674                 }
675                 // directory has bee created
676                 this.monitor(src.path);
677                 //this.top.append_val(src.path);
678                 //this.monitor(src.path );
679
680
681 // -- no point in adding a dir.. as git does not handle them...
682 //        this.queue.push( 
683   //          [ src.gitpath, 'add' , src.vpath,  { all: true } ],
684  //           [ src.gitpath, 'commit' , { all: true, message: src.vpath} ]
685   //          
686    //     );
687
688         }
689
690         public  override void onAttributeChanged(MonitorNamePathDir src) 
691         { 
692
693                 if (this.paused) {
694                         return;
695                 }
696                 GLib.debug("GitMonitor.onAttributeChanged %s", src.name);                       
697                 if (src.dir == GitMonitor.gitlive) {
698                    return; // attribute on top level..
699                 }
700                 
701                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
702                 var cmd = new GitMonitorQueue(src);
703                 if (cmd.shouldIgnore()) {
704                         return;
705                 }
706                 cmd.action = "add";
707                 this.queue.add(cmd);
708
709                 cmd = new GitMonitorQueue(src);
710                 cmd.action = "commit";
711                 cmd.message = "Attribute changed " + cmd.vname;
712                 this.queue.add(cmd);
713         }
714  
715    public  override void onMoved(MonitorNamePathDir src,MonitorNamePathDir dest)  
716         { 
717
718                 if (this.paused) {
719                         return;
720                 }
721                 GLib.debug("GitMonitor.onMoved");                       
722                 this.lastAdd = new DateTime.now(new TimeZone.local()); 
723                 var cmd_s = new GitMonitorQueue(src);
724
725                 var cmd_d = new GitMonitorQueue(dest);
726    
727                 
728                 if (cmd_d.gitpath != cmd_s.gitpath) {
729                         this.onDeleted(src);
730                         this.onCreated(dest);
731                         this.onChangesDoneHint(dest);
732                         return;
733                 }
734                 // needs to handle move to/from unsupported types..
735                 
736                 if (cmd_s.shouldIgnore()) {
737                         this.onCreated(dest);
738                         this.onChangesDoneHint(dest);
739                         return;
740
741                 }
742                 if (cmd_d.shouldIgnore()) {
743                         
744                         this.onDeleted(src);
745                         return;
746                 }
747                  
748                 GLib.debug("RM: %s", cmd_s.vname);
749                 cmd_s.action = "rm";
750                 this.queue.add(cmd_s);
751
752                 
753                 
754                 GLib.debug("ADD: %s", cmd_d.vname);
755                 cmd_d.action = "add";
756                 this.queue.add(cmd_d);
757
758
759                 var cmd = new GitMonitorQueue(dest);
760                 cmd.action = "commit";
761                 cmd.message = "MOVED " + cmd_s.vname + " to " + cmd_d.vname;
762                 if (GitMonitorQueue.queueHas(this.queue, cmd_s, "add")) {
763                         cmd.message = cmd_d.vname;
764                 }
765                 
766                 this.queue.add(cmd);
767
768
769                  
770         }
771            
772 }