X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=apply.c;h=155fbe84da8b6d8dfb231fe508b68a36d52ffc60;hb=d402d5566fdf226697a386dfb9858e5d954e9b91;hp=964df2db10c22d6ed0b89532e0af8cf9a43e607c;hpb=c1067050ce58b5b39f528fe634732da858664603;p=git.git diff --git a/apply.c b/apply.c index 964df2db..155fbe84 100644 --- a/apply.c +++ b/apply.c @@ -5,26 +5,17 @@ * * This applies patches on top of some (arbitrary) version of the SCM. * - * NOTE! It does all its work in the index file, and only cares about - * the files in the working directory if you tell it to "merge" the - * patch apply. - * - * Even when merging it always takes the source from the index, and - * uses the working tree as a "branch" for a 3-way merge. */ #include #include #include "cache.h" -// We default to the merge behaviour, since that's what most people would -// expect. -// // --check turns on checking that the working tree matches the // files that are being modified, but doesn't apply the patch // --stat does just a diffstat, and doesn't actually apply // --show-files shows the directory changes +// --show-index-info shows the old and new index info for paths if available. // -static int merge_patch = 1; static int check_index = 0; static int write_index = 0; static int diffstat = 0; @@ -32,8 +23,9 @@ static int summary = 0; static int check = 0; static int apply = 1; static int show_files = 0; +static int show_index_info = 0; static const char apply_usage[] = -"git-apply [--no-merge] [--stat] [--summary] [--check] [--index] [--apply] [--show-files] ..."; +"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] [--show-index-info] ..."; /* * For "diff-stat" like behaviour, we keep track of the biggest change @@ -66,6 +58,8 @@ struct patch { struct fragment *fragments; char *result; unsigned long resultsize; + char old_sha1_prefix[41]; + char new_sha1_prefix[41]; struct patch *next; }; @@ -344,6 +338,38 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch) return 0; } +static int gitdiff_index(const char *line, struct patch *patch) +{ + /* index line is N hexadecimal, "..", N hexadecimal, + * and optional space with octal mode. + */ + const char *ptr, *eol; + int len; + + ptr = strchr(line, '.'); + if (!ptr || ptr[1] != '.' || 40 <= ptr - line) + return 0; + len = ptr - line; + memcpy(patch->old_sha1_prefix, line, len); + patch->old_sha1_prefix[len] = 0; + + line = ptr + 2; + ptr = strchr(line, ' '); + eol = strchr(line, '\n'); + + if (!ptr || eol < ptr) + ptr = eol; + len = ptr - line; + + if (40 <= len) + return 0; + memcpy(patch->new_sha1_prefix, line, len); + patch->new_sha1_prefix[len] = 0; + if (*ptr == ' ') + patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8); + return 0; +} + /* * This is normal for a diff that doesn't change anything: we'll fall through * into the next diff. Tell the parser to break out. @@ -448,6 +474,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch { "rename to ", gitdiff_renamedst }, { "similarity index ", gitdiff_similarity }, { "dissimilarity index ", gitdiff_dissimilarity }, + { "index ", gitdiff_index }, { "", gitdiff_unrecognized }, }; int i; @@ -676,7 +703,10 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s /* We allow "\ No newline at end of file". Depending * on locale settings when the patch was produced we * don't know what this line looks like. The only - * thing we do know is that it begins with "\ ". */ + * thing we do know is that it begins with "\ ". + * Checking for 12 is just for sanity check -- any + * l10n of "\ No newline..." is at least that long. + */ case '\\': if (len < 12 || memcmp(line, "\\ ", 2)) return -1; @@ -723,6 +753,16 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc return offset; } +static inline int metadata_changes(struct patch *patch) +{ + return patch->is_rename > 0 || + patch->is_copy > 0 || + patch->is_new > 0 || + patch->is_delete || + (patch->old_mode && patch->new_mode && + patch->old_mode != patch->new_mode); +} + static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) { int hdrsize, patchsize; @@ -733,6 +773,9 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch); + if (!patchsize && !metadata_changes(patch)) + die("patch with only garbage at line %d", linenr); + return offset + hdrsize + patchsize; } @@ -1017,17 +1060,39 @@ static int check_patch(struct patch *patch) if (old_name) { int changed; + int stat_ret = lstat(old_name, &st); - if (lstat(old_name, &st) < 0) - return error("%s: %s", old_name, strerror(errno)); if (check_index) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) - return error("%s: does not exist in index", old_name); + return error("%s: does not exist in index", + old_name); + if (stat_ret < 0) { + struct checkout costate; + if (errno != ENOENT) + return error("%s: %s", old_name, + strerror(errno)); + /* checkout */ + costate.base_dir = ""; + costate.base_dir_len = 0; + costate.force = 0; + costate.quiet = 0; + costate.not_new = 0; + costate.refresh_cache = 1; + if (checkout_entry(active_cache[pos], + &costate) || + lstat(old_name, &st)) + return -1; + } + changed = ce_match_stat(active_cache[pos], &st); if (changed) - return error("%s: does not match index", old_name); + return error("%s: does not match index", + old_name); } + else if (stat_ret < 0) + return error("%s: %s", old_name, strerror(errno)); + if (patch->is_new < 0) patch->is_new = 0; st.st_mode = ntohl(create_ce_mode(st.st_mode)); @@ -1108,6 +1173,36 @@ static void show_file_list(struct patch *patch) } } +static inline int is_null_sha1(const unsigned char *sha1) +{ + return !memcmp(sha1, null_sha1, 20); +} + +static void show_index_list(struct patch *list) +{ + struct patch *patch; + + /* Once we start supporting the reverse patch, it may be + * worth showing the new sha1 prefix, but until then... + */ + for (patch = list; patch; patch = patch->next) { + const unsigned char *sha1_ptr; + unsigned char sha1[20]; + const char *name; + + name = patch->old_name ? patch->old_name : patch->new_name; + if (patch->is_new) + sha1_ptr = null_sha1; + else if (get_sha1(patch->old_sha1_prefix, sha1)) + die("sha1 information is lacking or useless (%s).", + name); + else + sha1_ptr = sha1; + printf("%06o %s %s\n",patch->old_mode, + sha1_to_hex(sha1_ptr), name); + } +} + static void stat_patch_list(struct patch *patch) { int files, adds, dels; @@ -1448,6 +1543,9 @@ static int apply_patch(int fd) if (show_files) show_file_list(list); + if (show_index_info) + show_index_list(list); + if (diffstat) stat_patch_list(list); @@ -1479,11 +1577,6 @@ int main(int argc, char **argv) excludes = x; continue; } - /* NEEDSWORK: this does not do anything at this moment. */ - if (!strcmp(arg, "--no-merge")) { - merge_patch = 0; - continue; - } if (!strcmp(arg, "--stat")) { apply = 0; diffstat = 1; @@ -1511,6 +1604,11 @@ int main(int argc, char **argv) show_files = 1; continue; } + if (!strcmp(arg, "--show-index-info")) { + apply = 0; + show_index_info = 1; + continue; + } fd = open(arg, O_RDONLY); if (fd < 0) usage(apply_usage);