#define CACHE_SIGNATURE 0x44495243 /* "DIRC" */
struct cache_header {
- unsigned int hdr_signature;
- unsigned int hdr_version;
- unsigned int hdr_entries;
+ uint32_t hdr_signature;
+ uint32_t hdr_version;
+ uint32_t hdr_entries;
};
#define INDEX_FORMAT_LB 2
* check it for equality in the 32 bits we save.
*/
struct cache_time {
- unsigned int sec;
- unsigned int nsec;
+ uint32_t sec;
+ uint32_t nsec;
};
struct stat_data {
#error "CE_EXTENDED_FLAGS out of range"
#endif
+struct pathspec;
+
/*
* Copy the sha1 and stat state of a cache entry from one to
* another. But we never change the name, or the hash state!
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
+#define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
+#define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
+#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
+#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
+#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
/*
* This environment variable is expected to contain a boolean indicating
extern int is_inside_git_dir(void);
extern char *git_work_tree_cfg;
extern int is_inside_work_tree(void);
- extern int have_git_dir(void);
extern const char *get_git_dir(void);
extern int is_git_directory(const char *path);
extern char *get_object_directory(void);
extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void);
extern char *prefix_path(const char *prefix, int len, const char *path);
+extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
extern const char *prefix_filename(const char *prefix, int len, const char *path);
extern int check_filename(const char *prefix, const char *name);
extern void verify_filename(const char *prefix,
/* Initialize and use the cache information */
extern int read_index(struct index_state *);
-extern int read_index_preload(struct index_state *, const char **pathspec);
+extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
extern int read_index_from(struct index_state *, const char *path);
extern int is_index_unborn(struct index_state *);
extern int read_index_unmerged(struct index_state *);
extern int discard_index(struct index_state *);
extern int unmerged_index(const struct index_state *);
extern int verify_path(const char *path);
+extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
+extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
extern int index_name_pos(const struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-#define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */
-
-struct pathspec {
- const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
- int nr;
- unsigned int has_wildcard:1;
- unsigned int recursive:1;
- int max_depth;
- struct pathspec_item {
- const char *match;
- int len;
- int nowildcard_len;
- int flags;
- } *items;
-};
-
-extern int init_pathspec(struct pathspec *, const char **);
-extern void free_pathspec(struct pathspec *);
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
-extern int limit_pathspec_to_literal(void);
-
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
-extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
+extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
struct lock_file {
struct lock_file *next;
const char *real_path(const char *path);
const char *real_path_if_valid(const char *path);
const char *absolute_path(const char *path);
+const char *remove_leading_path(const char *in, const char *prefix);
const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
+int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
int normalize_path_copy(char *dst, const char *src);
int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix);
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, struct strbuf *);
+extern int interpret_branch_name(const char *str, int len, struct strbuf *);
extern int get_sha1_mb(const char *str, unsigned char *sha1);
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
*/
extern int split_ident_line(struct ident_split *, const char *, int);
+/*
+ * Compare split idents for equality or strict ordering. Note that we
+ * compare only the ident part of the line, ignoring any timestamp.
+ *
+ * Because there are two fields, we must choose one as the primary key; we
+ * currently arbitrarily pick the email.
+ */
+extern int ident_cmp(const struct ident_split *, const struct ident_split *);
+
struct checkout {
const char *base_dir;
int base_dir_len;
refresh_cache:1;
};
+#define TEMPORARY_FILENAME_LENGTH 25
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
struct cache_def {
struct packed_git *p;
};
-struct ref {
- struct ref *next;
- unsigned char old_sha1[20];
- unsigned char new_sha1[20];
- char *symref;
- unsigned int
- force:1,
- forced_update:1,
- deletion:1,
- matched:1;
-
- /*
- * Order is important here, as we write to FETCH_HEAD
- * in numeric order. And the default NOT_FOR_MERGE
- * should be 0, so that xcalloc'd structures get it
- * by default.
- */
- enum {
- FETCH_HEAD_MERGE = -1,
- FETCH_HEAD_NOT_FOR_MERGE = 0,
- FETCH_HEAD_IGNORE = 1
- } fetch_head_status;
-
- enum {
- REF_STATUS_NONE = 0,
- REF_STATUS_OK,
- REF_STATUS_REJECT_NONFASTFORWARD,
- REF_STATUS_REJECT_ALREADY_EXISTS,
- REF_STATUS_REJECT_NODELETE,
- REF_STATUS_REJECT_FETCH_FIRST,
- REF_STATUS_REJECT_NEEDS_FORCE,
- REF_STATUS_UPTODATE,
- REF_STATUS_REMOTE_REJECT,
- REF_STATUS_EXPECTING_REPORT
- } status;
- char *remote_status;
- struct ref *peer_ref; /* when renaming */
- char name[FLEX_ARRAY]; /* more */
-};
-
-#define REF_NORMAL (1u << 0)
-#define REF_HEADS (1u << 1)
-#define REF_TAGS (1u << 2)
-
-extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
-
-#define CONNECT_VERBOSE (1u << 0)
-extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
-extern int finish_connect(struct child_process *conn);
-extern int git_connection_is_socket(struct child_process *conn);
-struct extra_have_objects {
- int nr, alloc;
- unsigned char (*array)[20];
-};
-extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
- struct ref **list, unsigned int flags,
- struct extra_have_objects *);
-extern int server_supports(const char *feature);
-extern int parse_feature_request(const char *features, const char *feature);
-extern const char *server_feature_value(const char *feature, int *len_ret);
-extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
-
extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
/* A hook for count-objects to report invalid files in pack directory */
extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *);
+extern int64_t git_config_int64(const char *, const char *);
extern unsigned long git_config_ulong(const char *, const char *);
extern int git_config_bool_or_int(const char *, const char *, int *);
extern int git_config_bool(const char *, const char *);
* return 0 if success, 1 - if addition of a file failed and
* ADD_FILES_IGNORE_ERRORS was specified in flags
*/
-int add_files_to_cache(const char *prefix, const char **pathspec, int flags);
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
/* diff.c */
extern int diff_auto_refresh_index;
#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)
/* ls-files */
-int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
+int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
char *alias_lookup(const char *alias);
{
int component_len, component_count = 0;
+ if (!strcmp(refname, "@"))
+ /* Refname is a single character '@'. */
+ return -1;
+
while (1) {
/* We are at the start of a path component. */
component_len = check_refname_component(refname, flags);
return refs;
}
- void invalidate_ref_cache(const char *submodule)
- {
- struct ref_cache *refs = get_ref_cache(submodule);
- clear_packed_ref_cache(refs);
- clear_loose_ref_cache(refs);
- }
-
/* The length of a peeled reference line in packed-refs, including EOL: */
#define PEELED_LINE_LENGTH 42
static char *substitute_branch_name(const char **string, int *len)
{
struct strbuf buf = STRBUF_INIT;
- int ret = interpret_branch_name(*string, &buf);
+ int ret = interpret_branch_name(*string, *len, &buf);
if (ret == *len) {
size_t size;
}
struct ref_lock *lock_any_ref_for_update(const char *refname,
- const unsigned char *old_sha1, int flags)
+ const unsigned char *old_sha1,
+ int flags, int *type_p)
{
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
return NULL;
- return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
+ return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
}
/*
return 0;
}
-static int repack_without_ref(const char *refname)
+static int repack_without_refs(const char **refnames, int n)
{
struct ref_dir *packed;
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
struct string_list_item *ref_to_delete;
+ int i, removed = 0;
+
+ /* Look for a packed ref */
+ for (i = 0; i < n; i++)
+ if (get_packed_ref(refnames[i]))
+ break;
- if (!get_packed_ref(refname))
- return 0; /* refname does not exist in packed refs */
+ /* Avoid locking if we have nothing to do */
+ if (i == n)
+ return 0; /* no refname exists in packed refs */
if (lock_packed_refs(0)) {
unable_to_lock_error(git_path("packed-refs"), errno);
- return error("cannot delete '%s' from packed refs", refname);
+ return error("cannot delete '%s' from packed refs", refnames[i]);
}
packed = get_packed_refs(&ref_cache);
- /* Remove refname from the cache: */
- if (remove_entry(packed, refname) == -1) {
+ /* Remove refnames from the cache */
+ for (i = 0; i < n; i++)
+ if (remove_entry(packed, refnames[i]) != -1)
+ removed = 1;
+ if (!removed) {
/*
- * The packed entry disappeared while we were
+ * All packed entries disappeared while we were
* acquiring the lock.
*/
rollback_packed_refs();
return 0;
}
- /* Remove any other accumulated cruft: */
+ /* Remove any other accumulated cruft */
do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete);
for_each_string_list_item(ref_to_delete, &refs_to_delete) {
if (remove_entry(packed, ref_to_delete->string) == -1)
die("internal error");
}
- /* Write what remains: */
+ /* Write what remains */
return commit_packed_refs();
}
-int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
+static int repack_without_ref(const char *refname)
{
- struct ref_lock *lock;
- int err, i = 0, ret = 0, flag = 0;
+ return repack_without_refs(&refname, 1);
+}
- lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
- if (!lock)
- return 1;
+static int delete_ref_loose(struct ref_lock *lock, int flag)
+{
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
/* loose */
- i = strlen(lock->lk->filename) - 5; /* .lock */
+ int err, i = strlen(lock->lk->filename) - 5; /* .lock */
+
lock->lk->filename[i] = 0;
err = unlink_or_warn(lock->lk->filename);
- if (err && errno != ENOENT)
- ret = 1;
-
lock->lk->filename[i] = '.';
+ if (err && errno != ENOENT)
+ return 1;
}
+ return 0;
+}
+
+int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
+{
+ struct ref_lock *lock;
+ int ret = 0, flag = 0;
+
+ lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
+ if (!lock)
+ return 1;
+ ret |= delete_ref_loose(lock, flag);
+
/* removing the loose one could have resurrected an earlier
* packed one. Also, if it was not loose we need to repack
* without it.
return retval;
}
-int update_ref(const char *action, const char *refname,
- const unsigned char *sha1, const unsigned char *oldval,
- int flags, enum action_on_err onerr)
+static struct ref_lock *update_ref_lock(const char *refname,
+ const unsigned char *oldval,
+ int flags, int *type_p,
+ enum action_on_err onerr)
{
- static struct ref_lock *lock;
- lock = lock_any_ref_for_update(refname, oldval, flags);
+ struct ref_lock *lock;
+ lock = lock_any_ref_for_update(refname, oldval, flags, type_p);
if (!lock) {
const char *str = "Cannot lock the ref '%s'.";
switch (onerr) {
case DIE_ON_ERR: die(str, refname); break;
case QUIET_ON_ERR: break;
}
- return 1;
}
+ return lock;
+}
+
+static int update_ref_write(const char *action, const char *refname,
+ const unsigned char *sha1, struct ref_lock *lock,
+ enum action_on_err onerr)
+{
if (write_ref_sha1(lock, sha1, action) < 0) {
const char *str = "Cannot update the ref '%s'.";
switch (onerr) {
return 0;
}
-struct ref *find_ref_by_name(const struct ref *list, const char *name)
+int update_ref(const char *action, const char *refname,
+ const unsigned char *sha1, const unsigned char *oldval,
+ int flags, enum action_on_err onerr)
{
- for ( ; list; list = list->next)
- if (!strcmp(list->name, name))
- return (struct ref *)list;
- return NULL;
+ struct ref_lock *lock;
+ lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
+ if (!lock)
+ return 1;
+ return update_ref_write(action, refname, sha1, lock, onerr);
+}
+
+static int ref_update_compare(const void *r1, const void *r2)
+{
+ const struct ref_update * const *u1 = r1;
+ const struct ref_update * const *u2 = r2;
+ return strcmp((*u1)->ref_name, (*u2)->ref_name);
+}
+
+static int ref_update_reject_duplicates(struct ref_update **updates, int n,
+ enum action_on_err onerr)
+{
+ int i;
+ for (i = 1; i < n; i++)
+ if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) {
+ const char *str =
+ "Multiple updates for ref '%s' not allowed.";
+ switch (onerr) {
+ case MSG_ON_ERR:
+ error(str, updates[i]->ref_name); break;
+ case DIE_ON_ERR:
+ die(str, updates[i]->ref_name); break;
+ case QUIET_ON_ERR:
+ break;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int update_refs(const char *action, const struct ref_update **updates_orig,
+ int n, enum action_on_err onerr)
+{
+ int ret = 0, delnum = 0, i;
+ struct ref_update **updates;
+ int *types;
+ struct ref_lock **locks;
+ const char **delnames;
+
+ if (!updates_orig || !n)
+ return 0;
+
+ /* Allocate work space */
+ updates = xmalloc(sizeof(*updates) * n);
+ types = xmalloc(sizeof(*types) * n);
+ locks = xcalloc(n, sizeof(*locks));
+ delnames = xmalloc(sizeof(*delnames) * n);
+
+ /* Copy, sort, and reject duplicate refs */
+ memcpy(updates, updates_orig, sizeof(*updates) * n);
+ qsort(updates, n, sizeof(*updates), ref_update_compare);
+ ret = ref_update_reject_duplicates(updates, n, onerr);
+ if (ret)
+ goto cleanup;
+
+ /* Acquire all locks while verifying old values */
+ for (i = 0; i < n; i++) {
+ locks[i] = update_ref_lock(updates[i]->ref_name,
+ (updates[i]->have_old ?
+ updates[i]->old_sha1 : NULL),
+ updates[i]->flags,
+ &types[i], onerr);
+ if (!locks[i]) {
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ /* Perform updates first so live commits remain referenced */
+ for (i = 0; i < n; i++)
+ if (!is_null_sha1(updates[i]->new_sha1)) {
+ ret = update_ref_write(action,
+ updates[i]->ref_name,
+ updates[i]->new_sha1,
+ locks[i], onerr);
+ locks[i] = NULL; /* freed by update_ref_write */
+ if (ret)
+ goto cleanup;
+ }
+
+ /* Perform deletes now that updates are safely completed */
+ for (i = 0; i < n; i++)
+ if (locks[i]) {
+ delnames[delnum++] = locks[i]->ref_name;
+ ret |= delete_ref_loose(locks[i], types[i]);
+ }
+ ret |= repack_without_refs(delnames, delnum);
+ for (i = 0; i < delnum; i++)
+ unlink_or_warn(git_path("logs/%s", delnames[i]));
+ clear_loose_ref_cache(&ref_cache);
+
+cleanup:
+ for (i = 0; i < n; i++)
+ if (locks[i])
+ unlock_ref(locks[i]);
+ free(updates);
+ free(types);
+ free(locks);
+ free(delnames);
+ return ret;
}
/*
size_t total_len = 0;
/* the rule list is NULL terminated, count them first */
- for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
+ for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)
/* no +1 because strlen("%s") < strlen("%.*s") */
total_len += strlen(ref_rev_parse_rules[nr_rules]);
int force_write;
};
+/**
+ * Information needed for a single ref update. Set new_sha1 to the
+ * new value or to zero to delete the ref. To check the old value
+ * while locking the ref, set have_old to 1 and set old_sha1 to the
+ * value or to zero to ensure the ref does not exist before update.
+ */
+struct ref_update {
+ const char *ref_name;
+ unsigned char new_sha1[20];
+ unsigned char old_sha1[20];
+ int flags; /* REF_NODEREF? */
+ int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
+};
+
/*
* Bit values set in the flags argument passed to each_ref_fn():
*/
#define REF_NODEREF 0x01
extern struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
- int flags);
+ int flags, int *type_p);
/** Close the file descriptor owned by a lock and return the status */
extern int close_ref(struct ref_lock *lock);
/** Writes sha1 into the ref specified by the lock. **/
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
- /*
- * Invalidate the reference cache for the specified submodule. Use
- * submodule=NULL to invalidate the cache for the main module. This
- * function must be called if references are changed via a mechanism
- * other than the refs API.
- */
- extern void invalidate_ref_cache(const char *submodule);
-
/** Setup reflog before using. **/
int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
const unsigned char *sha1, const unsigned char *oldval,
int flags, enum action_on_err onerr);
+/**
+ * Lock all refs and then perform all modifications.
+ */
+int update_refs(const char *action, const struct ref_update **updates,
+ int n, enum action_on_err onerr);
+
extern int parse_hide_refs_config(const char *var, const char *value, const char *);
extern int ref_is_hidden(const char *);