static int threeway_diff(struct cache_entry **src, struct unpack_trees_options *o) { struct cache_entry *m = src[1]; struct cache_entry *p1 = src[2]; struct cache_entry *p2 = src[3]; GList **results = o->unpack_data; DiffEntry *de; if (m == o->df_conflict_entry) m = NULL; if (p1 == o->df_conflict_entry) p1 = NULL; if (p2 == o->df_conflict_entry) p2 = NULL; /* diff m from both p1 and p2. */ if (m && p1 && p2) { if (!ce_same(m, p1) && !ce_same (m, p2)) { de = diff_entry_new (DIFF_TYPE_COMMITS, DIFF_STATUS_MODIFIED, m->sha1, m->name); *results = g_list_prepend (*results, de); } } else if (!m && p1 && p2) { de = diff_entry_new (DIFF_TYPE_COMMITS, DIFF_STATUS_DELETED, p1->sha1, p1->name); *results = g_list_prepend (*results, de); } else if (m && !p1 && p2) { if (!ce_same (m, p2)) { de = diff_entry_new (DIFF_TYPE_COMMITS, DIFF_STATUS_MODIFIED, m->sha1, m->name); *results = g_list_prepend (*results, de); } } else if (m && p1 && !p2) { if (!ce_same (m, p1)) { de = diff_entry_new (DIFF_TYPE_COMMITS, DIFF_STATUS_MODIFIED, m->sha1, m->name); *results = g_list_prepend (*results, de); } } else if (m && !p1 && !p2) { de = diff_entry_new (DIFF_TYPE_COMMITS, DIFF_STATUS_ADDED, m->sha1, m->name); *results = g_list_prepend (*results, de); } /* Nothing to do for: * 1. !m && p1 && !p2; * 2. !m && !p1 && p2; * 3. !m && !p1 && !p2 (should not happen) */ return 0; }
static void diff_two_cache_entries (struct cache_entry *tree1, struct cache_entry *tree2, int diff_type, GList **results) { DiffEntry *de; if (!tree1) { if (S_ISDIR(tree2->ce_mode)) { de = diff_entry_new (diff_type, DIFF_STATUS_DIR_ADDED, tree2->sha1, tree2->name); } else { de = diff_entry_new (diff_type, DIFF_STATUS_ADDED, tree2->sha1, tree2->name); } *results = g_list_prepend (*results, de); return; } if (!tree2) { if (S_ISDIR(tree1->ce_mode)) { de = diff_entry_new (diff_type, DIFF_STATUS_DIR_DELETED, tree1->sha1, tree1->name); } else { de = diff_entry_new (diff_type, DIFF_STATUS_DELETED, tree1->sha1, tree1->name); } *results = g_list_prepend (*results, de); return; } if (tree2->ce_mode != tree1->ce_mode || hashcmp(tree2->sha1, tree1->sha1) != 0) { if (S_ISDIR(tree2->ce_mode)) { de = diff_entry_new (diff_type, DIFF_STATUS_DELETED, tree1->sha1, tree1->name); *results = g_list_prepend (*results, de); de = diff_entry_new (diff_type, DIFF_STATUS_DIR_ADDED, tree2->sha1, tree2->name); *results = g_list_prepend (*results, de); } else if (S_ISDIR(tree1->ce_mode)) { de = diff_entry_new (diff_type, DIFF_STATUS_DIR_DELETED, tree1->sha1, tree1->name); *results = g_list_prepend (*results, de); de = diff_entry_new (diff_type, DIFF_STATUS_ADDED, tree2->sha1, tree2->name); *results = g_list_prepend (*results, de); } else { de = diff_entry_new (diff_type, DIFF_STATUS_MODIFIED, tree2->sha1, tree2->name); *results = g_list_prepend (*results, de); } } }
void wt_status_collect_untracked(struct index_state *index, GList **results, const char *worktree, IgnoreFunc ignore_func) { int i; struct dir_struct dir; DiffEntry *de; memset(&dir, 0, sizeof(dir)); read_directory(&dir, worktree, index); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (!ignore_func(ent->name, NULL)) { unsigned char sha1[20] = { 0 }; de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_ADDED, sha1, ent->name); *results = g_list_prepend (*results, de); } free(ent); } free(dir.entries); }
void wt_status_collect_changes_index (struct index_state *index, GList **results, SeafRepo *repo) { SeafFSManager *fs_mgr; SeafCommit *head; int pos = 0; DiffEntry *de; fs_mgr = repo->manager->seaf->fs_mgr; head = seaf_commit_manager_get_commit (seaf->commit_mgr, repo->id, repo->version, repo->head->commit_id); if (!head) { seaf_warning ("Failed to get commit %s.\n", repo->head->commit_id); return; } mark_all_ce_unused (index); /* if repo is initial, we don't need to check index changes */ if (strncmp(EMPTY_SHA1, head->root_id, 40) != 0) { SeafDir *root; /* call diff_index to get status */ root = seaf_fs_manager_get_seafdir (fs_mgr, repo->id, repo->version, head->root_id); if (!root) { seaf_warning ("Failed to get root %s.\n", head->root_id); seaf_commit_unref (head); return; } if (diff_index(repo->id, repo->version, index, root, results) < 0) g_warning("diff index failed\n"); seaf_dir_free (root); seaf_commit_unref (head); return; } seaf_commit_unref (head); while (1) { struct cache_entry *ce = next_cache_entry(index, &pos); if (!ce || ce_stage(ce)) break; ce->ce_flags |= CE_UNPACKED; de = diff_entry_new (DIFF_TYPE_INDEX, DIFF_STATUS_ADDED, ce->sha1, ce->name); *results = g_list_prepend (*results, de); } }
/* This function only resolve "strict" rename, i.e. two files must be * exactly the same. */ void diff_resolve_renames (GList **diff_entries) { GHashTable *deleted; GList *p; GList *added = NULL; DiffEntry *de; /* Hash and equal functions for raw sha1. */ deleted = g_hash_table_new (ccnet_sha1_hash, ccnet_sha1_equal); /* Collect all "deleted" entries. */ for (p = *diff_entries; p != NULL; p = p->next) { de = p->data; if (de->status == DIFF_STATUS_DELETED) g_hash_table_insert (deleted, de->sha1, p); } /* Collect all "added" entries into a separate list. */ for (p = *diff_entries; p != NULL; p = p->next) { de = p->data; if (de->status == DIFF_STATUS_ADDED) added = g_list_prepend (added, p); } /* For each "added" entry, if we find a "deleted" entry with * the same content, we find a rename pair. */ p = added; while (p != NULL) { GList *p_add, *p_del; DiffEntry *de_add, *de_del, *de_rename; p_add = p->data; de_add = p_add->data; p_del = g_hash_table_lookup (deleted, de_add->sha1); if (p_del) { de_del = p_del->data; de_rename = diff_entry_new (de_del->type, DIFF_STATUS_RENAMED, de_del->sha1, de_del->name); de_rename->new_name = g_strdup(de_add->name); *diff_entries = g_list_delete_link (*diff_entries, p_add); *diff_entries = g_list_delete_link (*diff_entries, p_del); *diff_entries = g_list_prepend (*diff_entries, de_rename); g_hash_table_remove (deleted, de_add->sha1); } p = g_list_delete_link (p, p); } g_hash_table_destroy (deleted); }
void wt_status_collect_changes_worktree(struct index_state *index, GList **results, const char *worktree) { DiffEntry *de; int entries, i; GList *ignore_list = seaf_repo_load_ignore_files (worktree); entries = index->cache_nr; for (i = 0; i < entries; i++) { char *realpath; SeafStat st; struct cache_entry *ce = index->cache[i]; int changed = 0; if (ce_stage(ce)) { int mask = 0; mask |= 1 << ce_stage(ce); while (i < entries) { struct cache_entry *nce = index->cache[i]; if (strcmp(ce->name, nce->name)) break; mask |= 1 << ce_stage(nce); i++; } /* * Compensate for loop update */ i--; de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_UNMERGED, ce->sha1, ce->name); de->unmerge_state = diff_unmerged_state (mask); *results = g_list_prepend (*results, de); continue; } if (ce_uptodate(ce) || ce_skip_worktree(ce)) continue; realpath = g_build_path (PATH_SEPERATOR, worktree, ce->name, NULL); if (seaf_stat(realpath, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) changed = -1; else changed = 1; } if (changed) { if (changed < 0) { g_warning ("Faile to stat %s: %s\n", ce->name, strerror(errno)); g_free (realpath); continue; } if (ce->ce_ctime.sec == 0) { g_free (realpath); continue; } de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_DELETED, ce->sha1, ce->name); *results = g_list_prepend (*results, de); g_free (realpath); continue; } if (S_ISDIR (ce->ce_mode)) { g_free (realpath); continue; } /* Don't check changes to ignored files. * This can happen when a file is committed and then added to * ignore.txt. After that changes to this file will not committed, * and it should be ignored here. */ if (seaf_repo_check_ignore_file (ignore_list, realpath)) { g_free (realpath); continue; } g_free (realpath); changed = ie_match_stat (index, ce, &st, 0); if (!changed) { ce_mark_uptodate (ce); continue; } de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_MODIFIED, ce->sha1, ce->name); *results = g_list_prepend (*results, de); } seaf_repo_free_ignore_files (ignore_list); }
void wt_status_collect_changes_worktree(struct index_state *index, GList **results, const char *worktree, IgnoreFunc ignore_func) { DiffEntry *de; int entries, i; entries = index->cache_nr; for (i = 0; i < entries; i++) { char *realpath; struct stat st; struct cache_entry *ce = index->cache[i]; int changed = 0; if (ce_stage(ce)) { int mask = 0; mask |= 1 << ce_stage(ce); while (i < entries) { struct cache_entry *nce = index->cache[i]; if (strcmp(ce->name, nce->name)) break; mask |= 1 << ce_stage(nce); i++; } /* * Compensate for loop update */ i--; de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_UNMERGED, ce->sha1, ce->name); de->unmerge_state = diff_unmerged_state (mask); *results = g_list_prepend (*results, de); continue; } if (ce_uptodate(ce) || ce_skip_worktree(ce)) continue; realpath = g_build_path (PATH_SEPERATOR, worktree, ce->name, NULL); if (g_lstat(realpath, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) changed = -1; changed = 1; } if (changed) { if (changed < 0) { g_warning ("Faile to stat %s: %s\n", ce->name, strerror(errno)); g_free (realpath); continue; } de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_DELETED, ce->sha1, ce->name); *results = g_list_prepend (*results, de); g_free (realpath); continue; } if (S_ISDIR (ce->ce_mode)) { if (!S_ISDIR (st.st_mode) || !is_empty_dir (realpath, ignore_func)) { de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_DIR_DELETED, ce->sha1, ce->name); *results = g_list_prepend (*results, de); } g_free (realpath); continue; } g_free (realpath); changed = ie_match_stat (index, ce, &st, 0); if (!changed) { ce_mark_uptodate (ce); continue; } de = diff_entry_new (DIFF_TYPE_WORKTREE, DIFF_STATUS_MODIFIED, ce->sha1, ce->name); *results = g_list_prepend (*results, de); } }