[gitg/wip/commit: 13/28] Implemented stage, unstage, delete and revert on files
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg/wip/commit: 13/28] Implemented stage, unstage, delete and revert on files
- Date: Wed, 3 Jul 2013 14:54:35 +0000 (UTC)
commit ecdde804b826c182bb0708937e1d8174999e97b4
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]