diff options
Diffstat (limited to 'update-index.c')
-rw-r--r-- | update-index.c | 222 |
1 files changed, 210 insertions, 12 deletions
diff --git a/update-index.c b/update-index.c index 1efac27c6b..3d7e02db2c 100644 --- a/update-index.c +++ b/update-index.c @@ -6,6 +6,7 @@ #include "cache.h" #include "strbuf.h" #include "quote.h" +#include "tree-walk.h" /* * Default to not allowing changes to the list of files. The @@ -328,7 +329,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, return 0; } -static int chmod_path(int flip, const char *path) +static void chmod_path(int flip, const char *path) { int pos; struct cache_entry *ce; @@ -336,21 +337,24 @@ static int chmod_path(int flip, const char *path) pos = cache_name_pos(path, strlen(path)); if (pos < 0) - return -1; + goto fail; ce = active_cache[pos]; mode = ntohl(ce->ce_mode); if (!S_ISREG(mode)) - return -1; + goto fail; switch (flip) { case '+': ce->ce_mode |= htonl(0111); break; case '-': ce->ce_mode &= htonl(~0111); break; default: - return -1; + goto fail; } active_cache_changed = 1; - return 0; + report("chmod %cx '%s'", flip, path); + return; + fail: + die("git-update-index: cannot chmod %cx '%s'", flip, path); } static struct cache_file cache_file; @@ -360,23 +364,26 @@ static void update_one(const char *path, const char *prefix, int prefix_length) const char *p = prefix_path(prefix, prefix_length, path); if (!verify_path(p)) { fprintf(stderr, "Ignoring path %s\n", path); - return; + goto free_return; } if (mark_valid_only) { if (mark_valid(p)) die("Unable to mark file %s", path); - return; + goto free_return; } if (force_remove) { if (remove_file_from_cache(p)) die("git-update-index: unable to remove %s", path); report("remove '%s'", path); - return; + goto free_return; } if (add_file_to_cache(p)) die("Unable to process file %s", path); report("add '%s'", path); + free_return: + if (p < path || p > path + strlen(path)) + free((char*)p); } static void read_index_info(int line_termination) @@ -469,7 +476,174 @@ static void read_index_info(int line_termination) } static const char update_index_usage[] = -"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--cacheinfo] [--chmod=(+|-)x] [--info-only] [--force-remove] [--stdin] [--index-info] [--ignore-missing] [-z] [--verbose] [--] <file>..."; +"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again] [--ignore-missing] [-z] [--verbose] [--] <file>..."; + +static unsigned char head_sha1[20]; +static unsigned char merge_head_sha1[20]; + +static struct cache_entry *read_one_ent(const char *which, + unsigned char *ent, const char *path, + int namelen, int stage) +{ + unsigned mode; + unsigned char sha1[20]; + int size; + struct cache_entry *ce; + + if (get_tree_entry(ent, path, sha1, &mode)) { + if (which) + error("%s: not in %s branch.", path, which); + return NULL; + } + if (mode == S_IFDIR) { + if (which) + error("%s: not a blob in %s branch.", path, which); + return NULL; + } + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + + memcpy(ce->sha1, sha1, 20); + memcpy(ce->name, path, namelen); + ce->ce_flags = create_ce_flags(namelen, stage); + ce->ce_mode = create_ce_mode(mode); + return ce; +} + +static int unresolve_one(const char *path) +{ + int namelen = strlen(path); + int pos; + int ret = 0; + struct cache_entry *ce_2 = NULL, *ce_3 = NULL; + + /* See if there is such entry in the index. */ + pos = cache_name_pos(path, namelen); + if (pos < 0) { + /* If there isn't, either it is unmerged, or + * resolved as "removed" by mistake. We do not + * want to do anything in the former case. + */ + pos = -pos-1; + if (pos < active_nr) { + struct cache_entry *ce = active_cache[pos]; + if (ce_namelen(ce) == namelen && + !memcmp(ce->name, path, namelen)) { + fprintf(stderr, + "%s: skipping still unmerged path.\n", + path); + goto free_return; + } + } + } + + /* Grab blobs from given path from HEAD and MERGE_HEAD, + * stuff HEAD version in stage #2, + * stuff MERGE_HEAD version in stage #3. + */ + ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); + ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); + + if (!ce_2 || !ce_3) { + ret = -1; + goto free_return; + } + if (!memcmp(ce_2->sha1, ce_3->sha1, 20) && + ce_2->ce_mode == ce_3->ce_mode) { + fprintf(stderr, "%s: identical in both, skipping.\n", + path); + goto free_return; + } + + remove_file_from_cache(path); + if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { + error("%s: cannot add our version to the index.", path); + ret = -1; + goto free_return; + } + if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) + return 0; + error("%s: cannot add their version to the index.", path); + ret = -1; + free_return: + free(ce_2); + free(ce_3); + return ret; +} + +static void read_head_pointers(void) +{ + if (read_ref(git_path("HEAD"), head_sha1)) + die("No HEAD -- no initial commit yet?\n"); + if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { + fprintf(stderr, "Not in the middle of a merge.\n"); + exit(0); + } +} + +static int do_unresolve(int ac, const char **av, + const char *prefix, int prefix_length) +{ + int i; + int err = 0; + + /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we + * are not doing a merge, so exit with success status. + */ + read_head_pointers(); + + for (i = 1; i < ac; i++) { + const char *arg = av[i]; + const char *p = prefix_path(prefix, prefix_length, arg); + err |= unresolve_one(p); + if (p < arg || p > arg + strlen(arg)) + free((char*)p); + } + return err; +} + +static int do_reupdate(int ac, const char **av, + const char *prefix, int prefix_length) +{ + /* Read HEAD and run update-index on paths that are + * merged and already different between index and HEAD. + */ + int pos; + int has_head = 1; + const char **pathspec = get_pathspec(prefix, av + 1); + + if (read_ref(git_path("HEAD"), head_sha1)) + /* If there is no HEAD, that means it is an initial + * commit. Update everything in the index. + */ + has_head = 0; + redo: + for (pos = 0; pos < active_nr; pos++) { + struct cache_entry *ce = active_cache[pos]; + struct cache_entry *old = NULL; + int save_nr; + + if (ce_stage(ce) || !ce_path_match(ce, pathspec)) + continue; + if (has_head) + old = read_one_ent(NULL, head_sha1, + ce->name, ce_namelen(ce), 0); + if (old && ce->ce_mode == old->ce_mode && + !memcmp(ce->sha1, old->sha1, 20)) { + free(old); + continue; /* unchanged */ + } + /* Be careful. The working tree may not have the + * path anymore, in which case, under 'allow_remove', + * or worse yet 'allow_replace', active_nr may decrease. + */ + save_nr = active_nr; + update_one(ce->name + prefix_length, prefix, prefix_length); + if (save_nr != active_nr) + goto redo; + } + return 0; +} int main(int argc, const char **argv) { @@ -478,6 +652,7 @@ int main(int argc, const char **argv) int read_from_stdin = 0; const char *prefix = setup_git_directory(); int prefix_length = prefix ? strlen(prefix) : 0; + char set_executable_bit = 0; git_config(git_default_config); @@ -544,8 +719,7 @@ int main(int argc, const char **argv) !strcmp(path, "--chmod=+x")) { if (argc <= i+1) die("git-update-index: %s <path>", path); - if (chmod_path(path[8], argv[++i])) - die("git-update-index: %s cannot chmod %s", path, argv[i]); + set_executable_bit = path[8]; continue; } if (!strcmp(path, "--assume-unchanged")) { @@ -581,6 +755,20 @@ int main(int argc, const char **argv) read_index_info(line_termination); break; } + if (!strcmp(path, "--unresolve")) { + has_errors = do_unresolve(argc - i, argv + i, + prefix, prefix_length); + if (has_errors) + active_cache_changed = 0; + goto finish; + } + if (!strcmp(path, "--again")) { + has_errors = do_reupdate(argc - i, argv + i, + prefix, prefix_length); + if (has_errors) + active_cache_changed = 0; + goto finish; + } if (!strcmp(path, "--ignore-missing")) { not_new = 1; continue; @@ -594,12 +782,15 @@ int main(int argc, const char **argv) die("unknown option %s", path); } update_one(path, prefix, prefix_length); + if (set_executable_bit) + chmod_path(set_executable_bit, path); } if (read_from_stdin) { struct strbuf buf; strbuf_init(&buf); while (1) { char *path_name; + const char *p; read_line(&buf, stdin, line_termination); if (buf.eof) break; @@ -607,11 +798,18 @@ int main(int argc, const char **argv) path_name = unquote_c_style(buf.buf, NULL); else path_name = buf.buf; - update_one(path_name, prefix, prefix_length); + p = prefix_path(prefix, prefix_length, path_name); + update_one(p, NULL, 0); + if (set_executable_bit) + chmod_path(set_executable_bit, p); + if (p < path_name || p > path_name + strlen(path_name)) + free((char*) p); if (path_name != buf.buf) free(path_name); } } + + finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_index_file(&cache_file)) |