summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Martí <tanoku@gmail.com>2012-04-11 12:38:45 +0200
committerVicent Martí <tanoku@gmail.com>2012-04-11 12:38:45 +0200
commitdcfdb958e2033aa59beb624da4263ce031fbb21e (patch)
treef17365bd6314e8318d143a8f2e57659b31037519
parent73fe6a8e20ffbc18ad667ff519c0fb8adf85fc3e (diff)
parentefef3795a2d29f6b99bb9575585bb3fc19c3ed79 (diff)
downloadlibgit2-dcfdb958e2033aa59beb624da4263ce031fbb21e.tar.gz
Merge branch 'new-error-handling' of github.com:libgit2/libgit2 into new-error-handling
-rw-r--r--include/git2.h2
-rw-r--r--include/git2/diff.h4
-rw-r--r--include/git2/status.h73
-rw-r--r--include/git2/submodule.h103
-rw-r--r--src/attr.c75
-rw-r--r--src/attr.h5
-rw-r--r--src/attr_file.c4
-rw-r--r--src/attr_file.h1
-rw-r--r--src/buffer.c24
-rw-r--r--src/config.c78
-rw-r--r--src/config.h5
-rw-r--r--src/config_file.c4
-rw-r--r--src/config_file.h31
-rw-r--r--src/diff.c171
-rw-r--r--src/diff.h9
-rw-r--r--src/diff_output.c6
-rw-r--r--src/fileops.c13
-rw-r--r--src/ignore.c20
-rw-r--r--src/iterator.c28
-rw-r--r--src/odb_loose.c2
-rw-r--r--src/repository.c1
-rw-r--r--src/repository.h11
-rw-r--r--src/status.c205
-rw-r--r--src/submodule.c376
-rw-r--r--src/tree.h2
-rw-r--r--src/vector.h2
-rw-r--r--tests-clar/core/errors.c1
-rw-r--r--tests-clar/diff/iterator.c48
-rw-r--r--tests-clar/refs/unicode.c42
-rw-r--r--tests-clar/status/status_data.h152
-rw-r--r--tests-clar/status/submodules.c117
-rw-r--r--tests-clar/status/worktree.c255
-rw-r--r--tests/resources/attr/.gitted/indexbin1376 -> 1856 bytes
-rw-r--r--tests/resources/attr/.gitted/logs/HEAD2
-rw-r--r--tests/resources/attr/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e04
-rw-r--r--tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432bbin0 -> 180 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4bin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485bin0 -> 81 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1bin0 -> 45 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242bin0 -> 20 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0ebin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596bbin0 -> 171 bytes
-rw-r--r--tests/resources/attr/.gitted/refs/heads/master2
-rw-r--r--tests/resources/issue_592/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/issue_592/.gitted/HEAD1
-rw-r--r--tests/resources/issue_592/.gitted/config8
-rw-r--r--tests/resources/issue_592/.gitted/indexbin0 -> 392 bytes
-rw-r--r--tests/resources/issue_592/.gitted/info/exclude6
-rw-r--r--tests/resources/issue_592/.gitted/logs/HEAD2
-rw-r--r--tests/resources/issue_592/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0ebin0 -> 87 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8bin0 -> 55 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e852
-rw-r--r--tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792bin0 -> 50 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84bin0 -> 107 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21bin0 -> 137 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7bin0 -> 29 bytes
-rw-r--r--tests/resources/issue_592/.gitted/refs/heads/master1
-rw-r--r--tests/resources/issue_592/a.txt1
-rw-r--r--tests/resources/issue_592/c/a.txt1
-rw-r--r--tests/resources/issue_592/l.txt1
-rw-r--r--tests/resources/issue_592/t/a.txt1
-rw-r--r--tests/resources/issue_592/t/b.txt1
-rw-r--r--tests/resources/submodules/.gitted/HEAD1
-rw-r--r--tests/resources/submodules/.gitted/config6
-rw-r--r--tests/resources/submodules/.gitted/description1
-rw-r--r--tests/resources/submodules/.gitted/indexbin0 -> 408 bytes
-rw-r--r--tests/resources/submodules/.gitted/info/exclude8
-rw-r--r--tests/resources/submodules/.gitted/info/refs1
-rw-r--r--tests/resources/submodules/.gitted/logs/HEAD2
-rw-r--r--tests/resources/submodules/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e2
-rw-r--r--tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357bin0 -> 97 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc68503
-rw-r--r--tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888bin0 -> 138 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818bin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4aebin0 -> 120 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/info/packs2
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.packbin0 -> 228 bytes
-rw-r--r--tests/resources/submodules/.gitted/packed-refs2
-rw-r--r--tests/resources/submodules/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodules/added1
-rw-r--r--tests/resources/submodules/gitmodules3
-rw-r--r--tests/resources/submodules/ignored1
-rw-r--r--tests/resources/submodules/modified2
-rw-r--r--tests/resources/submodules/testrepo/.gitted/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/config12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/description1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/indexbin0 -> 256 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/info/exclude6
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/packed-refs12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/README1
-rw-r--r--tests/resources/submodules/testrepo/branch_file.txt2
-rw-r--r--tests/resources/submodules/testrepo/new.txt1
-rw-r--r--tests/resources/submodules/unmodified1
-rw-r--r--tests/resources/submodules/untracked1
143 files changed, 1865 insertions, 145 deletions
diff --git a/include/git2.h b/include/git2.h
index 1711ff8be..7d053c4af 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -40,7 +40,7 @@
#include "git2/net.h"
#include "git2/status.h"
#include "git2/indexer.h"
-
+#include "git2/submodule.h"
#include "git2/notes.h"
#endif
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 0e7c02fd0..0c9f620c1 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -39,7 +39,9 @@ enum {
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
GIT_DIFF_PATIENCE = (1 << 6),
GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
- GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8)
+ GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
+ GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
};
/**
diff --git a/include/git2/status.h b/include/git2/status.h
index 5c45dae1e..f5fc95f0a 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -47,6 +47,79 @@ GIT_BEGIN_DECL
GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload);
/**
+ * Select the files on which to report status.
+ *
+ * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
+ * rough equivalent of `git status --porcelain` where each file
+ * will receive a callback indicating its status in the index and
+ * in the workdir.
+ * - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
+ * side of status. The status of the index contents relative to
+ * the HEAD will be given.
+ * - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
+ * workdir side of status, reporting the status of workdir content
+ * relative to the index.
+ * - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
+ * followed by workdir-only, causing two callbacks to be issued
+ * per file (first index then workdir). This is slightly more
+ * efficient than making separate calls. This makes it easier to
+ * emulate the output of a plain `git status`.
+ */
+typedef enum {
+ GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
+ GIT_STATUS_SHOW_INDEX_ONLY = 1,
+ GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
+ GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3,
+} git_status_show_t;
+
+/**
+ * Flags to control status callbacks
+ *
+ * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should
+ * be made on untracked files. These will only be made if the
+ * workdir files are included in the status "show" option.
+ * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should
+ * get callbacks. Again, these callbacks will only be made if
+ * the workdir files are included in the status "show" option.
+ * Right now, there is no option to include all files in
+ * directories that are ignored completely.
+ * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback
+ * should be made even on unmodified files.
+ * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories
+ * which appear to be submodules should just be skipped over.
+ * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the
+ * contents of untracked directories should be included in the
+ * status. Normally if an entire directory is new, then just
+ * the top-level directory will be included (with a trailing
+ * slash on the entry name). Given this flag, the directory
+ * itself will not be included, but all the files in it will.
+ */
+#define GIT_STATUS_OPT_INCLUDE_UNTRACKED (1 << 0)
+#define GIT_STATUS_OPT_INCLUDE_IGNORED (1 << 1)
+#define GIT_STATUS_OPT_INCLUDE_UNMODIFIED (1 << 2)
+#define GIT_STATUS_OPT_EXCLUDE_SUBMODULES (1 << 3)
+#define GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS (1 << 4)
+
+/**
+ * Options to control how callbacks will be made by
+ * `git_status_foreach_ext()`.
+ */
+typedef struct {
+ git_status_show_t show;
+ unsigned int flags;
+ git_strarray pathspec;
+} git_status_options;
+
+/**
+ * Gather file status information and run callbacks as requested.
+ */
+GIT_EXTERN(int) git_status_foreach_ext(
+ git_repository *repo,
+ git_status_options *opts,
+ int (*callback)(const char *, unsigned int, void *),
+ void *payload);
+
+/**
* Get file status for a single file
*
* @param status_flags the status value
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
new file mode 100644
index 000000000..930168275
--- /dev/null
+++ b/include/git2/submodule.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_submodule_h__
+#define INCLUDE_git_submodule_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/submodule.h
+ * @brief Git submodule management utilities
+ * @defgroup git_submodule Git submodule management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+typedef enum {
+ GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
+ GIT_SUBMODULE_UPDATE_REBASE = 1,
+ GIT_SUBMODULE_UPDATE_MERGE = 2
+} git_submodule_update_t;
+
+typedef enum {
+ GIT_SUBMODULE_IGNORE_ALL = 0, /* never dirty */
+ GIT_SUBMODULE_IGNORE_DIRTY = 1, /* only dirty if HEAD moved */
+ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
+ GIT_SUBMODULE_IGNORE_NONE = 3 /* any change or untracked == dirty */
+} git_submodule_ignore_t;
+
+/**
+ * Description of submodule
+ *
+ * This record describes a submodule found in a repository. There
+ * should be an entry for every submodule found in the HEAD and for
+ * every submodule described in .gitmodules. The fields are as follows:
+ *
+ * - `name` is the name of the submodule from .gitmodules.
+ * - `path` is the path to the submodule from the repo working directory.
+ * It is almost always the same as `name`.
+ * - `url` is the url for the submodule.
+ * - `oid` is the HEAD SHA1 for the submodule.
+ * - `update` is a value from above - see gitmodules(5) update.
+ * - `ignore` is a value from above - see gitmodules(5) ignore.
+ * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
+ * - `refcount` is for internal use.
+ *
+ * If the submodule has been added to .gitmodules but not yet git added,
+ * then the `oid` will be zero. If the submodule has been deleted, but
+ * the delete has not been committed yet, then the `oid` will be set, but
+ * the `url` will be NULL.
+ */
+typedef struct {
+ char *name;
+ char *path;
+ char *url;
+ git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */
+ git_submodule_update_t update;
+ git_submodule_ignore_t ignore;
+ int fetch_recurse;
+ int refcount;
+} git_submodule;
+
+/**
+ * Iterate over all submodules of a repository.
+ *
+ * @param repo The repository
+ * @param callback Function to be called with the name of each submodule.
+ * Return a non-zero value to terminate the iteration.
+ * @param payload Extra data to pass to callback
+ * @return 0 on success, -1 on error, or non-zero return value of callback
+ */
+GIT_EXTERN(int) git_submodule_foreach(
+ git_repository *repo,
+ int (*callback)(const char *name, void *payload),
+ void *payload);
+
+/**
+ * Lookup submodule information by name or path.
+ *
+ * Given either the submodule name or path (they are ususally the same),
+ * this returns a structure describing the submodule. If the submodule
+ * does not exist, this will return GIT_ENOTFOUND and set the submodule
+ * pointer to NULL.
+ *
+ * @param submodule Pointer to submodule description object pointer..
+ * @param repo The repository.
+ * @param name The name of the submodule. Trailing slashes will be ignored.
+ * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error
+ */
+GIT_EXTERN(int) git_submodule_lookup(
+ git_submodule **submodule,
+ git_repository *repo,
+ const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/src/attr.c b/src/attr.c
index 2c5add34f..5cf96acf7 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -196,7 +196,8 @@ bool git_attr_cache__is_cached(git_repository *repo, const char *path)
const char *cache_key = path;
if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
cache_key += strlen(git_repository_workdir(repo));
- return (git_hashtable_lookup(repo->attrcache.files, cache_key) != NULL);
+ return (git_hashtable_lookup(
+ git_repository_attr_cache(repo)->files, cache_key) != NULL);
}
int git_attr_cache__lookup_or_create_file(
@@ -207,7 +208,7 @@ int git_attr_cache__lookup_or_create_file(
git_attr_file **file_ptr)
{
int error;
- git_attr_cache *cache = &repo->attrcache;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
if ((file = git_hashtable_lookup(cache->files, key)) != NULL) {
@@ -293,7 +294,6 @@ static int collect_attr_files(
{
int error;
git_buf dir = GIT_BUF_INIT;
- git_config *cfg;
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info;
@@ -312,7 +312,8 @@ static int collect_attr_files(
* - $GIT_PREFIX/etc/gitattributes
*/
- error = push_attrs(repo, files, repo->path_repository, GIT_ATTR_FILE_INREPO);
+ error = push_attrs(
+ repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
if (error < 0)
goto cleanup;
@@ -322,22 +323,18 @@ static int collect_attr_files(
if (error < 0)
goto cleanup;
- if (!(error = git_repository_config(&cfg, repo))) {
- const char *core_attribs = NULL;
- git_config_get_string(cfg, GIT_ATTR_CONFIG, &core_attribs);
- git_clearerror(); /* don't care if attributesfile is not set */
- if (core_attribs)
- error = push_attrs(repo, files, NULL, core_attribs);
- git_config_free(cfg);
+ if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
+ error = push_attrs(
+ repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
+ if (error < 0)
+ goto cleanup;
}
- if (!error) {
- error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
- if (!error)
- error = push_attrs(repo, files, NULL, dir.ptr);
- else if (error == GIT_ENOTFOUND)
- error = 0;
- }
+ error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
+ if (!error)
+ error = push_attrs(repo, files, NULL, dir.ptr);
+ else if (error == GIT_ENOTFOUND)
+ error = 0;
cleanup:
if (error < 0)
@@ -350,11 +347,26 @@ static int collect_attr_files(
int git_attr_cache__init(git_repository *repo)
{
- git_attr_cache *cache = &repo->attrcache;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_config *cfg;
if (cache->initialized)
return 0;
+ /* cache config settings for attributes and ignores */
+ if (git_repository_config(&cfg, repo) < 0)
+ return -1;
+ if (git_config_get_string(cfg, GIT_ATTR_CONFIG, &cache->cfg_attr_file)) {
+ giterr_clear();
+ cache->cfg_attr_file = NULL;
+ }
+ if (git_config_get_string(cfg, GIT_IGNORE_CONFIG, &cache->cfg_excl_file)) {
+ giterr_clear();
+ cache->cfg_excl_file = NULL;
+ }
+ git_config_free(cfg);
+
+ /* allocate hashtable for attribute and ignore file contents */
if (cache->files == NULL) {
cache->files = git_hashtable_alloc(
8, git_hash__strhash_cb, git_hash__strcmp_cb);
@@ -362,6 +374,7 @@ int git_attr_cache__init(git_repository *repo)
return -1;
}
+ /* allocate hashtable for attribute macros */
if (cache->macros == NULL) {
cache->macros = git_hashtable_alloc(
8, git_hash__strhash_cb, git_hash__strcmp_cb);
@@ -378,30 +391,30 @@ int git_attr_cache__init(git_repository *repo)
void git_attr_cache_flush(
git_repository *repo)
{
+ git_hashtable *table;
+
if (!repo)
return;
- if (repo->attrcache.files) {
+ if ((table = git_repository_attr_cache(repo)->files) != NULL) {
git_attr_file *file;
- GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.files, file,
- git_attr_file__free(file));
+ GIT_HASHTABLE_FOREACH_VALUE(table, file, git_attr_file__free(file));
+ git_hashtable_free(table);
- git_hashtable_free(repo->attrcache.files);
- repo->attrcache.files = NULL;
+ git_repository_attr_cache(repo)->files = NULL;
}
- if (repo->attrcache.macros) {
+ if ((table = git_repository_attr_cache(repo)->macros) != NULL) {
git_attr_rule *rule;
- GIT_HASHTABLE_FOREACH_VALUE(repo->attrcache.macros, rule,
- git_attr_rule__free(rule));
+ GIT_HASHTABLE_FOREACH_VALUE(table, rule, git_attr_rule__free(rule));
+ git_hashtable_free(table);
- git_hashtable_free(repo->attrcache.macros);
- repo->attrcache.macros = NULL;
+ git_repository_attr_cache(repo)->macros = NULL;
}
- repo->attrcache.initialized = 0;
+ git_repository_attr_cache(repo)->initialized = 0;
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
@@ -411,5 +424,5 @@ int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
return 0;
return git_hashtable_insert(
- repo->attrcache.macros, macro->match.pattern, macro);
+ git_repository_attr_cache(repo)->macros, macro->match.pattern, macro);
}
diff --git a/src/attr.h b/src/attr.h
index eccda0ed7..350c0ebad 100644
--- a/src/attr.h
+++ b/src/attr.h
@@ -9,10 +9,15 @@
#include "attr_file.h"
+#define GIT_ATTR_CONFIG "core.attributesfile"
+#define GIT_IGNORE_CONFIG "core.excludesfile"
+
typedef struct {
int initialized;
git_hashtable *files; /* hash path to git_attr_file of rules */
git_hashtable *macros; /* hash name to vector<git_attr_assignment> */
+ const char *cfg_attr_file; /* cached value of core.attributesfile */
+ const char *cfg_excl_file; /* cached value of core.excludesfile */
} git_attr_cache;
extern int git_attr_cache__init(git_repository *repo);
diff --git a/src/attr_file.c b/src/attr_file.c
index 646bd044c..6568313e5 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -495,8 +495,8 @@ int git_attr_assignment__parse(
/* expand macros (if given a repo with a macro cache) */
if (repo != NULL && assign->value == git_attr__true) {
- git_attr_rule *macro =
- git_hashtable_lookup(repo->attrcache.macros, assign->name);
+ git_attr_rule *macro = git_hashtable_lookup(
+ git_repository_attr_cache(repo)->macros, assign->name);
if (macro != NULL) {
unsigned int i;
diff --git a/src/attr_file.h b/src/attr_file.h
index 6284c5386..53e479ad9 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -14,7 +14,6 @@
#define GIT_ATTR_FILE ".gitattributes"
#define GIT_ATTR_FILE_INREPO "info/attributes"
#define GIT_ATTR_FILE_SYSTEM "gitattributes"
-#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
diff --git a/src/buffer.c b/src/buffer.c
index ec0302b9a..c23803564 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -256,8 +256,8 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
{
va_list ap;
int i;
- size_t total_size = 0;
- char *out;
+ size_t total_size = 0, original_size = buf->size;
+ char *out, *original = buf->ptr;
if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
++total_size; /* space for initial separator */
@@ -281,8 +281,9 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
va_end(ap);
/* expand buffer if needed */
- if (total_size > 0 &&
- git_buf_grow(buf, buf->size + total_size + 1) < 0)
+ if (total_size == 0)
+ return 0;
+ if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
return -1;
out = buf->ptr + buf->size;
@@ -300,12 +301,23 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
if (!segment)
continue;
+ /* deal with join that references buffer's original content */
+ if (segment >= original && segment < original + original_size) {
+ size_t offset = (segment - original);
+ segment = buf->ptr + offset;
+ segment_len = original_size - offset;
+ } else {
+ segment_len = strlen(segment);
+ }
+
/* skip leading separators */
if (out > buf->ptr && out[-1] == separator)
- while (*segment == separator) segment++;
+ while (segment_len > 0 && *segment == separator) {
+ segment++;
+ segment_len--;
+ }
/* copy over next buffer */
- segment_len = strlen(segment);
if (segment_len > 0) {
memmove(out, segment, segment_len);
out += segment_len;
diff --git a/src/config.c b/src/config.c
index 77598d6a6..250bfa652 100644
--- a/src/config.c
+++ b/src/config.c
@@ -140,7 +140,7 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
{
- int ret = GIT_SUCCESS;
+ int ret = 0;
unsigned int i;
file_internal *internal;
git_config_file *file;
@@ -201,25 +201,25 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
-static int parse_bool(int *out, const char *value)
+int git_config_parse_bool(int *out, const char *value)
{
/* A missing value means true */
if (value == NULL) {
*out = 1;
- return GIT_SUCCESS;
+ return 0;
}
if (!strcasecmp(value, "true") ||
!strcasecmp(value, "yes") ||
!strcasecmp(value, "on")) {
*out = 1;
- return GIT_SUCCESS;
+ return 0;
}
if (!strcasecmp(value, "false") ||
!strcasecmp(value, "no") ||
!strcasecmp(value, "off")) {
*out = 0;
- return GIT_SUCCESS;
+ return 0;
}
return GIT_EINVALIDTYPE;
@@ -283,46 +283,58 @@ static int parse_int32(int32_t *out, const char *value)
/***********
* Getters
***********/
-int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
+int git_config_lookup_map_value(
+ git_cvar_map *maps, size_t map_n, const char *value, int *out)
{
size_t i;
- const char *value;
- int error;
- error = git_config_get_string(cfg, name, &value);
- if (error < GIT_SUCCESS)
- return error;
+ if (!value)
+ return GIT_ENOTFOUND;
for (i = 0; i < map_n; ++i) {
git_cvar_map *m = maps + i;
switch (m->cvar_type) {
- case GIT_CVAR_FALSE:
- case GIT_CVAR_TRUE: {
- int bool_val;
+ case GIT_CVAR_FALSE:
+ case GIT_CVAR_TRUE: {
+ int bool_val;
+
+ if (git_config_parse_bool(&bool_val, value) == 0 &&
+ bool_val == (int)m->cvar_type) {
+ *out = m->map_value;
+ return 0;
+ }
+ break;
+ }
- if (parse_bool(&bool_val, value) == 0 &&
- bool_val == (int)m->cvar_type) {
- *out = m->map_value;
- return 0;
- }
+ case GIT_CVAR_INT32:
+ if (parse_int32(out, value) == 0)
+ return 0;
+ break;
- break;
+ case GIT_CVAR_STRING:
+ if (strcasecmp(value, m->str_match) == 0) {
+ *out = m->map_value;
+ return 0;
}
+ break;
+ }
+ }
- case GIT_CVAR_INT32:
- if (parse_int32(out, value) == 0)
- return 0;
+ return GIT_ENOTFOUND;
+}
- break;
+int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
+{
+ const char *value;
+ int error;
- case GIT_CVAR_STRING:
- if (strcasecmp(value, m->str_match) == 0) {
- *out = m->map_value;
- return 0;
- }
- }
- }
+ error = git_config_get_string(cfg, name, &value);
+ if (error < 0)
+ return error;
+
+ if (!git_config_lookup_map_value(maps, map_n, value, out))
+ return 0;
giterr_set(GITERR_CONFIG,
"Failed to map the '%s' config variable with a valid value", name);
@@ -372,7 +384,7 @@ int git_config_get_bool(git_config *cfg, const char *name, int *out)
if (ret < 0)
return ret;
- if (parse_bool(out, value) == 0)
+ if (git_config_parse_bool(out, value) == 0)
return 0;
if (parse_int32(out, value) == 0) {
@@ -449,7 +461,7 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
internal = git_vector_get(&cfg->files, i - 1);
file = internal->file;
ret = file->set_multivar(file, name, regexp, value);
- if (ret < GIT_SUCCESS && ret != GIT_ENOTFOUND)
+ if (ret < 0 && ret != GIT_ENOTFOUND)
return ret;
}
diff --git a/src/config.h b/src/config.h
index 59d1d9a26..82e98ce51 100644
--- a/src/config.h
+++ b/src/config.h
@@ -25,4 +25,9 @@ struct git_config {
extern int git_config_find_global_r(git_buf *global_config_path);
extern int git_config_find_system_r(git_buf *system_config_path);
+extern int git_config_parse_bool(int *out, const char *bool_string);
+
+extern int git_config_lookup_map_value(
+ git_cvar_map *maps, size_t map_n, const char *value, int *out);
+
#endif
diff --git a/src/config_file.c b/src/config_file.c
index 077e2c03f..e16606512 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -192,6 +192,9 @@ static int file_foreach(git_config_file *backend, int (*fn)(const char *, const
cvar_t *var;
const char *key;
+ if (!b->values)
+ return 0;
+
GIT_HASHTABLE_FOREACH(b->values, key, var,
do {
if (fn(key, var->value, data) < 0)
@@ -331,6 +334,7 @@ static int config_get_multivar(
var = var->next;
} while (var != NULL);
+ regfree(&regex);
} else {
/* no regex; go through all the variables */
do {
diff --git a/src/config_file.h b/src/config_file.h
new file mode 100644
index 000000000..0080b5713
--- /dev/null
+++ b/src/config_file.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_config_file_h__
+#define INCLUDE_config_file_h__
+
+#include "git2/config.h"
+
+GIT_INLINE(int) git_config_file_open(git_config_file *cfg)
+{
+ return cfg->open(cfg);
+}
+
+GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
+{
+ cfg->free(cfg);
+}
+
+GIT_INLINE(int) git_config_file_foreach(
+ git_config_file *cfg,
+ int (*fn)(const char *key, const char *value, void *data),
+ void *data)
+{
+ return cfg->foreach(cfg, fn, data);
+}
+
+#endif
+
diff --git a/src/diff.c b/src/diff.c
index 69c944c63..54e8dd166 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -8,6 +8,7 @@
#include "git2/diff.h"
#include "diff.h"
#include "fileops.h"
+#include "config.h"
static void diff_delta__free(git_diff_delta *delta)
{
@@ -132,7 +133,17 @@ static int diff_delta__from_one(
git_delta_t status,
const git_index_entry *entry)
{
- git_diff_delta *delta = diff_delta__alloc(diff, status, entry->path);
+ git_diff_delta *delta;
+
+ if (status == GIT_DELTA_IGNORED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return 0;
+
+ if (status == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return 0;
+
+ delta = diff_delta__alloc(diff, status, entry->path);
GITERR_CHECK_ALLOC(delta);
/* This fn is just for single-sided diffs */
@@ -168,6 +179,10 @@ static int diff_delta__from_two(
{
git_diff_delta *delta;
+ if (status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return 0;
+
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
const git_index_entry *temp = old;
old = new;
@@ -219,15 +234,38 @@ static int diff_delta__cmp(const void *a, const void *b)
return val ? val : ((int)da->status - (int)db->status);
}
+static int config_bool(git_config *cfg, const char *name, int defvalue)
+{
+ int val = defvalue;
+ if (git_config_get_bool(cfg, name, &val) < 0)
+ giterr_clear();
+ return val;
+}
+
static git_diff_list *git_diff_list_alloc(
git_repository *repo, const git_diff_options *opts)
{
+ git_config *cfg;
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
if (diff == NULL)
return NULL;
diff->repo = repo;
+ /* load config values that affect diff behavior */
+ if (git_repository_config(&cfg, repo) < 0)
+ goto fail;
+ if (config_bool(cfg, "core.symlinks", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
+ if (config_bool(cfg, "core.ignorestat", 0))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
+ if (config_bool(cfg, "core.filemode", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
+ if (config_bool(cfg, "core.trustctime", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
+ /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
+ git_config_free(cfg);
+
if (opts == NULL)
return diff;
@@ -238,10 +276,8 @@ static git_diff_list *git_diff_list_alloc(
diff->opts.dst_prefix = diff_strdup_prefix(
opts->dst_prefix ? opts->dst_prefix : DIFF_DST_PREFIX_DEFAULT);
- if (!diff->opts.src_prefix || !diff->opts.dst_prefix) {
- git__free(diff);
- return NULL;
- }
+ if (!diff->opts.src_prefix || !diff->opts.dst_prefix)
+ goto fail;
if (diff->opts.flags & GIT_DIFF_REVERSE) {
char *swap = diff->opts.src_prefix;
@@ -249,16 +285,19 @@ static git_diff_list *git_diff_list_alloc(
diff->opts.dst_prefix = swap;
}
- if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0) {
- git__free(diff->opts.src_prefix);
- git__free(diff->opts.dst_prefix);
- git__free(diff);
- return NULL;
- }
+ if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0)
+ goto fail;
/* TODO: do something safe with the pathspec strarray */
return diff;
+
+fail:
+ git_vector_free(&diff->deltas);
+ git__free(diff->opts.src_prefix);
+ git__free(diff->opts.dst_prefix);
+ git__free(diff);
+ return NULL;
}
void git_diff_list_free(git_diff_list *diff)
@@ -312,6 +351,8 @@ static int oid_for_workdir_item(
return result;
}
+#define EXEC_BIT_MASK 0000111
+
static int maybe_modified(
git_iterator *old,
const git_index_entry *oitem,
@@ -320,53 +361,92 @@ static int maybe_modified(
git_diff_list *diff)
{
git_oid noid, *use_noid = NULL;
+ git_delta_t status = GIT_DELTA_MODIFIED;
+ unsigned int omode = oitem->mode;
+ unsigned int nmode = nitem->mode;
GIT_UNUSED(old);
- /* support "assume unchanged" & "skip worktree" bits */
- if ((oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) != 0 ||
- (oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
- return 0;
+ /* on platforms with no symlinks, promote plain files to symlinks */
+ if (S_ISLNK(omode) && S_ISREG(nmode) &&
+ !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
+ nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
+
+ /* on platforms with no execmode, clear exec bit from comparisons */
+ if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
+ omode = omode & ~EXEC_BIT_MASK;
+ nmode = nmode & ~EXEC_BIT_MASK;
+ }
+
+ /* support "assume unchanged" (badly, b/c we still stat everything) */
+ if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
+ status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
+ GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
- if (GIT_MODE_TYPE(oitem->mode) != GIT_MODE_TYPE(nitem->mode)) {
+ /* support "skip worktree" index bit */
+ else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if basic type of file changed, then split into delete and add */
+ else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
return -1;
return 0;
}
- if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
- oitem->mode == nitem->mode)
- return 0;
+ /* if oids and modes match, then file is unmodified */
+ else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if we have a workdir item with an unknown oid, check deeper */
+ else if (git_oid_iszero(&nitem->oid) && new->type == GIT_ITERATOR_WORKDIR) {
+ /* TODO: add check against index file st_mtime to avoid racy-git */
- if (git_oid_iszero(&nitem->oid) && new->type == GIT_ITERATOR_WORKDIR) {
/* if they files look exactly alike, then we'll assume the same */
if (oitem->file_size == nitem->file_size &&
- oitem->ctime.seconds == nitem->ctime.seconds &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
+ (oitem->ctime.seconds == nitem->ctime.seconds)) &&
oitem->mtime.seconds == nitem->mtime.seconds &&
- oitem->dev == nitem->dev &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
+ (oitem->dev == nitem->dev)) &&
oitem->ino == nitem->ino &&
oitem->uid == nitem->uid &&
oitem->gid == nitem->gid)
- return 0;
+ status = GIT_DELTA_UNMODIFIED;
+
+ else if (S_ISGITLINK(nmode)) {
+ git_submodule *sub;
+
+ if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+ else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
+ return -1;
+ else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
+ status = GIT_DELTA_UNMODIFIED;
+ else {
+ /* TODO: support other GIT_SUBMODULE_IGNORE values */
+ status = GIT_DELTA_UNMODIFIED;
+ }
+ }
/* TODO: check git attributes so we will not have to read the file
* in if it is marked binary.
*/
- if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
+ else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
return -1;
- if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
- oitem->mode == nitem->mode)
- return 0;
+ else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
/* store calculated oid so we don't have to recalc later */
use_noid = &noid;
}
- return diff_delta__from_two(
- diff, GIT_DELTA_MODIFIED, oitem, nitem, use_noid);
+ return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
}
static int diff_from_iterators(
@@ -418,7 +498,12 @@ static int diff_from_iterators(
is_ignored = git_iterator_current_is_ignored(new);
if (S_ISDIR(nitem->mode)) {
- if (git__prefixcmp(oitem->path, nitem->path) == 0) {
+ /* recurse into directory if explicitly requested or
+ * if there are tracked items inside the directory
+ */
+ if ((diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) ||
+ (oitem && git__prefixcmp(oitem->path, nitem->path) == 0))
+ {
if (is_ignored)
ignore_prefix = nitem->path;
if (git_iterator_advance_into_directory(new, &nitem) < 0)
@@ -538,23 +623,27 @@ int git_diff_merge(
const git_diff_list *from)
{
int error = 0;
- unsigned int i = 0, j = 0;
git_vector onto_new;
git_diff_delta *delta;
+ unsigned int i, j;
+
+ assert(onto && from);
+
+ if (!from->deltas.length)
+ return 0;
if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0)
return -1;
- while (!error && (i < onto->deltas.length || j < from->deltas.length)) {
- git_diff_delta *o = git_vector_get(&onto->deltas, i);
- const git_diff_delta *f = git_vector_get_const(&from->deltas, j);
- const char *opath = !o ? NULL : o->old.path ? o->old.path : o->new.path;
- const char *fpath = !f ? NULL : f->old.path ? f->old.path : f->new.path;
+ for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
+ git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
+ const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
+ int cmp = !f ? -1 : !o ? 1 : strcmp(o->old.path, f->old.path);
- if (opath && (!fpath || strcmp(opath, fpath) < 0)) {
+ if (cmp < 0) {
delta = diff_delta__dup(o);
i++;
- } else if (fpath && (!opath || strcmp(opath, fpath) > 0)) {
+ } else if (cmp > 0) {
delta = diff_delta__dup(f);
j++;
} else {
@@ -563,10 +652,11 @@ int git_diff_merge(
j++;
}
- error = !delta ? -1 : git_vector_insert(&onto_new, delta);
+ if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
+ break;
}
- if (error == 0) {
+ if (!error) {
git_vector_swap(&onto->deltas, &onto_new);
onto->new_src = from->new_src;
}
@@ -577,3 +667,4 @@ int git_diff_merge(
return error;
}
+
diff --git a/src/diff.h b/src/diff.h
index 7d69199ea..b4a375586 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -13,12 +13,21 @@
#include "iterator.h"
#include "repository.h"
+enum {
+ GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
+ GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
+ GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */
+ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
+ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
+};
+
struct git_diff_list {
git_repository *repo;
git_diff_options opts;
git_vector deltas; /* vector of git_diff_file_delta */
git_iterator_type_t old_src;
git_iterator_type_t new_src;
+ uint32_t diffcaps;
};
#endif
diff --git a/src/diff_output.c b/src/diff_output.c
index 638cabca5..f4c214314 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -314,7 +314,8 @@ int git_diff_foreach(
git_blob *old_blob = NULL, *new_blob = NULL;
git_map old_data, new_data;
- if (delta->status == GIT_DELTA_UNMODIFIED)
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
continue;
if (delta->status == GIT_DELTA_IGNORED &&
@@ -377,7 +378,8 @@ int git_diff_foreach(
*/
if (git_oid_cmp(&delta->old.oid, &delta->new.oid) == 0) {
delta->status = GIT_DELTA_UNMODIFIED;
- goto cleanup;
+ if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ goto cleanup;
}
}
}
diff --git a/src/fileops.c b/src/fileops.c
index f1f820ab7..b3bb3890e 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -62,7 +62,18 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode
int git_futils_creat_locked(const char *path, const mode_t mode)
{
- int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+ int fd;
+
+#ifdef GIT_WIN32
+ wchar_t* buf;
+
+ buf = gitwin_to_utf16(path);
+ fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+ git__free(buf);
+#else
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+#endif
+
if (fd < 0) {
giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
return -1;
diff --git a/src/ignore.c b/src/ignore.c
index be00efd1b..1827eda82 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -1,11 +1,9 @@
#include "ignore.h"
#include "path.h"
-#include "git2/config.h"
#define GIT_IGNORE_INTERNAL "[internal]exclude"
#define GIT_IGNORE_FILE_INREPO "info/exclude"
#define GIT_IGNORE_FILE ".gitignore"
-#define GIT_IGNORE_CONFIG "core.excludesfile"
static int load_ignore_file(
git_repository *repo, const char *path, git_attr_file *ignores)
@@ -73,7 +71,6 @@ static int push_one_ignore(void *ref, git_buf *path)
int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ignores)
{
int error = 0;
- git_config *cfg;
const char *workdir = git_repository_workdir(repo);
assert(ignores);
@@ -104,26 +101,19 @@ int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ig
/* load .git/info/exclude */
error = push_ignore(repo, &ignores->ign_global,
- repo->path_repository, GIT_IGNORE_FILE_INREPO);
+ git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
if (error < 0)
goto cleanup;
/* load core.excludesfile */
- if ((error = git_repository_config(&cfg, repo)) == 0) {
- const char *core_ignore;
- error = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &core_ignore);
- if (error == 0 && core_ignore != NULL)
- error = push_ignore(repo, &ignores->ign_global, NULL, core_ignore);
- else {
- error = 0;
- giterr_clear(); /* don't care if attributesfile is not set */
- }
- git_config_free(cfg);
- }
+ if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
+ error = push_ignore(repo, &ignores->ign_global, NULL,
+ git_repository_attr_cache(repo)->cfg_excl_file);
cleanup:
if (error < 0)
git_ignore__free(ignores);
+
return error;
}
diff --git a/src/iterator.c b/src/iterator.c
index cc15b5f67..3a3be1755 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -9,6 +9,7 @@
#include "tree.h"
#include "ignore.h"
#include "buffer.h"
+#include "git2/submodule.h"
typedef struct tree_iterator_frame tree_iterator_frame;
struct tree_iterator_frame {
@@ -121,7 +122,6 @@ static int tree_iterator__advance(
break;
tree_iterator__pop_frame(ti);
- git_buf_rtruncate_at_char(&ti->path, '/');
}
if (te && entry_is_tree(te))
@@ -425,9 +425,25 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
return 0; /* if error, ignore it and ignore file */
/* detect submodules */
- if (S_ISDIR(wi->entry.mode) &&
- git_path_contains(&wi->path, DOT_GIT) == true)
- wi->entry.mode = S_IFGITLINK;
+ if (S_ISDIR(wi->entry.mode)) {
+ bool is_submodule = git_path_contains(&wi->path, DOT_GIT);
+
+ /* if there is no .git, still check submodules data */
+ if (!is_submodule) {
+ int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
+ is_submodule = (res == 0);
+ if (res == GIT_ENOTFOUND)
+ giterr_clear();
+ }
+
+ /* if submodule, mark as GITLINK and remove trailing slash */
+ if (is_submodule) {
+ size_t len = strlen(wi->entry.path);
+ assert(wi->entry.path[len - 1] == '/');
+ wi->entry.path[len - 1] = '\0';
+ wi->entry.mode = S_IFGITLINK;
+ }
+ }
return 0;
}
@@ -485,7 +501,9 @@ int git_iterator_advance_into_directory(
workdir_iterator *wi = (workdir_iterator *)iter;
if (iter->type == GIT_ITERATOR_WORKDIR &&
- wi->entry.path && S_ISDIR(wi->entry.mode))
+ wi->entry.path &&
+ S_ISDIR(wi->entry.mode) &&
+ !S_ISGITLINK(wi->entry.mode))
{
if (workdir_iterator__expand_dir(wi) < 0)
/* if error loading or if empty, skip the directory. */
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 085df428a..b593d1846 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -772,7 +772,7 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
{
- int error, header_len;
+ int error = 0, header_len;
git_buf final_path = GIT_BUF_INIT;
char header[64];
git_filebuf fbuf = GIT_FILEBUF_INIT;
diff --git a/src/repository.c b/src/repository.c
index 45bedcbe0..4e0f9d491 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -64,6 +64,7 @@ void git_repository_free(git_repository *repo)
git_cache_free(&repo->objects);
git_repository__refcache_free(&repo->references);
git_attr_cache_flush(repo);
+ git_submodule_config_free(repo);
git__free(repo->path_repository);
git__free(repo->workdir);
diff --git a/src/repository.h b/src/repository.h
index b5dcc1340..178f29742 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -83,6 +83,7 @@ struct git_repository {
git_cache objects;
git_refcache references;
git_attr_cache attrcache;
+ git_hashtable *submodules;
char *path_repository;
char *workdir;
@@ -100,6 +101,11 @@ void git_object__free(void *object);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
+GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
+{
+ return &repo->attrcache;
+}
+
/*
* Weak pointers to repository internals.
*
@@ -120,4 +126,9 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo);
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
void git_repository__cvar_cache_clear(git_repository *repo);
+/*
+ * Submodule cache
+ */
+extern void git_submodule_config_free(git_repository *repo);
+
#endif
diff --git a/src/status.c b/src/status.c
index 2221db3d9..7cd914f21 100644
--- a/src/status.c
+++ b/src/status.c
@@ -15,6 +15,209 @@
#include "repository.h"
#include "ignore.h"
+#include "git2/diff.h"
+#include "diff.h"
+
+static int resolve_head_to_tree(git_tree **tree, git_repository *repo)
+{
+ git_reference *head = NULL;
+ git_object *obj = NULL;
+
+ if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
+ return -1;
+
+ if (git_reference_oid(head) == NULL) {
+ git_reference *resolved;
+
+ if (git_reference_resolve(&resolved, head) < 0) {
+ /* cannot resolve HEAD - probably brand new repo */
+ giterr_clear();
+ git_reference_free(head);
+ return GIT_ENOTFOUND;
+ }
+
+ git_reference_free(head);
+ head = resolved;
+ }
+
+ if (git_object_lookup(&obj, repo, git_reference_oid(head), GIT_OBJ_ANY) < 0)
+ goto fail;
+
+ git_reference_free(head);
+
+ switch (git_object_type(obj)) {
+ case GIT_OBJ_TREE:
+ *tree = (git_tree *)obj;
+ break;
+ case GIT_OBJ_COMMIT:
+ if (git_commit_tree(tree, (git_commit *)obj) < 0)
+ goto fail;
+ git_object_free(obj);
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ git_object_free(obj);
+ git_reference_free(head);
+ return -1;
+}
+
+static unsigned int index_delta2status(git_delta_t index_status)
+{
+ unsigned int st = GIT_STATUS_CURRENT;
+
+ switch (index_status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ st = GIT_STATUS_INDEX_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_INDEX_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_INDEX_MODIFIED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+static unsigned int workdir_delta2status(git_delta_t workdir_status)
+{
+ unsigned int st = GIT_STATUS_CURRENT;
+
+ switch (workdir_status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ case GIT_DELTA_UNTRACKED:
+ st = GIT_STATUS_WT_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_WT_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_WT_MODIFIED;
+ break;
+ case GIT_DELTA_IGNORED:
+ st = GIT_STATUS_IGNORED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+int git_status_foreach_ext(
+ git_repository *repo,
+ git_status_options *opts,
+ int (*cb)(const char *, unsigned int, void *),
+ void *cbdata)
+{
+ int err = 0, cmp;
+ git_diff_options diffopt;
+ git_diff_list *idx2head = NULL, *wd2idx = NULL;
+ git_tree *head = NULL;
+ git_status_show_t show =
+ opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ git_diff_delta *i2h, *w2i;
+ unsigned int i, j, i_max, j_max;
+
+ assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+
+ switch (resolve_head_to_tree(&head, repo)) {
+ case 0: break;
+ case GIT_ENOTFOUND: return 0;
+ default: return -1;
+ }
+
+ memset(&diffopt, 0, sizeof(diffopt));
+ memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
+
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
+ if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ /* TODO: support EXCLUDE_SUBMODULES flag */
+
+ if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
+ (err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
+ goto cleanup;
+
+ if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
+ (err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
+ goto cleanup;
+
+ if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
+ for (i = 0; !err && i < idx2head->deltas.length; i++) {
+ i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
+ err = cb(i2h->old.path, index_delta2status(i2h->status), cbdata);
+ }
+ git_diff_list_free(idx2head);
+ idx2head = NULL;
+ }
+
+ i_max = idx2head ? idx2head->deltas.length : 0;
+ j_max = wd2idx ? wd2idx->deltas.length : 0;
+
+ for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
+ i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
+ w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
+
+ cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old.path, w2i->old.path);
+
+ if (cmp < 0) {
+ err = cb(i2h->old.path, index_delta2status(i2h->status), cbdata);
+ i++;
+ } else if (cmp > 0) {
+ err = cb(w2i->old.path, workdir_delta2status(w2i->status), cbdata);
+ j++;
+ } else {
+ err = cb(i2h->old.path, index_delta2status(i2h->status) |
+ workdir_delta2status(w2i->status), cbdata);
+ i++; j++;
+ }
+ }
+
+cleanup:
+ git_tree_free(head);
+ git_diff_list_free(idx2head);
+ git_diff_list_free(wd2idx);
+ return err;
+}
+
+int git_status_foreach(
+ git_repository *repo,
+ int (*callback)(const char *, unsigned int, void *),
+ void *payload)
+{
+ git_status_options opts;
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ return git_status_foreach_ext(repo, &opts, callback, payload);
+}
+
+
+/*
+ * the old stuff
+ */
+
struct status_entry {
git_index_time mtime;
@@ -461,7 +664,7 @@ static int status_cmp(const void *a, const void *b)
#define DEFAULT_SIZE 16
-int git_status_foreach(
+int git_status_foreach_old(
git_repository *repo,
int (*callback)(const char *, unsigned int, void *),
void *payload)
diff --git a/src/submodule.c b/src/submodule.c
new file mode 100644
index 000000000..907e43e88
--- /dev/null
+++ b/src/submodule.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "git2/config.h"
+#include "git2/types.h"
+#include "git2/repository.h"
+#include "git2/index.h"
+#include "git2/submodule.h"
+#include "buffer.h"
+#include "hashtable.h"
+#include "vector.h"
+#include "posix.h"
+#include "config_file.h"
+#include "config.h"
+#include "repository.h"
+
+static git_cvar_map _sm_update_map[] = {
+ {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
+ {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
+ {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE}
+};
+
+static git_cvar_map _sm_ignore_map[] = {
+ {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+ {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
+ {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
+ {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}
+};
+
+static uint32_t strhash_no_trailing_slash(const void *key, int hash_id)
+{
+ static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
+ 0x01010101,
+ 0x12345678,
+ 0xFEDCBA98
+ };
+
+ size_t key_len = key ? strlen((const char *)key) : 0;
+ if (key_len > 0 && ((const char *)key)[key_len - 1] == '/')
+ key_len--;
+
+ return git__hash(key, (int)key_len, hash_seeds[hash_id]);
+}
+
+static int strcmp_no_trailing_slash(const void *a, const void *b)
+{
+ const char *astr = (const char *)a;
+ const char *bstr = (const char *)b;
+ size_t alen = a ? strlen(astr) : 0;
+ size_t blen = b ? strlen(bstr) : 0;
+ int cmp;
+
+ if (alen > 0 && astr[alen - 1] == '/')
+ alen--;
+ if (blen > 0 && bstr[blen - 1] == '/')
+ blen--;
+
+ cmp = strncmp(astr, bstr, min(alen, blen));
+ if (cmp == 0)
+ cmp = (alen < blen) ? -1 : (alen > blen) ? 1 : 0;
+
+ return cmp;
+}
+
+static git_submodule *submodule_alloc(const char *name)
+{
+ git_submodule *sm = git__calloc(1, sizeof(git_submodule));
+ if (sm == NULL)
+ return sm;
+
+ sm->path = sm->name = git__strdup(name);
+ if (!sm->name) {
+ git__free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+static void submodule_release(git_submodule *sm, int decr)
+{
+ if (!sm)
+ return;
+
+ sm->refcount -= decr;
+
+ if (sm->refcount == 0) {
+ if (sm->name != sm->path)
+ git__free(sm->path);
+ git__free(sm->name);
+ git__free(sm->url);
+ git__free(sm);
+ }
+}
+
+static int submodule_from_entry(
+ git_hashtable *smcfg, git_index_entry *entry)
+{
+ git_submodule *sm;
+ void *old_sm;
+
+ sm = git_hashtable_lookup(smcfg, entry->path);
+ if (!sm)
+ sm = submodule_alloc(entry->path);
+
+ git_oid_cpy(&sm->oid, &entry->oid);
+
+ if (strcmp(sm->path, entry->path) != 0) {
+ if (sm->path != sm->name) {
+ git__free(sm->path);
+ sm->path = sm->name;
+ }
+ sm->path = git__strdup(entry->path);
+ if (!sm->path)
+ goto fail;
+ }
+
+ if (git_hashtable_insert2(smcfg, sm->path, sm, &old_sm) < 0)
+ goto fail;
+ sm->refcount++;
+
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
+ /* TODO: log warning about multiple entrys for same submodule path */
+ submodule_release(old_sm, 1);
+ }
+
+ return 0;
+
+fail:
+ submodule_release(sm, 0);
+ return -1;
+}
+
+static int submodule_from_config(
+ const char *key, const char *value, void *data)
+{
+ git_hashtable *smcfg = data;
+ const char *namestart;
+ const char *property;
+ git_buf name = GIT_BUF_INIT;
+ git_submodule *sm;
+ void *old_sm = NULL;
+ bool is_path;
+
+ if (git__prefixcmp(key, "submodule.") != 0)
+ return 0;
+
+ namestart = key + strlen("submodule.");
+ property = strrchr(namestart, '.');
+ if (property == NULL)
+ return 0;
+ property++;
+ is_path = (strcmp(property, "path") == 0);
+
+ if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
+ return -1;
+
+ sm = git_hashtable_lookup(smcfg, name.ptr);
+ if (!sm && is_path)
+ sm = git_hashtable_lookup(smcfg, value);
+ if (!sm)
+ sm = submodule_alloc(name.ptr);
+ if (!sm)
+ goto fail;
+
+ if (strcmp(sm->name, name.ptr) != 0) {
+ assert(sm->path == sm->name);
+ sm->name = git_buf_detach(&name);
+ if (git_hashtable_insert2(smcfg, sm->name, sm, &old_sm) < 0)
+ goto fail;
+ sm->refcount++;
+ }
+ else if (is_path && strcmp(sm->path, value) != 0) {
+ assert(sm->path == sm->name);
+ if ((sm->path = git__strdup(value)) == NULL ||
+ git_hashtable_insert2(smcfg, sm->path, sm, &old_sm) < 0)
+ goto fail;
+ sm->refcount++;
+ }
+ git_buf_free(&name);
+
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
+ /* TODO: log entry about multiple submodules with same path */
+ submodule_release(old_sm, 1);
+ }
+
+ if (is_path)
+ return 0;
+
+ /* copy other properties into submodule entry */
+ if (strcmp(property, "url") == 0) {
+ if (sm->url) {
+ git__free(sm->url);
+ sm->url = NULL;
+ }
+ if ((sm->url = git__strdup(value)) == NULL)
+ goto fail;
+ }
+ else if (strcmp(property, "update") == 0) {
+ int val;
+ if (git_config_lookup_map_value(
+ _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) {
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule update property: '%s'", value);
+ goto fail;
+ }
+ sm->update = (git_submodule_update_t)val;
+ }
+ else if (strcmp(property, "fetchRecurseSubmodules") == 0) {
+ if (git_config_parse_bool(&sm->fetch_recurse, value) < 0)
+ goto fail;
+ }
+ else if (strcmp(property, "ignore") == 0) {
+ int val;
+ if (git_config_lookup_map_value(
+ _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) {
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule ignore property: '%s'", value);
+ goto fail;
+ }
+ sm->ignore = (git_submodule_ignore_t)val;
+ }
+ /* ignore other unknown submodule properties */
+
+ return 0;
+
+fail:
+ submodule_release(sm, 0);
+ git_buf_free(&name);
+ return -1;
+}
+
+static int load_submodule_config(git_repository *repo)
+{
+ int error;
+ git_index *index;
+ unsigned int i, max_i;
+ git_oid gitmodules_oid;
+ git_hashtable *smcfg;
+ struct git_config_file *mods = NULL;
+
+ if (repo->submodules)
+ return 0;
+
+ /* submodule data is kept in a hashtable with each submodule stored
+ * under both its name and its path. These are usually the same, but
+ * that is not guaranteed.
+ */
+ smcfg = git_hashtable_alloc(
+ 4, strhash_no_trailing_slash, strcmp_no_trailing_slash);
+ GITERR_CHECK_ALLOC(smcfg);
+
+ /* scan index for gitmodules (and .gitmodules entry) */
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+ goto cleanup;
+ memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
+ max_i = git_index_entrycount(index);
+
+ for (i = 0; i < max_i; i++) {
+ git_index_entry *entry = git_index_get(index, i);
+ if (S_ISGITLINK(entry->mode)) {
+ if ((error = submodule_from_entry(smcfg, entry)) < 0)
+ goto cleanup;
+ }
+ else if (strcmp(entry->path, ".gitmodules") == 0)
+ git_oid_cpy(&gitmodules_oid, &entry->oid);
+ }
+
+ /* load .gitmodules from workdir if it exists */
+ if (git_repository_workdir(repo) != NULL) {
+ /* look in workdir for .gitmodules */
+ git_buf path = GIT_BUF_INIT;
+ if (!git_buf_joinpath(
+ &path, git_repository_workdir(repo), ".gitmodules") &&
+ git_path_isfile(path.ptr))
+ {
+ if (!(error = git_config_file__ondisk(&mods, path.ptr)))
+ error = git_config_file_open(mods);
+ }
+ git_buf_free(&path);
+ }
+
+ /* load .gitmodules from object cache if not in workdir */
+ if (!error && mods == NULL && !git_oid_iszero(&gitmodules_oid)) {
+ /* TODO: is it worth loading gitmodules from object cache? */
+ }
+
+ /* process .gitmodules info */
+ if (!error && mods != NULL)
+ error = git_config_file_foreach(mods, submodule_from_config, smcfg);
+
+ /* store submodule config in repo */
+ if (!error)
+ repo->submodules = smcfg;
+
+cleanup:
+ if (mods != NULL)
+ git_config_file_free(mods);
+ if (error)
+ git_hashtable_free(smcfg);
+ return error;
+}
+
+void git_submodule_config_free(git_repository *repo)
+{
+ git_hashtable *smcfg = repo->submodules;
+ git_submodule *sm;
+
+ repo->submodules = NULL;
+
+ if (smcfg == NULL)
+ return;
+
+ GIT_HASHTABLE_FOREACH_VALUE(smcfg, sm, { submodule_release(sm,1); });
+ git_hashtable_free(smcfg);
+}
+
+static int submodule_cmp(const void *a, const void *b)
+{
+ return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
+}
+
+int git_submodule_foreach(
+ git_repository *repo,
+ int (*callback)(const char *name, void *payload),
+ void *payload)
+{
+ int error;
+ git_submodule *sm;
+ git_vector seen = GIT_VECTOR_INIT;
+ seen._cmp = submodule_cmp;
+
+ if ((error = load_submodule_config(repo)) < 0)
+ return error;
+
+ GIT_HASHTABLE_FOREACH_VALUE(
+ repo->submodules, sm, {
+ /* usually the following will not come into play */
+ if (sm->refcount > 1) {
+ if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
+ continue;
+ if ((error = git_vector_insert(&seen, sm)) < 0)
+ break;
+ }
+
+ if ((error = callback(sm->name, payload)) < 0)
+ break;
+ });
+
+ git_vector_free(&seen);
+
+ return error;
+}
+
+int git_submodule_lookup(
+ git_submodule **sm_ptr, /* NULL allowed if user only wants to test */
+ git_repository *repo,
+ const char *name) /* trailing slash is allowed */
+{
+ git_submodule *sm;
+
+ if (load_submodule_config(repo) < 0)
+ return -1;
+
+ sm = git_hashtable_lookup(repo->submodules, name);
+
+ if (sm_ptr)
+ *sm_ptr = sm;
+
+ return sm ? 0 : GIT_ENOTFOUND;
+}
diff --git a/src/tree.h b/src/tree.h
index 0bff41312..fd00afde5 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -32,7 +32,7 @@ struct git_treebuilder {
GIT_INLINE(unsigned int) entry_is_tree(const struct git_tree_entry *e)
{
- return e->attr & 040000;
+ return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
}
void git_tree__free(git_tree *tree);
diff --git a/src/vector.h b/src/vector.h
index 180edbf7c..5bc27914a 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -44,6 +44,8 @@ GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int
return (position < v->length) ? v->contents[position] : NULL;
}
+#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
+
GIT_INLINE(void *) git_vector_last(git_vector *v)
{
return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
diff --git a/tests-clar/core/errors.c b/tests-clar/core/errors.c
index 52b2652c8..c781000d5 100644
--- a/tests-clar/core/errors.c
+++ b/tests-clar/core/errors.c
@@ -46,6 +46,7 @@ void test_core_errors__new_school(void)
{
struct stat st;
assert(p_lstat("this_file_does_not_exist", &st) < 0);
+ GIT_UNUSED(st);
}
giterr_set(GITERR_OS, "stat failed"); /* internal fn */
diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c
index 3953fd83f..60f416fad 100644
--- a/tests-clar/diff/iterator.c
+++ b/tests-clar/diff/iterator.c
@@ -139,6 +139,40 @@ void test_diff_iterator__tree_3(void)
tree_iterator_test("status", "0017bd4ab1e", 8, expected_tree_3);
}
+/* $ git ls-tree -r --name-only 24fa9a9fc4e202313e24b648087495441dab432b */
+const char *expected_tree_4[] = {
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "binfile",
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "sub/abc",
+ "sub/file",
+ "sub/sub/file",
+ "sub/sub/subsub.txt",
+ "sub/subdir_test1",
+ "sub/subdir_test2.txt",
+ "subdir/.gitattributes",
+ "subdir/abc",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+ NULL
+};
+
+void test_diff_iterator__tree_4(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ 23, expected_tree_4);
+}
/* -- INDEX ITERATOR TESTS -- */
@@ -188,6 +222,12 @@ static const char *expected_index_0[] = {
"root_test2",
"root_test3",
"root_test4.txt",
+ "sub/abc",
+ "sub/file",
+ "sub/sub/file",
+ "sub/sub/subsub.txt",
+ "sub/subdir_test1",
+ "sub/subdir_test2.txt",
"subdir/.gitattributes",
"subdir/abc",
"subdir/subdir_test1",
@@ -208,6 +248,12 @@ static const char *expected_index_oids_0[] = {
"4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
"108bb4e7fd7b16490dc33ff7d972151e73d7166e",
"fe773770c5a6cc7185580c9204b1ff18a33ff3fc",
+ "3e42ffc54a663f9401cc25843d6c0e71a33e4249",
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "9e5bdc47d6a80f2be0ea3049ad74231b94609242",
+ "e563cf4758f0d646f1b14b76016aa17fa9e549a4",
+ "fb5067b1aef3ac1ada4b379dbcb7d17255df7d78",
"99eae476896f4907224978b88e5ecaa6c5bb67a9",
"3e42ffc54a663f9401cc25843d6c0e71a33e4249",
"e563cf4758f0d646f1b14b76016aa17fa9e549a4",
@@ -217,7 +263,7 @@ static const char *expected_index_oids_0[] = {
void test_diff_iterator__index_0(void)
{
- index_iterator_test("attr", 17, expected_index_0, expected_index_oids_0);
+ index_iterator_test("attr", 23, expected_index_0, expected_index_oids_0);
}
static const char *expected_index_1[] = {
diff --git a/tests-clar/refs/unicode.c b/tests-clar/refs/unicode.c
new file mode 100644
index 000000000..889c85666
--- /dev/null
+++ b/tests-clar/refs/unicode.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+
+void test_refs_unicode__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_refs_unicode__cleanup(void)
+{
+ git_repository_free(repo);
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_refs_unicode__create_and_lookup(void)
+{
+ git_reference *ref0, *ref1, *ref2;
+ git_repository *repo2;
+
+ const char *REFNAME = "refs/heads/" "\305" "ngstr" "\366" "m";
+ const char *master = "refs/heads/master";
+
+ /* Create the reference */
+ cl_git_pass(git_reference_lookup(&ref0, repo, master));
+ cl_git_pass(git_reference_create_oid(&ref1, repo, REFNAME, git_reference_oid(ref0), 0));
+ cl_assert(strcmp(REFNAME, git_reference_name(ref1)) == 0);
+
+ /* Lookup the reference in a different instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
+
+ cl_assert(git_oid_cmp(git_reference_oid(ref1), git_reference_oid(ref2)) == 0);
+ cl_assert(strcmp(REFNAME, git_reference_name(ref2)) == 0);
+
+ git_reference_free(ref0);
+ git_reference_free(ref1);
+ git_reference_free(ref2);
+ git_repository_free(repo2);
+}
diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h
index 1a68648f4..e60b67cb3 100644
--- a/tests-clar/status/status_data.h
+++ b/tests-clar/status/status_data.h
@@ -8,6 +8,8 @@ struct status_entry_counts {
int expected_entry_count;
};
+/* entries for a plain copy of tests/resources/status */
+
static const char *entry_paths0[] = {
"file_deleted",
"ignored_file",
@@ -48,3 +50,153 @@ static const unsigned int entry_statuses0[] = {
static const size_t entry_count0 = 15;
+/* entries for a copy of tests/resources/status with all content
+ * deleted from the working directory
+ */
+
+static const char *entry_paths2[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+};
+
+static const unsigned int entry_statuses2[] = {
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+};
+
+static const size_t entry_count2 = 15;
+
+/* entries for a copy of tests/resources/status with some mods */
+
+static const char *entry_paths3[] = {
+ ".HEADER",
+ "42-is-not-prime.sigh",
+ "README.md",
+ "current_file",
+ "current_file/",
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+};
+
+static const unsigned int entry_statuses3[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+};
+
+static const size_t entry_count3 = 21;
+
+
+/* entries for a copy of tests/resources/status with some mods
+ * and different options to the status call
+ */
+
+static const char *entry_paths4[] = {
+ ".new_file",
+ "current_file",
+ "current_file/current_file",
+ "current_file/modified_file",
+ "current_file/new_file",
+ "file_deleted",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "zzz_new_dir/new_file",
+ "zzz_new_file"
+};
+
+static const unsigned int entry_statuses4[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+};
+
+static const size_t entry_count4 = 22;
diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c
new file mode 100644
index 000000000..969158825
--- /dev/null
+++ b/tests-clar/status/submodules.c
@@ -0,0 +1,117 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "posix.h"
+
+static git_repository *g_repo = NULL;
+
+void test_status_submodules__initialize(void)
+{
+ git_buf modpath = GIT_BUF_INIT;
+
+ g_repo = cl_git_sandbox_init("submodules");
+
+ cl_fixture_sandbox("testrepo.git");
+
+ cl_git_pass(git_buf_sets(&modpath, git_repository_workdir(g_repo)));
+ cl_assert(git_path_dirname_r(&modpath, modpath.ptr) >= 0);
+ cl_git_pass(git_buf_joinpath(&modpath, modpath.ptr, "testrepo.git\n"));
+
+ p_rename("submodules/gitmodules", "submodules/.gitmodules");
+ cl_git_append2file("submodules/.gitmodules", modpath.ptr);
+ git_buf_free(&modpath);
+
+ p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
+}
+
+void test_status_submodules__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_status_submodules__api(void)
+{
+ git_submodule *sm;
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND);
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_assert(sm != NULL);
+ cl_assert_equal_s("testrepo", sm->name);
+ cl_assert_equal_s("testrepo", sm->path);
+}
+
+static int
+cb_status__submodule_count(const char *p, unsigned int s, void *payload)
+{
+ volatile int *count = (int *)payload;
+
+ GIT_UNUSED(p);
+ GIT_UNUSED(s);
+
+ (*count)++;
+
+ return 0;
+}
+
+void test_status_submodules__0(void)
+{
+ int counts = 0;
+
+ cl_assert(git_path_isdir("submodules/.git"));
+ cl_assert(git_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_path_isfile("submodules/.gitmodules"));
+
+ cl_git_pass(
+ git_status_foreach(g_repo, cb_status__submodule_count, &counts)
+ );
+
+ cl_assert(counts == 6);
+}
+
+static const char *expected_files[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "ignored",
+ "modified",
+ "untracked"
+};
+
+static unsigned int expected_status[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+};
+
+static int
+cb_status__match(const char *p, unsigned int s, void *payload)
+{
+ volatile int *index = (int *)payload;
+
+ cl_assert_equal_s(expected_files[*index], p);
+ cl_assert(expected_status[*index] == s);
+ (*index)++;
+
+ return 0;
+}
+
+void test_status_submodules__1(void)
+{
+ int index = 0;
+
+ cl_assert(git_path_isdir("submodules/.git"));
+ cl_assert(git_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_path_isfile("submodules/.gitmodules"));
+
+ cl_git_pass(
+ git_status_foreach(g_repo, cb_status__match, &index)
+ );
+
+ cl_assert(index == 6);
+}
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index f80975795..efdf6f41b 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -2,7 +2,9 @@
#include "fileops.h"
#include "ignore.h"
#include "status_data.h"
-
+#include "posix.h"
+#include "util.h"
+#include "path.h"
/**
* Auxiliary methods
@@ -43,7 +45,6 @@ cb_status__count(const char *p, unsigned int s, void *payload)
return 0;
}
-
/**
* Initializer
*
@@ -68,6 +69,7 @@ void test_status_worktree__cleanup(void)
/**
* Tests - Status determination on a working tree
*/
+/* this test is equivalent to t18-status.c:statuscb0 */
void test_status_worktree__whole_repository(void)
{
struct status_entry_counts counts;
@@ -87,6 +89,7 @@ void test_status_worktree__whole_repository(void)
cl_assert(counts.wrong_sorted_path == 0);
}
+/* this test is equivalent to t18-status.c:statuscb1 */
void test_status_worktree__empty_repository(void)
{
int count = 0;
@@ -97,6 +100,122 @@ void test_status_worktree__empty_repository(void)
cl_assert(count == 0);
}
+static int remove_file_cb(void *data, git_buf *file)
+{
+ const char *filename = git_buf_cstr(file);
+
+ GIT_UNUSED(data);
+
+ if (git__suffixcmp(filename, ".git") == 0)
+ return 0;
+
+ if (git_path_isdir(filename))
+ cl_git_pass(git_futils_rmdir_r(filename, 1));
+ else
+ cl_git_pass(p_unlink(git_buf_cstr(file)));
+
+ return 0;
+}
+
+/* this test is equivalent to t18-status.c:statuscb2 */
+void test_status_worktree__purged_worktree(void)
+{
+ struct status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_buf workdir = GIT_BUF_INIT;
+
+ /* first purge the contents of the worktree */
+ cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo)));
+ cl_git_pass(git_path_direach(&workdir, remove_file_cb, NULL));
+ git_buf_free(&workdir);
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(struct status_entry_counts));
+ counts.expected_entry_count = entry_count2;
+ counts.expected_paths = entry_paths2;
+ counts.expected_statuses = entry_statuses2;
+
+ cl_git_pass(
+ git_status_foreach(repo, cb_status__normal, &counts)
+ );
+
+ cl_assert(counts.entry_count == counts.expected_entry_count);
+ cl_assert(counts.wrong_status_flags_count == 0);
+ cl_assert(counts.wrong_sorted_path == 0);
+}
+
+/* this test is similar to t18-status.c:statuscb3 */
+void test_status_worktree__swap_subdir_and_file(void)
+{
+ struct status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts;
+
+ /* first alter the contents of the worktree */
+ cl_git_pass(p_rename("status/current_file", "status/swap"));
+ cl_git_pass(p_rename("status/subdir", "status/current_file"));
+ cl_git_pass(p_rename("status/swap", "status/subdir"));
+
+ cl_git_mkfile("status/.HEADER", "dummy");
+ cl_git_mkfile("status/42-is-not-prime.sigh", "dummy");
+ cl_git_mkfile("status/README.md", "dummy");
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(struct status_entry_counts));
+ counts.expected_entry_count = entry_count3;
+ counts.expected_paths = entry_paths3;
+ counts.expected_statuses = entry_statuses3;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert(counts.entry_count == counts.expected_entry_count);
+ cl_assert(counts.wrong_status_flags_count == 0);
+ cl_assert(counts.wrong_sorted_path == 0);
+
+}
+
+void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
+{
+ struct status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts;
+
+ /* first alter the contents of the worktree */
+ cl_git_pass(p_rename("status/current_file", "status/swap"));
+ cl_git_pass(p_rename("status/subdir", "status/current_file"));
+ cl_git_pass(p_rename("status/swap", "status/subdir"));
+ cl_git_mkfile("status/.new_file", "dummy");
+ cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", NULL, 0777));
+ cl_git_mkfile("status/zzz_new_dir/new_file", "dummy");
+ cl_git_mkfile("status/zzz_new_file", "dummy");
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(struct status_entry_counts));
+ counts.expected_entry_count = entry_count4;
+ counts.expected_paths = entry_paths4;
+ counts.expected_statuses = entry_statuses4;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+ /* TODO: set pathspec to "current_file" eventually */
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert(counts.entry_count == counts.expected_entry_count);
+ cl_assert(counts.wrong_status_flags_count == 0);
+ cl_assert(counts.wrong_sorted_path == 0);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus0 */
void test_status_worktree__single_file(void)
{
int i;
@@ -111,6 +230,54 @@ void test_status_worktree__single_file(void)
}
}
+/* this test is equivalent to t18-status.c:singlestatus1 */
+void test_status_worktree__single_nonexistent_file(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ error = git_status_file(&status_flags, repo, "nonexistent");
+ cl_git_fail(error);
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus2 */
+void test_status_worktree__single_nonexistent_file_empty_repo(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ error = git_status_file(&status_flags, repo, "nonexistent");
+ cl_git_fail(error);
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus3 */
+void test_status_worktree__single_file_empty_repo(void)
+{
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/new_file", "new_file\n");
+
+ cl_git_pass(git_status_file(&status_flags, repo, "new_file"));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus4 */
+void test_status_worktree__single_folder(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ error = git_status_file(&status_flags, repo, "subdir");
+ cl_git_fail(error);
+}
+
+
void test_status_worktree__ignores(void)
{
int i, ignored;
@@ -133,3 +300,87 @@ void test_status_worktree__ignores(void)
);
cl_assert(ignored);
}
+
+static int cb_status__check_592(const char *p, unsigned int s, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (s != GIT_STATUS_WT_DELETED || (payload != NULL && strcmp(p, (const char *)payload) != 0))
+ return -1;
+
+ return 0;
+}
+
+void test_status_worktree__issue_592(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "l.txt"));
+ cl_git_pass(p_unlink(git_buf_cstr(&path)));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "l.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_2(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c/a.txt"));
+ cl_git_pass(p_unlink(git_buf_cstr(&path)));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_3(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), 1));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_4(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t/b.txt"));
+ cl_git_pass(p_unlink(git_buf_cstr(&path)));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "t/b.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_5(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), 1));
+ cl_git_pass(p_mkdir(git_buf_cstr(&path), 0777));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, NULL));
+
+ git_buf_free(&path);
+}
diff --git a/tests/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index
index 19fa99d5b..1d60eab8f 100644
--- a/tests/resources/attr/.gitted/index
+++ b/tests/resources/attr/.gitted/index
Binary files differ
diff --git a/tests/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD
index 68fcff2c5..73f00f345 100644
--- a/tests/resources/attr/.gitted/logs/HEAD
+++ b/tests/resources/attr/.gitted/logs/HEAD
@@ -4,3 +4,5 @@
a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
+a97cc019851d401a4f1d091cb91a15890a0dd1ba 217878ab49e1314388ea2e32dc6fdb58a1b969e0 Russell Belfer <arrbee@arrbee.com> 1332734901 -0700 commit: added files in sub/sub
+217878ab49e1314388ea2e32dc6fdb58a1b969e0 24fa9a9fc4e202313e24b648087495441dab432b Russell Belfer <arrbee@arrbee.com> 1332735555 -0700 commit: adding more files in sub for tree status
diff --git a/tests/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master
index 68fcff2c5..73f00f345 100644
--- a/tests/resources/attr/.gitted/logs/refs/heads/master
+++ b/tests/resources/attr/.gitted/logs/refs/heads/master
@@ -4,3 +4,5 @@
a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
+a97cc019851d401a4f1d091cb91a15890a0dd1ba 217878ab49e1314388ea2e32dc6fdb58a1b969e0 Russell Belfer <arrbee@arrbee.com> 1332734901 -0700 commit: added files in sub/sub
+217878ab49e1314388ea2e32dc6fdb58a1b969e0 24fa9a9fc4e202313e24b648087495441dab432b Russell Belfer <arrbee@arrbee.com> 1332735555 -0700 commit: adding more files in sub for tree status
diff --git a/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
new file mode 100644
index 000000000..b537899f2
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
@@ -0,0 +1,4 @@
+xQ
+0D)nD#xmvJ߀7cx0Iۺ -+e"v☝pwcJH1x%HL>Dd xC\ʤzᔶd0Z#mغڰ
+y
+>{qK \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
new file mode 100644
index 000000000..e7099bbaa
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
new file mode 100644
index 000000000..f90f0d79c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
new file mode 100644
index 000000000..6fcc549b4
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
new file mode 100644
index 000000000..4b57836cd
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
new file mode 100644
index 000000000..d6385ec8d
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
new file mode 100644
index 000000000..1005f944a
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
new file mode 100644
index 000000000..44d703b2e
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
Binary files differ
diff --git a/tests/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master
index 7f8bbe3e7..8768776b3 100644
--- a/tests/resources/attr/.gitted/refs/heads/master
+++ b/tests/resources/attr/.gitted/refs/heads/master
@@ -1 +1 @@
-a97cc019851d401a4f1d091cb91a15890a0dd1ba
+24fa9a9fc4e202313e24b648087495441dab432b
diff --git a/tests/resources/issue_592/.gitted/COMMIT_EDITMSG b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..5852f4463
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Initial commit
diff --git a/tests/resources/issue_592/.gitted/HEAD b/tests/resources/issue_592/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_592/.gitted/config b/tests/resources/issue_592/.gitted/config
new file mode 100644
index 000000000..78387c50b
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/issue_592/.gitted/index b/tests/resources/issue_592/.gitted/index
new file mode 100644
index 000000000..eaeb5d761
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/info/exclude b/tests/resources/issue_592/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/issue_592/.gitted/logs/HEAD b/tests/resources/issue_592/.gitted/logs/HEAD
new file mode 100644
index 000000000..f19fe35a6
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 4d383e87f0371ba8fa353f3912db6862b2625e85 nulltoken <emeric.fermas@gmail.com> 1331989635 +0100 commit (initial): Initial commit
+4d383e87f0371ba8fa353f3912db6862b2625e85 e38fcc7a6060f5eb5b876e836b52ae4769363f21 nulltoken <emeric.fermas@gmail.com> 1332227062 +0100 commit (amend): Initial commit
diff --git a/tests/resources/issue_592/.gitted/logs/refs/heads/master b/tests/resources/issue_592/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..f19fe35a6
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 4d383e87f0371ba8fa353f3912db6862b2625e85 nulltoken <emeric.fermas@gmail.com> 1331989635 +0100 commit (initial): Initial commit
+4d383e87f0371ba8fa353f3912db6862b2625e85 e38fcc7a6060f5eb5b876e836b52ae4769363f21 nulltoken <emeric.fermas@gmail.com> 1332227062 +0100 commit (amend): Initial commit
diff --git a/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
new file mode 100644
index 000000000..05dec10f7
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
new file mode 100644
index 000000000..e997e1b49
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
new file mode 100644
index 000000000..c49a8be58
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
@@ -0,0 +1,2 @@
+xM
+0]o/IDz bz H9v2t =k,p+͏>4U=^(tAF2̟3sL|%cYuZv{=r_G}K> \ No newline at end of file
diff --git a/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
new file mode 100644
index 000000000..25d44d938
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
new file mode 100644
index 000000000..1d6e38d37
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
new file mode 100644
index 000000000..36c5b9aab
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
new file mode 100644
index 000000000..c08ecd5ed
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/refs/heads/master b/tests/resources/issue_592/.gitted/refs/heads/master
new file mode 100644
index 000000000..1f6669628
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/refs/heads/master
@@ -0,0 +1 @@
+e38fcc7a6060f5eb5b876e836b52ae4769363f21
diff --git a/tests/resources/issue_592/a.txt b/tests/resources/issue_592/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/c/a.txt b/tests/resources/issue_592/c/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/c/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/l.txt b/tests/resources/issue_592/l.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/l.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/t/a.txt b/tests/resources/issue_592/t/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/t/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/t/b.txt b/tests/resources/issue_592/t/b.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/t/b.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/submodules/.gitted/HEAD b/tests/resources/submodules/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submodules/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodules/.gitted/config b/tests/resources/submodules/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/submodules/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/submodules/.gitted/description b/tests/resources/submodules/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submodules/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submodules/.gitted/index b/tests/resources/submodules/.gitted/index
new file mode 100644
index 000000000..97bf8ef51
--- /dev/null
+++ b/tests/resources/submodules/.gitted/index
Binary files differ
diff --git a/tests/resources/submodules/.gitted/info/exclude b/tests/resources/submodules/.gitted/info/exclude
new file mode 100644
index 000000000..dfc411579
--- /dev/null
+++ b/tests/resources/submodules/.gitted/info/exclude
@@ -0,0 +1,8 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+ignored
+
diff --git a/tests/resources/submodules/.gitted/info/refs b/tests/resources/submodules/.gitted/info/refs
new file mode 100644
index 000000000..ba17abdde
--- /dev/null
+++ b/tests/resources/submodules/.gitted/info/refs
@@ -0,0 +1 @@
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests/resources/submodules/.gitted/logs/HEAD b/tests/resources/submodules/.gitted/logs/HEAD
new file mode 100644
index 000000000..87a7bdafc
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 09176a980273d801a3e37cc45c84af1366501ed9 Russell Belfer <arrbee@arrbee.com> 1332365253 -0700 commit (initial): initial commit
+09176a980273d801a3e37cc45c84af1366501ed9 97896810b3210244a62a82458b8e0819ecfc6850 Russell Belfer <arrbee@arrbee.com> 1332780781 -0700 commit: Setting up gitmodules
diff --git a/tests/resources/submodules/.gitted/logs/refs/heads/master b/tests/resources/submodules/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..87a7bdafc
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 09176a980273d801a3e37cc45c84af1366501ed9 Russell Belfer <arrbee@arrbee.com> 1332365253 -0700 commit (initial): initial commit
+09176a980273d801a3e37cc45c84af1366501ed9 97896810b3210244a62a82458b8e0819ecfc6850 Russell Belfer <arrbee@arrbee.com> 1332780781 -0700 commit: Setting up gitmodules
diff --git a/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
new file mode 100644
index 000000000..2c3c2cb61
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
@@ -0,0 +1,2 @@
+x%=
+0 F])0"I*|-t{?2ilV8$mvkk*F DA=(=|=6 DAv=A}&'O$= \ No newline at end of file
diff --git a/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
new file mode 100644
index 000000000..c85fb5512
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
new file mode 100644
index 000000000..1c8dbdf9f
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
@@ -0,0 +1,3 @@
+x[
+0E*fʤS K4ݿwׅ9p2MCFP @u..p!OYdiYU'̕8XbPn6
+ħԞ1[q}0qc[W#1fR:SZ+Y+{tdlvOmu_}5i` K \ No newline at end of file
diff --git a/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
new file mode 100644
index 000000000..3d78bd6be
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
new file mode 100644
index 000000000..6e0b49e86
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
new file mode 100644
index 000000000..082a58941
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/info/packs b/tests/resources/submodules/.gitted/objects/info/packs
new file mode 100644
index 000000000..0785ef698
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
+
diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
new file mode 100644
index 000000000..810fc3181
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
new file mode 100644
index 000000000..c25c4a73f
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
Binary files differ
diff --git a/tests/resources/submodules/.gitted/packed-refs b/tests/resources/submodules/.gitted/packed-refs
new file mode 100644
index 000000000..a6450691e
--- /dev/null
+++ b/tests/resources/submodules/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests/resources/submodules/.gitted/refs/heads/master b/tests/resources/submodules/.gitted/refs/heads/master
new file mode 100644
index 000000000..32b935853
--- /dev/null
+++ b/tests/resources/submodules/.gitted/refs/heads/master
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/added b/tests/resources/submodules/added
new file mode 100644
index 000000000..d5f7fc3f7
--- /dev/null
+++ b/tests/resources/submodules/added
@@ -0,0 +1 @@
+added
diff --git a/tests/resources/submodules/gitmodules b/tests/resources/submodules/gitmodules
new file mode 100644
index 000000000..1262f8bb0
--- /dev/null
+++ b/tests/resources/submodules/gitmodules
@@ -0,0 +1,3 @@
+[submodule "testrepo"]
+ path = testrepo
+ url = \ No newline at end of file
diff --git a/tests/resources/submodules/ignored b/tests/resources/submodules/ignored
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests/resources/submodules/ignored
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules/modified b/tests/resources/submodules/modified
new file mode 100644
index 000000000..452216e1d
--- /dev/null
+++ b/tests/resources/submodules/modified
@@ -0,0 +1,2 @@
+changed
+
diff --git a/tests/resources/submodules/testrepo/.gitted/HEAD b/tests/resources/submodules/testrepo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config
new file mode 100644
index 000000000..d6dcad12b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests/resources/testrepo.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submodules/testrepo/.gitted/description b/tests/resources/submodules/testrepo/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submodules/testrepo/.gitted/index b/tests/resources/submodules/testrepo/.gitted/index
new file mode 100644
index 000000000..3eb8d84fe
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/info/exclude b/tests/resources/submodules/testrepo/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/HEAD b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
new file mode 100644
index 000000000..147643a30
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Russell Belfer <arrbee@arrbee.com> 1332366307 -0700 clone: from /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..147643a30
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Russell Belfer <arrbee@arrbee.com> 1332366307 -0700 clone: from /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 000000000..225c45734
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 000000000..df40d99af
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 000000000..321eaa867
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 000000000..9bb5b623b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xQ
+0D)6ͦ "xO-FbEo0 Ǥ,ske[Pn8R,EpD?g}^3 <GhYK8ЖDA);gݧjp4-r;sGA4ۺ=(in7IKFE \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+x 1ENi@k2 "X$YW0YcÅszMD08!s Xgd::@X0Pw"F/RUzmZZV}|/o5I!1z:vUim}/>
+F- \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 000000000..716b0c64b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xAj!?009o}H6}jUPPZ&Y AԛpFdpz[fYPqLJ.,Z`Ů.`v q $5+9Ot>/DE/龡W*eVdf1>覭ěʙFThk.i^0?PR, \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 000000000..23c462f34
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 000000000..4cc3f4dff
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutueXlmmAṃJ}G;UTWRQ`6Kǥ^/-*|W3Py`%E\&g|0{Ӎ1X \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 000000000..bf7b2bb68
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+x[
+0E*fդ "W0-Ft݁pS[Yx^
+Db CLhut}8X*4ZsYUA X3RM) s6輢Mរ&Jm;}<\@ޏpĀv?jۺL?H \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1Dmdǎ|M3`V{ >QvL0I?!4Z=!צ8F!rsQy9]$D&l6A>jFWҵ IKNiZ%S
+ U~̽>' w [ DGڡQ-M>dO}\8g_ШoYr \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 000000000..29c8e824d
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xQ
+!@sBQ" ٱ r{<xƪ
+HlJSer!ZPTe*jUEo^2(XS€EDO<Yj$2s_&} ,}[~p7~<: Zp?1_C0 \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 000000000..d95254674
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 000000000..f460f2547
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xA
+0a9I p'1Ѷv\x{cVpvWgǎ0x[ ]"g#{rD Cot N U $?9-p+1^Qx9O\C m'D {mV(+l, \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 000000000..f613670e2
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D)zUB-0uV9<#+W<J&8/seȕKJS
+Rv{QrYQN$H\E=6X5K Fr)(dCΆjs}9c-w8o\rI:
+l}FW$DsǣٚOWe]V8-Ý"U \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xQ
+0D)ʦI<'lR+FjEo0<xha ]șXUlPF)z4y,\r 'S-mI4
+Xh&F}n+\Y-p|鷜oUz;-alt{?I,:oRcHK \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 000000000..a67d6e647
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 000000000..b135eccda
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 000000000..82e2790e8
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..697c94c92
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 000000000..5068f2818
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 000000000..a6a1f3020
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 000000000..555cfa977
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 000000000..4d539ed0a
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/packed-refs b/tests/resources/submodules/testrepo/.gitted/packed-refs
new file mode 100644
index 000000000..9c0433e1c
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/packed-refs
@@ -0,0 +1,12 @@
+# pack-refs with: peeled
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/test
+^e90810b8df3e80c413d903f631643c716887138d
+1385f264afb75a56a5bec74243be9b367ba4ca08 refs/tags/point_to_blob
+7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/test
+763d71aadf09a7951596c9746c024e7eece7c7af refs/remotes/origin/subtrees
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045 refs/remotes/origin/packed-test
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/remotes/origin/master
+a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/br2
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
new file mode 100644
index 000000000..3d8f0a402
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submodules/testrepo/README b/tests/resources/submodules/testrepo/README
new file mode 100644
index 000000000..a8233120f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/submodules/testrepo/branch_file.txt b/tests/resources/submodules/testrepo/branch_file.txt
new file mode 100644
index 000000000..3697d64be
--- /dev/null
+++ b/tests/resources/submodules/testrepo/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/submodules/testrepo/new.txt b/tests/resources/submodules/testrepo/new.txt
new file mode 100644
index 000000000..a71586c1d
--- /dev/null
+++ b/tests/resources/submodules/testrepo/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/submodules/unmodified b/tests/resources/submodules/unmodified
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests/resources/submodules/unmodified
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules/untracked b/tests/resources/submodules/untracked
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests/resources/submodules/untracked
@@ -0,0 +1 @@
+yo