[gitg/commit] Implemented stage, unstage, delete and revert on files



commit 610c5bc837b7fb8ded2f5ec2ea3cc120e775d4fd
Author: Jesse van den Kieboom <jessevdk gmail com>
Date:   Sun Jun 30 11:06:56 2013 +0200

    Implemented stage, unstage, delete and revert on files

 libgitg/gitg-stage.vala       |  134 +++++++++++++++++++++++++++++++
 libgitg/tests/test-stage.vala |  174 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 279 insertions(+), 29 deletions(-)
---
diff --git a/libgitg/gitg-stage.vala b/libgitg/gitg-stage.vala
index f03b8d0..b0f6786 100644
--- a/libgitg/gitg-stage.vala
+++ b/libgitg/gitg-stage.vala
@@ -24,12 +24,22 @@ public class Stage : Object
 {
        private Repository d_repository;
        private StageStatusEnumerator ?d_enumerator;
+       private Mutex d_index_mutex;
 
        internal Stage(Repository repository)
        {
                d_repository = repository;
        }
 
+       public async void refresh() throws Error
+       {
+               d_enumerator = null;
+
+               yield thread_index((index) => {
+                       index.read();
+               });
+       }
+
        public StageStatusEnumerator file_status()
        {
                if (d_enumerator == null)
@@ -39,6 +49,130 @@ public class Stage : Object
 
                return d_enumerator;
        }
+
+       private delegate void WithIndexFunc(Ggit.Index index) throws Error;
+
+       private void with_index(WithIndexFunc func)
+       {
+               lock(d_index_mutex)
+               {
+                       try
+                       {
+                               func(d_repository.get_index());
+                       } catch {}
+               }
+       }
+
+       private async void thread_index(WithIndexFunc func) throws Error
+       {
+               yield Async.thread(() => {
+                       with_index(func);
+               });
+       }
+
+       /**
+        * Revert working directory changes.
+        *
+        * @param file the file to revert.
+        *
+        * Revert a file to the version currently recorded in HEAD. This will delete
+        * any modifications done in the current working directory to this file,
+        * so use with care! Note that this only affects the working directory,
+        * not the index.
+        */
+       public async void revert(File file) throws Error
+       {
+               yield thread_index((index) => {
+                       // lookup the tree of HEAD
+                       var head = d_repository.get_head();
+                       var commit = (Ggit.Commit)head.lookup();
+                       var tree = commit.get_tree();
+
+                       // get path relative to the repository working directory
+                       var wd = d_repository.get_workdir();
+                       var path = wd.get_relative_path(file);
+
+                       // get the tree entry of that file
+                       var entry = tree.get_by_path(path);
+                       var id = entry.get_id();
+
+                       // resolve the blob
+                       var blob = d_repository.lookup<Ggit.Blob>(id);
+
+                       var stream = file.replace(null, false, FileCreateFlags.NONE);
+                       stream.write_all(blob.get_raw_content(), null);
+                       stream.close();
+
+                       index.write();
+               });
+       }
+
+       /**
+        * Delete a file from the index.
+        *
+        * @param file the file to delete.
+        *
+        * Delete the file from the index.
+        */
+       public async void @delete(File file) throws Error
+       {
+               yield thread_index((index) => {
+                       index.remove(file, 0);
+                       index.write();
+               });
+       }
+
+       /**
+        * Stage a file to the index.
+        *
+        * @param file the file to stage.
+        *
+        * Stage the file to the index. This will record the state of the file in
+        * the working directory to the index.
+        */
+       public async void stage(File file) throws Error
+       {
+               yield thread_index((index) => {
+                       index.add_file(file);
+                       index.write();
+               });
+       }
+
+       /**
+        * Unstage a file from the index.
+        *
+        * @param file the file to unstage.
+        *
+        * Unstage changes in the specified file from the index. This will record
+        * the state of the file in HEAD to the index.
+        */
+       public async void unstage(File file) throws Error
+       {
+               yield thread_index((index) => {
+                       // lookup the tree of HEAD
+                       var head = d_repository.get_head();
+                       var commit = (Ggit.Commit)head.lookup();
+                       var tree = commit.get_tree();
+
+                       // get path relative to the repository working directory
+                       var wd = d_repository.get_workdir();
+                       var path = wd.get_relative_path(file);
+
+                       // get the tree entry of that file
+                       var entry = tree.get_by_path(path);
+                       var id = entry.get_id();
+
+                       // create a new index entry for the file, pointing to the blob
+                       // from the HEAD tree
+                       var ientry = d_repository.create_index_entry_for_path(path, id);
+
+                       // override file mode since the file might not actually exist.
+                       ientry.set_mode(entry.get_file_mode());
+
+                       index.add(ientry);
+                       index.write();
+               });
+       }
 }
 
 }
diff --git a/libgitg/tests/test-stage.vala b/libgitg/tests/test-stage.vala
index 9faa6e2..659a21d 100644
--- a/libgitg/tests/test-stage.vala
+++ b/libgitg/tests/test-stage.vala
@@ -17,8 +17,13 @@
  * along with gitg. If not, see <http://www.gnu.org/licenses/>.
  */
 
+using Gitg.Test.Assert;
+
 class Gitg.Test.Stage : Gitg.Test.Repository
 {
+       /**
+        * Create basic repository with files in a variety of states.
+        */
        protected override void set_up()
        {
                base.set_up();
@@ -35,50 +40,161 @@ class Gitg.Test.Stage : Gitg.Test.Repository
                index_modify("b", "staged changes\n");
        }
 
-       protected virtual signal void test_index_files()
+       private void check_file_status(MainLoop loop, Gee.HashMap<string, Ggit.StatusFlags> cfiles)
        {
-               /* Test whether the different file statuses created by the set_up()
-                * are properly reported by the stage file status enumerator.
-                */
+               var seen = new Gee.HashSet<string>();
+
+               foreach (var f in cfiles.keys)
+               {
+                       seen.add(f);
+               }
 
                var stage = d_repository.get_stage();
                var e = stage.file_status();
 
-               var loop = new GLib.MainLoop();
-
                e.next_files.begin(-1, (obj, res) => {
                        var files = e.next_files.end(res);
 
-                       assert(files.length == 3);
-
-                       var seen = new Gee.HashSet<string>();
+                       assert(files.length == cfiles.size);
 
                        foreach (var f in files)
                        {
-                               assert(f.path == "a" || f.path == "b" || f.path == "c");
-                               seen.add(f.path);
-
-                               switch (f.path)
-                               {
-                               case "a":
-                                       assert(f.flags == Ggit.StatusFlags.WORKING_TREE_MODIFIED);
-                                       break;
-                               case "b":
-                                       assert(f.flags == (Ggit.StatusFlags.WORKING_TREE_MODIFIED |
-                                                          Ggit.StatusFlags.INDEX_MODIFIED));
-                                       break;
-                               case "c":
-                                       assert(f.flags == Ggit.StatusFlags.WORKING_TREE_DELETED);
-                                       break;
-                               }
-                       }
+                               assert(cfiles.has_key(f.path));
+                               assert_inteq(cfiles[f.path], f.flags);
 
-                       assert("a" in seen);
-                       assert("b" in seen);
-                       assert("c" in seen);
+                               seen.remove(f.path);
+                       }
 
+                       assert(seen.size == 0);
                        loop.quit();
                });
+       }
+
+       /**
+        * Test whether the different file statuses created by the set_up()
+        * are properly reported by the stage file status enumerator.
+        */
+       protected virtual signal void test_file_status()
+       {
+               var m = new Gee.HashMap<string, Ggit.StatusFlags>();
+
+               m["a"] = Ggit.StatusFlags.WORKING_TREE_MODIFIED;
+               m["b"] = Ggit.StatusFlags.WORKING_TREE_MODIFIED | Ggit.StatusFlags.INDEX_MODIFIED;
+               m["c"] = Ggit.StatusFlags.WORKING_TREE_DELETED;
+
+               var loop = new GLib.MainLoop();
+
+               check_file_status(loop, m);
+               loop.run();
+       }
+
+       /**
+        * test staging a complete file in the index.
+        */
+       protected virtual signal void test_stage()
+       {
+               var stage = d_repository.get_stage();
+
+               var f = d_repository.get_workdir().get_child("a");
+               var loop = new MainLoop();
+
+               stage.stage.begin(f, (obj, res) => {
+                       try
+                       {
+                               stage.stage.end(res);
+                       } catch (Error e) { assert_no_error(e); }
+
+                       var m = new Gee.HashMap<string, Ggit.StatusFlags>();
+
+                       m["a"] = Ggit.StatusFlags.INDEX_MODIFIED;
+                       m["b"] = Ggit.StatusFlags.WORKING_TREE_MODIFIED | Ggit.StatusFlags.INDEX_MODIFIED;
+                       m["c"] = Ggit.StatusFlags.WORKING_TREE_DELETED;
+
+                       check_file_status(loop, m);
+               });
+
+               loop.run();
+       }
+
+       /**
+        * test staging a complete file in the index.
+        */
+       protected virtual signal void test_unstage()
+       {
+               var stage = d_repository.get_stage();
+
+               var f = d_repository.get_workdir().get_child("b");
+               var loop = new MainLoop();
+
+               stage.unstage.begin(f, (obj, res) => {
+                       try
+                       {
+                               stage.unstage.end(res);
+                       } catch (Error e) { assert_no_error(e); }
+
+                       var m = new Gee.HashMap<string, Ggit.StatusFlags>();
+
+                       m["a"] = Ggit.StatusFlags.WORKING_TREE_MODIFIED;
+                       m["b"] = Ggit.StatusFlags.WORKING_TREE_MODIFIED;
+                       m["c"] = Ggit.StatusFlags.WORKING_TREE_DELETED;
+
+                       check_file_status(loop, m);
+               });
+
+               loop.run();
+       }
+
+       /**
+        * test reverting a complete file in the index.
+        */
+       protected virtual signal void test_revert()
+       {
+               var stage = d_repository.get_stage();
+
+               var f = d_repository.get_workdir().get_child("a");
+               var loop = new MainLoop();
+
+               stage.revert.begin(f, (obj, res) => {
+                       try
+                       {
+                               stage.revert.end(res);
+                       } catch (Error e) { assert_no_error(e); }
+
+                       var m = new Gee.HashMap<string, Ggit.StatusFlags>();
+
+                       m["b"] = Ggit.StatusFlags.INDEX_MODIFIED | Ggit.StatusFlags.WORKING_TREE_MODIFIED;
+                       m["c"] = Ggit.StatusFlags.WORKING_TREE_DELETED;
+
+                       check_file_status(loop, m);
+               });
+
+               loop.run();
+       }
+
+       /**
+        * test deleting a file in the index.
+        */
+       protected virtual signal void test_delete()
+       {
+               var stage = d_repository.get_stage();
+
+               var f = d_repository.get_workdir().get_child("c");
+               var loop = new MainLoop();
+
+               stage.delete.begin(f, (obj, res) => {
+                       try
+                       {
+                               stage.delete.end(res);
+                       } catch (Error e) { assert_no_error(e); }
+
+                       var m = new Gee.HashMap<string, Ggit.StatusFlags>();
+
+                       m["a"] = Ggit.StatusFlags.WORKING_TREE_MODIFIED;
+                       m["b"] = Ggit.StatusFlags.INDEX_MODIFIED | Ggit.StatusFlags.WORKING_TREE_MODIFIED;
+                       m["c"] = Ggit.StatusFlags.INDEX_DELETED;
+
+                       check_file_status(loop, m);
+               });
 
                loop.run();
        }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]