summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-05-07 04:32:17 -0700
committerRussell Belfer <rb@github.com>2013-05-07 04:32:17 -0700
commite35e2684f693f28afb7a8f28028b4cb8bdd19f49 (patch)
tree95b1a7c9a41145a6495535cdc1f8b840d241dca0 /src
parent3405f78754703948475b4677d03fcdbfb099b6a4 (diff)
downloadlibgit2-e35e2684f693f28afb7a8f28028b4cb8bdd19f49.tar.gz
Add GIT_DIFF_LINE_CONTEXT_EOFNL
This adds a new line origin constant for the special line that is used when both files end without a newline. In the course of writing the tests for this, I was having problems with modifying a file but not having diff notice because it was the same size and modified less than one second from the start of the test, so I decided to start working on nanosecond timestamp support. This commit doesn't contain the nanosecond support, but it contains the reorganization of maybe_modified and the hooks so that if the nanosecond data were being read by stat() (or rather being copied by git_index_entry__init_from_stat), then the nsec would be taken into account. This new stuff could probably use some more tests, although there is some amount of it here.
Diffstat (limited to 'src')
-rw-r--r--src/diff.c129
-rw-r--r--src/diff.h1
-rw-r--r--src/diff_output.c2
3 files changed, 80 insertions, 52 deletions
diff --git a/src/diff.c b/src/diff.c
index fbff1a6fb..e0dff9c95 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -389,6 +389,9 @@ static int diff_list_apply_options(
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
+ /* Set GIT_DIFFCAPS_TRUST_NANOSECS on a platform basis */
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_NANOSECS;
+
/* If not given explicit `opts`, check `diff.xyz` configs */
if (!opts) {
diff->opts.context_lines = config_int(cfg, "diff.context", 3);
@@ -529,6 +532,13 @@ cleanup:
return result;
}
+static bool diff_time_eq(
+ const git_index_time *a, const git_index_time *b, bool use_nanos)
+{
+ return a->seconds == a->seconds &&
+ (!use_nanos || a->nanoseconds == b->nanoseconds);
+}
+
typedef struct {
git_repository *repo;
git_iterator *old_iter;
@@ -540,11 +550,51 @@ typedef struct {
#define MODE_BITS_MASK 0000777
+static int maybe_modified_submodule(
+ git_delta_t *status,
+ git_oid *found_oid,
+ git_diff_list *diff,
+ diff_in_progress *info)
+{
+ int error = 0;
+ git_submodule *sub;
+ unsigned int sm_status = 0;
+ const git_oid *sm_oid;
+
+ *status = GIT_DELTA_UNMODIFIED;
+
+ if (!DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) &&
+ !(error = git_submodule_lookup(
+ &sub, diff->repo, info->nitem->path)) &&
+ git_submodule_ignore(sub) != GIT_SUBMODULE_IGNORE_ALL &&
+ !(error = git_submodule_status(&sm_status, sub)))
+ {
+ /* check IS_WD_UNMODIFIED because this case is only used
+ * when the new side of the diff is the working directory
+ */
+ if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
+ *status = GIT_DELTA_MODIFIED;
+
+ /* grab OID while we are here */
+ if (git_oid_iszero(&info->nitem->oid) &&
+ (sm_oid = git_submodule_wd_id(sub)) != NULL)
+ git_oid_cpy(found_oid, sm_oid);
+ }
+
+ /* GIT_EEXISTS means a dir with .git in it was found - ignore it */
+ if (error == GIT_EEXISTS) {
+ giterr_clear();
+ error = 0;
+ }
+
+ return error;
+}
+
static int maybe_modified(
git_diff_list *diff,
diff_in_progress *info)
{
- git_oid noid, *use_noid = NULL;
+ git_oid noid;
git_delta_t status = GIT_DELTA_MODIFIED;
const git_index_entry *oitem = info->oitem;
const git_index_entry *nitem = info->nitem;
@@ -560,6 +610,8 @@ static int maybe_modified(
&matched_pathspec))
return 0;
+ memset(&noid, 0, sizeof(noid));
+
/* on platforms with no symlinks, preserve mode of existing symlinks */
if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
@@ -600,55 +652,30 @@ static int maybe_modified(
* circumstances that can accelerate things or need special handling
*/
else if (git_oid_iszero(&nitem->oid) && new_is_workdir) {
- /* TODO: add check against index file st_mtime to avoid racy-git */
+ bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
+ bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0);
- /* if the stat data looks exactly alike, then assume the same */
- if (omode == nmode &&
- oitem->file_size == nitem->file_size &&
- (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
- (oitem->ctime.seconds == nitem->ctime.seconds)) &&
- oitem->mtime.seconds == nitem->mtime.seconds &&
- (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
- (oitem->dev == nitem->dev)) &&
- oitem->ino == nitem->ino &&
- oitem->uid == nitem->uid &&
- oitem->gid == nitem->gid)
- status = GIT_DELTA_UNMODIFIED;
+ status = GIT_DELTA_UNMODIFIED;
- else if (S_ISGITLINK(nmode)) {
- int err;
- git_submodule *sub;
-
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
- status = GIT_DELTA_UNMODIFIED;
- else if ((err = git_submodule_lookup(&sub, diff->repo, nitem->path)) < 0) {
- if (err == GIT_EEXISTS)
- status = GIT_DELTA_UNMODIFIED;
- else
- return err;
- } else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
- status = GIT_DELTA_UNMODIFIED;
- else {
- unsigned int sm_status = 0;
- if (git_submodule_status(&sm_status, sub) < 0)
- return -1;
-
- /* check IS_WD_UNMODIFIED because this case is only used
- * when the new side of the diff is the working directory
- */
- status = GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)
- ? GIT_DELTA_UNMODIFIED : GIT_DELTA_MODIFIED;
-
- /* grab OID while we are here */
- if (git_oid_iszero(&nitem->oid)) {
- const git_oid *sm_oid = git_submodule_wd_id(sub);
- if (sm_oid != NULL) {
- git_oid_cpy(&noid, sm_oid);
- use_noid = &noid;
- }
- }
- }
+ /* TODO: add check against index file st_mtime to avoid racy-git */
+
+ if (S_ISGITLINK(nmode)) {
+ if (maybe_modified_submodule(&status, &noid, diff, info) < 0)
+ return -1;
}
+
+ /* if the stat data looks different, then mark modified - this just
+ * means that the OID will be recalculated below to confirm change
+ */
+ else if (omode != nmode ||
+ oitem->file_size != nitem->file_size ||
+ !diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
+ (use_ctime &&
+ !diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) ||
+ oitem->ino != nitem->ino ||
+ oitem->uid != nitem->uid ||
+ oitem->gid != nitem->gid)
+ status = GIT_DELTA_MODIFIED;
}
/* if mode is GITLINK and submodules are ignored, then skip */
@@ -660,11 +687,10 @@ static int maybe_modified(
* haven't calculated the OID of the new item, then calculate it now
*/
if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
- if (!use_noid) {
+ if (git_oid_iszero(&noid)) {
if (git_diff__oid_for_file(diff->repo,
nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
return -1;
- use_noid = &noid;
}
/* if oid matches, then mark unmodified (except submodules, where
@@ -672,12 +698,13 @@ static int maybe_modified(
* matches between the index and the workdir HEAD)
*/
if (omode == nmode && !S_ISGITLINK(omode) &&
- git_oid_equal(&oitem->oid, use_noid))
+ git_oid_equal(&oitem->oid, &noid))
status = GIT_DELTA_UNMODIFIED;
}
return diff_delta__from_two(
- diff, status, oitem, omode, nitem, nmode, use_noid, matched_pathspec);
+ diff, status, oitem, omode, nitem, nmode,
+ git_oid_iszero(&noid) ? NULL : &noid, matched_pathspec);
}
static bool entry_is_prefixed(
diff --git a/src/diff.h b/src/diff.h
index 48e20d1e3..16df431ed 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -26,6 +26,7 @@ enum {
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
+ GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */
};
enum {
diff --git a/src/diff_output.c b/src/diff_output.c
index bac8622c8..dda0f534d 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -726,7 +726,7 @@ static int diff_patch_cb(void *priv, mmbuffer_t *bufs, int len)
char origin =
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
- GIT_DIFF_LINE_CONTEXT;
+ GIT_DIFF_LINE_CONTEXT_EOFNL;
if (ctxt->data_cb != NULL &&
ctxt->data_cb(patch->delta, &ctxt->range,