From: Junio C Hamano Date: Mon, 3 Aug 2015 18:01:17 +0000 (-0700) Subject: Merge branch 'mh/init-delete-refs-api' X-Git-Tag: v2.6.0-rc0~105 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/be9cb560e31c76c00760dadb151b5e3059970586?ds=inline;hp=-c Merge branch 'mh/init-delete-refs-api' Clean up refs API and make "git clone" less intimate with the implementation detail. * mh/init-delete-refs-api: delete_ref(): use the usual convention for old_sha1 cmd_update_ref(): make logic more straightforward update_ref(): don't read old reference value before delete check_branch_commit(): make first parameter const refs.h: add some parameter names to function declarations refs: move the remaining ref module declarations to refs.h initial_ref_transaction_commit(): check for ref D/F conflicts initial_ref_transaction_commit(): check for duplicate refs refs: remove some functions from the module's public interface initial_ref_transaction_commit(): function for initial ref creation repack_without_refs(): make function private prune_refs(): use delete_refs() prune_remote(): use delete_refs() delete_refs(): bail early if the packed-refs file cannot be rewritten delete_refs(): make error message more generic delete_refs(): new function for the refs API delete_ref(): handle special case more explicitly remove_branches(): remove temporary delete_ref(): move declaration to refs.h --- be9cb560e31c76c00760dadb151b5e3059970586 diff --combined builtin/blame.c index a22ac17407,1c998cb26c..272a222687 --- a/builtin/blame.c +++ b/builtin/blame.c @@@ -6,6 -6,7 +6,7 @@@ */ #include "cache.h" + #include "refs.h" #include "builtin.h" #include "blob.h" #include "commit.h" @@@ -2176,14 -2177,6 +2177,14 @@@ static int git_blame_config(const char blank_boundary = git_config_bool(var, value); return 0; } + if (!strcmp(var, "blame.showemail")) { + int *output_option = cb; + if (git_config_bool(var, value)) + *output_option |= OUTPUT_SHOW_EMAIL; + else + *output_option &= ~OUTPUT_SHOW_EMAIL; + return 0; + } if (!strcmp(var, "blame.date")) { if (!value) return config_error_nonbool(var); @@@ -2528,7 -2521,7 +2529,7 @@@ int cmd_blame(int argc, const char **ar unsigned int range_i; long anchor; - git_config(git_blame_config, NULL); + git_config(git_blame_config, &output_option); init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV); diff --combined builtin/clone.c index a72ff7e009,8539b8de64..303a3a7eb6 --- a/builtin/clone.c +++ b/builtin/clone.c @@@ -147,7 -147,6 +147,7 @@@ static char *get_repo_path(const char * static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { const char *end = repo + strlen(repo), *start; + size_t len; char *dir; /* @@@ -174,12 -173,20 +174,12 @@@ /* * Strip .{bundle,git}. */ - if (is_bundle) { - if (end - start > 7 && !strncmp(end - 7, ".bundle", 7)) - end -= 7; - } else { - if (end - start > 4 && !strncmp(end - 4, ".git", 4)) - end -= 4; - } + strip_suffix(start, is_bundle ? ".bundle" : ".git" , &len); - if (is_bare) { - struct strbuf result = STRBUF_INIT; - strbuf_addf(&result, "%.*s.git", (int)(end - start), start); - dir = strbuf_detach(&result, NULL); - } else - dir = xstrndup(start, end - start); + if (is_bare) + dir = xstrfmt("%.*s.git", (int)len, start); + else + dir = xstrndup(start, len); /* * Replace sequences of 'control' characters and whitespace * with one ascii space, remove leading and trailing spaces. @@@ -484,16 -491,26 +484,26 @@@ static void write_remote_refs(const str { const struct ref *r; - lock_packed_refs(LOCK_DIE_ON_ERROR); + struct ref_transaction *t; + struct strbuf err = STRBUF_INIT; + + t = ref_transaction_begin(&err); + if (!t) + die("%s", err.buf); for (r = local_refs; r; r = r->next) { if (!r->peer_ref) continue; - add_packed_ref(r->peer_ref->name, r->old_sha1); + if (ref_transaction_create(t, r->peer_ref->name, r->old_sha1, + 0, NULL, &err)) + die("%s", err.buf); } - if (commit_packed_refs()) - die_errno("unable to overwrite old ref-pack file"); + if (initial_ref_transaction_commit(t, &err)) + die("%s", err.buf); + + strbuf_release(&err); + ref_transaction_free(t); } static void write_followtags(const struct ref *refs, const char *msg) diff --combined builtin/log.c index 878104943f,3caa91776a..93025d08cd --- a/builtin/log.c +++ b/builtin/log.c @@@ -5,6 -5,7 +5,7 @@@ * 2006 Junio Hamano */ #include "cache.h" + #include "refs.h" #include "color.h" #include "commit.h" #include "diff.h" @@@ -795,7 -796,7 +796,7 @@@ static int reopen_stdout(struct commit static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) { struct rev_info check_rev; - struct commit *commit; + struct commit *commit, *c1, *c2; struct object *o1, *o2; unsigned flags1, flags2; @@@ -803,11 -804,9 +804,11 @@@ die(_("Need exactly one range.")); o1 = rev->pending.objects[0].item; - flags1 = o1->flags; o2 = rev->pending.objects[1].item; + flags1 = o1->flags; flags2 = o2->flags; + c1 = lookup_commit_reference(o1->sha1); + c2 = lookup_commit_reference(o2->sha1); if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) die(_("Not a range.")); @@@ -829,8 -828,10 +830,8 @@@ } /* reset for next revision walk */ - clear_commit_marks((struct commit *)o1, - SEEN | UNINTERESTING | SHOWN | ADDED); - clear_commit_marks((struct commit *)o2, - SEEN | UNINTERESTING | SHOWN | ADDED); + clear_commit_marks(c1, SEEN | UNINTERESTING | SHOWN | ADDED); + clear_commit_marks(c2, SEEN | UNINTERESTING | SHOWN | ADDED); o1->flags = flags1; o2->flags = flags2; } diff --combined cache.h index dcc9d3f40b,1c00098f87..6997b2cd47 --- a/cache.h +++ b/cache.h @@@ -397,7 -397,6 +397,7 @@@ static inline enum object_type object_t #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH" #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES" #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS" +#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE" #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" #define ATTRIBUTE_MACRO_PREFIX "[attr]" @@@ -447,17 -446,7 +447,17 @@@ extern int get_common_dir(struct strbu extern const char *get_git_namespace(void); extern const char *strip_namespace(const char *namespaced_ref); extern const char *get_git_work_tree(void); -extern const char *read_gitfile(const char *path); + +#define READ_GITFILE_ERR_STAT_FAILED 1 +#define READ_GITFILE_ERR_NOT_A_FILE 2 +#define READ_GITFILE_ERR_OPEN_FAILED 3 +#define READ_GITFILE_ERR_READ_FAILED 4 +#define READ_GITFILE_ERR_INVALID_FORMAT 5 +#define READ_GITFILE_ERR_NO_PATH 6 +#define READ_GITFILE_ERR_NOT_A_REPO 7 +#define READ_GITFILE_ERR_TOO_LARGE 8 +extern const char *read_gitfile_gently(const char *path, int *return_error_code); +#define read_gitfile(path) read_gitfile_gently((path), NULL) extern const char *resolve_gitdir(const char *suspect); extern void set_git_work_tree(const char *tree); @@@ -596,8 -585,6 +596,6 @@@ extern void update_index_if_able(struc extern int hold_locked_index(struct lock_file *, int); extern void set_alternate_index_output(const char *); - extern int delete_ref(const char *, const unsigned char *sha1, unsigned int flags); - /* Environment bits from configuration mechanism */ extern int trust_executable_bit; extern int trust_ctime; @@@ -633,7 -620,6 +631,7 @@@ extern unsigned long pack_size_limit_cf * been sought but there were none. */ extern int check_replace_refs; +extern char *git_replace_ref_base; extern int fsync_object_files; extern int core_preload_index; @@@ -955,17 -941,8 +953,17 @@@ extern int has_sha1_pack(const unsigne * Return true iff we have an object named sha1, whether local or in * an alternate object database, and whether packed or loose. This * function does not respect replace references. + * + * If the QUICK flag is set, do not re-check the pack directory + * when we cannot find the object (this means we may give a false + * negative answer if another process is simultaneously repacking). */ -extern int has_sha1_file(const unsigned char *sha1); +#define HAS_SHA1_QUICK 0x1 +extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags); +static inline int has_sha1_file(const unsigned char *sha1) +{ + return has_sha1_file_with_flags(sha1, 0); +} /* * Return true iff an alternate object database has a loose object @@@ -1032,76 -1009,10 +1030,10 @@@ extern int get_oid_hex(const char *hex extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */ - extern int read_ref_full(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); - extern int read_ref(const char *refname, unsigned char *sha1); - /* - * Resolve a reference, recursively following symbolic refererences. - * - * Store the referred-to object's name in sha1 and return the name of - * the non-symbolic reference that ultimately pointed at it. The - * return value, if not NULL, is a pointer into either a static buffer - * or the input ref. - * - * If the reference cannot be resolved to an object, the behavior - * depends on the RESOLVE_REF_READING flag: - * - * - If RESOLVE_REF_READING is set, return NULL. - * - * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of - * the last reference name in the chain, which will either be a non-symbolic - * reference or an undefined reference. If this is a prelude to - * "writing" to the ref, the return value is the name of the ref - * that will actually be created or changed. - * - * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one - * level of symbolic reference. The value stored in sha1 for a symbolic - * reference will always be null_sha1 in this case, and the return - * value is the reference that the symref refers to directly. - * - * If flags is non-NULL, set the value that it points to the - * combination of REF_ISPACKED (if the reference was found among the - * packed references), REF_ISSYMREF (if the initial reference was a - * symbolic reference), REF_BAD_NAME (if the reference name is ill - * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN - * (if the ref is malformed or has a bad name). See refs.h for more detail - * on each flag. - * - * If ref is not a properly-formatted, normalized reference, return - * NULL. If more than MAXDEPTH recursive symbolic lookups are needed, - * give up and return NULL. - * - * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their - * name is invalid according to git-check-ref-format(1). If the name - * is bad then the value stored in sha1 will be null_sha1 and the two - * flags REF_ISBROKEN and REF_BAD_NAME will be set. - * - * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/ - * directory and do not consist of all caps and underscores cannot be - * resolved. The function returns NULL for such ref names. - * Caps and underscores refers to the special refs, such as HEAD, - * FETCH_HEAD and friends, that all live outside of the refs/ directory. - */ - #define RESOLVE_REF_READING 0x01 - #define RESOLVE_REF_NO_RECURSE 0x02 - #define RESOLVE_REF_ALLOW_BAD_NAME 0x04 - extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags); - extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags); - - extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); - extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); extern int interpret_branch_name(const char *str, int len, struct strbuf *); extern int get_sha1_mb(const char *str, unsigned char *sha1); - /* - * Return true iff abbrev_name is a possible abbreviation for - * full_name according to the rules defined by ref_rev_parse_rules in - * refs.c. - */ - extern int refname_match(const char *abbrev_name, const char *full_name); - - extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg); extern int validate_headref(const char *ref); extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); @@@ -1719,6 -1630,5 +1651,6 @@@ int stat_validity_check(struct stat_val void stat_validity_update(struct stat_validity *sv, int fd); int versioncmp(const char *s1, const char *s2); +void sleep_millisec(int millisec); #endif /* CACHE_H */ diff --combined refs.c index 227cb50c44,c5086ae70c..fb568d7a31 --- a/refs.c +++ b/refs.c @@@ -1314,7 -1314,13 +1314,13 @@@ static struct ref_dir *get_packed_refs( return get_packed_ref_dir(get_packed_ref_cache(refs)); } - void add_packed_ref(const char *refname, const unsigned char *sha1) + /* + * Add a reference to the in-memory packed reference cache. This may + * only be called while the packed-refs file is locked (see + * lock_packed_refs()). To actually write the packed-refs file, call + * commit_packed_refs(). + */ + static void add_packed_ref(const char *refname, const unsigned char *sha1) { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(&ref_cache); @@@ -1373,34 -1379,19 +1379,34 @@@ static void read_loose_refs(const char create_dir_entry(refs, refname.buf, refname.len, 1)); } else { + int read_ok; + if (*refs->name) { hashclr(sha1); flag = 0; - if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) { - hashclr(sha1); - flag |= REF_ISBROKEN; - } - } else if (read_ref_full(refname.buf, - RESOLVE_REF_READING, - sha1, &flag)) { + read_ok = !resolve_gitlink_ref(refs->name, + refname.buf, sha1); + } else { + read_ok = !read_ref_full(refname.buf, + RESOLVE_REF_READING, + sha1, &flag); + } + + if (!read_ok) { hashclr(sha1); flag |= REF_ISBROKEN; + } else if (is_null_sha1(sha1)) { + /* + * It is so astronomically unlikely + * that NULL_SHA1 is the SHA-1 of an + * actual object that we consider its + * appearance in a loose reference + * file to be repo corruption + * (probably due to a software bug). + */ + flag |= REF_ISBROKEN; } + if (check_refname_format(refname.buf, REFNAME_ALLOW_ONELEVEL)) { if (!refname_is_safe(refname.buf)) @@@ -1741,9 -1732,11 +1747,11 @@@ const char *resolve_ref_unsafe(const ch return ret; } - char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags) + char *resolve_refdup(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags) { - return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags)); + return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags, + sha1, flags)); } /* The argument to filter_refs */ @@@ -2122,8 -2115,7 +2130,8 @@@ int for_each_remote_ref_submodule(cons int for_each_replace_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data); + return do_for_each_ref(&ref_cache, git_replace_ref_base, fn, + strlen(git_replace_ref_base), 0, cb_data); } int head_ref_namespaced(each_ref_fn fn, void *cb_data) @@@ -2531,8 -2523,12 +2539,12 @@@ static int write_packed_entry_fn(struc return 0; } - /* This should return a meaningful errno on failure */ - int lock_packed_refs(int flags) + /* + * Lock the packed-refs file for writing. Flags is passed to + * hold_lock_file_for_update(). Return 0 on success. On errors, set + * errno appropriately and return a nonzero value. + */ + static int lock_packed_refs(int flags) { static int timeout_configured = 0; static int timeout_value = 1000; @@@ -2562,10 -2558,12 +2574,12 @@@ } /* - * Commit the packed refs changes. - * On error we must make sure that errno contains a meaningful value. + * Write the current version of the packed refs cache from memory to + * disk. The packed-refs file must already be locked for writing (see + * lock_packed_refs()). Return zero on success. On errors, set errno + * and return a nonzero value */ - int commit_packed_refs(void) + static int commit_packed_refs(void) { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(&ref_cache); @@@ -2594,7 -2592,12 +2608,12 @@@ return error; } - void rollback_packed_refs(void) + /* + * Rollback the lockfile for the packed-refs file, and discard the + * in-memory packed reference cache. (The packed-refs file will be + * read anew if it is needed again after this function is called.) + */ + static void rollback_packed_refs(void) { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(&ref_cache); @@@ -2752,7 -2755,14 +2771,14 @@@ int pack_refs(unsigned int flags return 0; } - int repack_without_refs(struct string_list *refnames, struct strbuf *err) + /* + * Rewrite the packed-refs file, omitting any refs listed in + * 'refnames'. On error, leave packed-refs unchanged, write an error + * message to 'err', and return a nonzero value. + * + * The refs in 'refnames' needn't be sorted. `err` must not be NULL. + */ + static int repack_without_refs(struct string_list *refnames, struct strbuf *err) { struct ref_dir *packed; struct string_list_item *refname; @@@ -2817,15 -2827,15 +2843,15 @@@ static int delete_ref_loose(struct ref_ return 0; } - int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags) + int delete_ref(const char *refname, const unsigned char *old_sha1, + unsigned int flags) { struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_delete(transaction, refname, - (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL, + ref_transaction_delete(transaction, refname, old_sha1, flags, NULL, &err) || ref_transaction_commit(transaction, &err)) { error("%s", err.buf); @@@ -2838,6 -2848,44 +2864,44 @@@ return 0; } + int delete_refs(struct string_list *refnames) + { + struct strbuf err = STRBUF_INIT; + int i, result = 0; + + if (!refnames->nr) + return 0; + + result = repack_without_refs(refnames, &err); + if (result) { + /* + * If we failed to rewrite the packed-refs file, then + * it is unsafe to try to remove loose refs, because + * doing so might expose an obsolete packed value for + * a reference that might even point at an object that + * has been garbage collected. + */ + if (refnames->nr == 1) + error(_("could not delete reference %s: %s"), + refnames->items[0].string, err.buf); + else + error(_("could not delete references: %s"), err.buf); + + goto out; + } + + for (i = 0; i < refnames->nr; i++) { + const char *refname = refnames->items[i].string; + + if (delete_ref(refname, NULL, 0)) + result |= error(_("could not remove reference %s"), refname); + } + + out: + strbuf_release(&err); + return result; + } + /* * People using contrib's git-new-workdir have .git/logs/refs -> * /some/other/path/.git/logs/refs, and that may live on another device. @@@ -4039,6 -4087,98 +4103,98 @@@ cleanup return ret; } + static int ref_present(const char *refname, + const struct object_id *oid, int flags, void *cb_data) + { + struct string_list *affected_refnames = cb_data; + + return string_list_has_string(affected_refnames, refname); + } + + int initial_ref_transaction_commit(struct ref_transaction *transaction, + struct strbuf *err) + { + struct ref_dir *loose_refs = get_loose_refs(&ref_cache); + struct ref_dir *packed_refs = get_packed_refs(&ref_cache); + int ret = 0, i; + int n = transaction->nr; + struct ref_update **updates = transaction->updates; + struct string_list affected_refnames = STRING_LIST_INIT_NODUP; + + assert(err); + + if (transaction->state != REF_TRANSACTION_OPEN) + die("BUG: commit called for transaction that is not open"); + + /* Fail if a refname appears more than once in the transaction: */ + for (i = 0; i < n; i++) + string_list_append(&affected_refnames, updates[i]->refname); + string_list_sort(&affected_refnames); + if (ref_update_reject_duplicates(&affected_refnames, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + + /* + * It's really undefined to call this function in an active + * repository or when there are existing references: we are + * only locking and changing packed-refs, so (1) any + * simultaneous processes might try to change a reference at + * the same time we do, and (2) any existing loose versions of + * the references that we are setting would have precedence + * over our values. But some remote helpers create the remote + * "HEAD" and "master" branches before calling this function, + * so here we really only check that none of the references + * that we are creating already exists. + */ + if (for_each_rawref(ref_present, &affected_refnames)) + die("BUG: initial ref transaction called with existing refs"); + + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if ((update->flags & REF_HAVE_OLD) && + !is_null_sha1(update->old_sha1)) + die("BUG: initial ref transaction with old_sha1 set"); + if (verify_refname_available(update->refname, + &affected_refnames, NULL, + loose_refs, err) || + verify_refname_available(update->refname, + &affected_refnames, NULL, + packed_refs, err)) { + ret = TRANSACTION_NAME_CONFLICT; + goto cleanup; + } + } + + if (lock_packed_refs(0)) { + strbuf_addf(err, "unable to lock packed-refs file: %s", + strerror(errno)); + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if ((update->flags & REF_HAVE_NEW) && + !is_null_sha1(update->new_sha1)) + add_packed_ref(update->refname, update->new_sha1); + } + + if (commit_packed_refs()) { + strbuf_addf(err, "unable to commit packed-refs file: %s", + strerror(errno)); + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + + cleanup: + transaction->state = REF_TRANSACTION_CLOSED; + string_list_clear(&affected_refnames, 0); + return ret; + } + char *shorten_unambiguous_ref(const char *refname, int strict) { int i;