summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c209
-rw-r--r--builtin/apply.c40
-rw-r--r--builtin/archive.c4
-rw-r--r--builtin/blame.c133
-rw-r--r--builtin/branch.c10
-rw-r--r--builtin/cat-file.c23
-rw-r--r--builtin/check-attr.c3
-rw-r--r--builtin/checkout.c35
-rw-r--r--builtin/clean.c57
-rw-r--r--builtin/clone.c30
-rw-r--r--builtin/column.c2
-rw-r--r--builtin/commit-tree.c12
-rw-r--r--builtin/commit.c107
-rw-r--r--builtin/config.c110
-rw-r--r--builtin/describe.c57
-rw-r--r--builtin/diff.c58
-rw-r--r--builtin/fast-export.c6
-rw-r--r--builtin/fetch-pack.c29
-rw-r--r--builtin/fetch.c369
-rw-r--r--builtin/fmt-merge-msg.c10
-rw-r--r--builtin/for-each-ref.c110
-rw-r--r--builtin/fsck.c14
-rw-r--r--builtin/gc.c63
-rw-r--r--builtin/get-tar-commit-id.c40
-rw-r--r--builtin/grep.c9
-rw-r--r--builtin/help.c14
-rw-r--r--builtin/index-pack.c50
-rw-r--r--builtin/init-db.c19
-rw-r--r--builtin/log.c18
-rw-r--r--builtin/ls-files.c9
-rw-r--r--builtin/ls-remote.c6
-rw-r--r--builtin/ls-tree.c2
-rw-r--r--builtin/mailinfo.c22
-rw-r--r--builtin/merge-base.c148
-rw-r--r--builtin/merge-recursive.c4
-rw-r--r--builtin/merge.c25
-rw-r--r--builtin/mktree.c5
-rw-r--r--builtin/mv.c15
-rw-r--r--builtin/name-rev.c11
-rw-r--r--builtin/notes.c10
-rw-r--r--builtin/pack-objects.c524
-rw-r--r--builtin/prune-packed.c40
-rw-r--r--builtin/prune.c50
-rw-r--r--builtin/push.c122
-rw-r--r--builtin/receive-pack.c311
-rw-r--r--builtin/reflog.c6
-rw-r--r--builtin/remote.c264
-rw-r--r--builtin/repack.c60
-rw-r--r--builtin/replace.c67
-rw-r--r--builtin/rerere.c2
-rw-r--r--builtin/reset.c47
-rw-r--r--builtin/rev-list.c39
-rw-r--r--builtin/rev-parse.c81
-rw-r--r--builtin/revert.c2
-rw-r--r--builtin/rm.c4
-rw-r--r--builtin/send-pack.c17
-rw-r--r--builtin/shortlog.c6
-rw-r--r--builtin/show-branch.c25
-rw-r--r--builtin/show-ref.c6
-rw-r--r--builtin/symbolic-ref.c2
-rw-r--r--builtin/tag.c83
-rw-r--r--builtin/tar-tree.c102
-rw-r--r--builtin/unpack-objects.c6
-rw-r--r--builtin/update-index.c79
-rw-r--r--builtin/update-ref.c11
-rw-r--r--builtin/upload-archive.c2
66 files changed, 2365 insertions, 1491 deletions
diff --git a/builtin/add.c b/builtin/add.c
index d7e3e44..459208a 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -15,6 +15,7 @@
#include "diffcore.h"
#include "revision.h"
#include "bulk-checkin.h"
+#include "argv-array.h"
static const char * const builtin_add_usage[] = {
N_("git add [options] [--] <pathspec>..."),
@@ -26,55 +27,8 @@ static int take_worktree_changes;
struct update_callback_data {
int flags;
int add_errors;
- const char *implicit_dot;
- size_t implicit_dot_len;
-
- /* only needed for 2.0 transition preparation */
- int warn_add_would_remove;
};
-static const char *option_with_implicit_dot;
-static const char *short_option_with_implicit_dot;
-
-static void warn_pathless_add(void)
-{
- static int shown;
- assert(option_with_implicit_dot && short_option_with_implicit_dot);
-
- if (shown)
- return;
- shown = 1;
-
- /*
- * To be consistent with "git add -p" and most Git
- * commands, we should default to being tree-wide, but
- * this is not the original behavior and can't be
- * changed until users trained themselves not to type
- * "git add -u" or "git add -A". For now, we warn and
- * keep the old behavior. Later, the behavior can be changed
- * to tree-wide, keeping the warning for a while, and
- * eventually we can drop the warning.
- */
- warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
- "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
- "To add content for the whole tree, run:\n"
- "\n"
- " git add %s :/\n"
- " (or git add %s :/)\n"
- "\n"
- "To restrict the command to the current directory, run:\n"
- "\n"
- " git add %s .\n"
- " (or git add %s .)\n"
- "\n"
- "With the current Git version, the command is restricted to "
- "the current directory.\n"
- ""),
- option_with_implicit_dot, short_option_with_implicit_dot,
- option_with_implicit_dot, short_option_with_implicit_dot,
- option_with_implicit_dot, short_option_with_implicit_dot);
-}
-
static int fix_unmerged_status(struct diff_filepair *p,
struct update_callback_data *data)
{
@@ -96,49 +50,15 @@ static int fix_unmerged_status(struct diff_filepair *p,
return DIFF_STATUS_MODIFIED;
}
-static const char *add_would_remove_warning = N_(
- "You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
-"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
-"Paths like '%s' that are\n"
-"removed from your working tree are ignored with this version of Git.\n"
-"\n"
-"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
-" ignores paths you removed from your working tree.\n"
-"\n"
-"* 'git add --all <pathspec>' will let you also record the removals.\n"
-"\n"
-"Run 'git status' to check the paths you removed from your working tree.\n");
-
-static void warn_add_would_remove(const char *path)
-{
- warning(_(add_would_remove_warning), path);
-}
-
static void update_callback(struct diff_queue_struct *q,
struct diff_options *opt, void *cbdata)
{
int i;
struct update_callback_data *data = cbdata;
- const char *implicit_dot = data->implicit_dot;
- size_t implicit_dot_len = data->implicit_dot_len;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
const char *path = p->one->path;
- /*
- * Check if "git add -A" or "git add -u" was run from a
- * subdirectory with a modified file outside that directory,
- * and warn if so.
- *
- * "git add -u" will behave like "git add -u :/" instead of
- * "git add -u ." in the future. This warning prepares for
- * that change.
- */
- if (implicit_dot &&
- strncmp_icase(path, implicit_dot, implicit_dot_len)) {
- warn_pathless_add();
- continue;
- }
switch (fix_unmerged_status(p, data)) {
default:
die(_("unexpected diff status %c"), p->status);
@@ -151,10 +71,6 @@ static void update_callback(struct diff_queue_struct *q,
}
break;
case DIFF_STATUS_DELETED:
- if (data->warn_add_would_remove) {
- warn_add_would_remove(path);
- data->warn_add_would_remove = 0;
- }
if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
break;
if (!(data->flags & ADD_CACHE_PRETEND))
@@ -166,37 +82,28 @@ static void update_callback(struct diff_queue_struct *q,
}
}
-static void update_files_in_cache(const char *prefix,
- const struct pathspec *pathspec,
- struct update_callback_data *data)
+int add_files_to_cache(const char *prefix,
+ const struct pathspec *pathspec, int flags)
{
+ struct update_callback_data data;
struct rev_info rev;
+ memset(&data, 0, sizeof(data));
+ data.flags = flags;
+
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
if (pathspec)
copy_pathspec(&rev.prune_data, pathspec);
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
- rev.diffopt.format_callback_data = data;
+ rev.diffopt.format_callback_data = &data;
rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-}
-
-int add_files_to_cache(const char *prefix,
- const struct pathspec *pathspec, int flags)
-{
- struct update_callback_data data;
-
- memset(&data, 0, sizeof(data));
- data.flags = flags;
- update_files_in_cache(prefix, pathspec, &data);
return !!data.add_errors;
}
-#define WARN_IMPLICIT_DOT (1u << 0)
-static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
- int prefix, unsigned flag)
+static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
{
char *seen;
int i;
@@ -208,19 +115,8 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
i = dir->nr;
while (--i >= 0) {
struct dir_entry *entry = *src++;
- if (match_pathspec_depth(pathspec, entry->name, entry->len,
- prefix, seen))
+ if (dir_path_match(entry, pathspec, prefix, seen))
*dst++ = entry;
- else if (flag & WARN_IMPLICIT_DOT)
- /*
- * "git add -A" was run from a subdirectory with a
- * new file outside that directory.
- *
- * "git add -A" will behave like "git add -A :/"
- * instead of "git add -A ." in the future.
- * Warn about the coming behavior change.
- */
- warn_pathless_add();
}
dir->nr = dst - dir->entries;
add_pathspec_matches_against_index(pathspec, seen);
@@ -246,23 +142,21 @@ static void refresh(int verbose, const struct pathspec *pathspec)
int run_add_interactive(const char *revision, const char *patch_mode,
const struct pathspec *pathspec)
{
- int status, ac, i;
- const char **args;
+ int status, i;
+ struct argv_array argv = ARGV_ARRAY_INIT;
- args = xcalloc(sizeof(const char *), (pathspec->nr + 6));
- ac = 0;
- args[ac++] = "add--interactive";
+ argv_array_push(&argv, "add--interactive");
if (patch_mode)
- args[ac++] = patch_mode;
+ argv_array_push(&argv, patch_mode);
if (revision)
- args[ac++] = revision;
- args[ac++] = "--";
+ argv_array_push(&argv, revision);
+ argv_array_push(&argv, "--");
for (i = 0; i < pathspec->nr; i++)
/* pass original pathspec, to be re-parsed */
- args[ac++] = pathspec->items[i].original;
+ argv_array_push(&argv, pathspec->items[i].original);
- status = run_command_v_opt(args, RUN_GIT_CMD);
- free(args);
+ status = run_command_v_opt(argv.argv, RUN_GIT_CMD);
+ argv_array_clear(&argv);
return status;
}
@@ -339,7 +233,7 @@ N_("The following paths are ignored by one of your .gitignore files:\n");
static int verbose, show_only, ignored_too, refresh_only;
static int ignore_add_errors, intent_to_add, ignore_missing;
-#define ADDREMOVE_DEFAULT 0 /* Change to 1 in Git 2.0 */
+#define ADDREMOVE_DEFAULT 1
static int addremove = ADDREMOVE_DEFAULT;
static int addremove_explicit = -1; /* unspecified */
@@ -412,8 +306,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int add_new_files;
int require_pathspec;
char *seen = NULL;
- int implicit_dot = 0;
- struct update_callback_data update_data;
git_config(add_config, NULL);
@@ -437,36 +329,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (addremove && take_worktree_changes)
die(_("-A and -u are mutually incompatible"));
- /*
- * Warn when "git add pathspec..." was given without "-u" or "-A"
- * and pathspec... covers a removed path.
- */
- memset(&update_data, 0, sizeof(update_data));
- if (!take_worktree_changes && addremove_explicit < 0)
- update_data.warn_add_would_remove = 1;
-
if (!take_worktree_changes && addremove_explicit < 0 && argc)
- /*
- * Turn "git add pathspec..." to "git add -A pathspec..."
- * in Git 2.0 but not yet
- */
- ; /* addremove = 1; */
+ /* Turn "git add pathspec..." to "git add -A pathspec..." */
+ addremove = 1;
if (!show_only && ignore_missing)
die(_("Option --ignore-missing can only be used together with --dry-run"));
- if (addremove) {
- option_with_implicit_dot = "--all";
- short_option_with_implicit_dot = "-A";
- }
- if (take_worktree_changes) {
- option_with_implicit_dot = "--update";
- short_option_with_implicit_dot = "-u";
- }
- if (option_with_implicit_dot && !argc) {
- static const char *here[2] = { ".", NULL };
+
+ if ((0 < addremove_explicit || take_worktree_changes) && !argc) {
+ static const char *whole[2] = { ":/", NULL };
argc = 1;
- argv = here;
- implicit_dot = 1;
+ argv = whole;
}
add_new_files = !take_worktree_changes && !refresh_only;
@@ -479,8 +352,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
(intent_to_add ? ADD_CACHE_INTENT : 0) |
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
(!(addremove || take_worktree_changes)
- ? ADD_CACHE_IGNORE_REMOVAL : 0)) |
- (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0);
+ ? ADD_CACHE_IGNORE_REMOVAL : 0));
if (require_pathspec && argc == 0) {
fprintf(stderr, _("Nothing specified, nothing added.\n"));
@@ -514,18 +386,15 @@ int cmd_add(int argc, const char **argv, const char *prefix)
memset(&empty_pathspec, 0, sizeof(empty_pathspec));
/* This picks up the paths that are not tracked */
- baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec);
+ baselen = fill_directory(&dir, &pathspec);
if (pathspec.nr)
- seen = prune_directory(&dir, &pathspec, baselen,
- implicit_dot ? WARN_IMPLICIT_DOT : 0);
+ seen = prune_directory(&dir, &pathspec, baselen);
}
if (refresh_only) {
refresh(verbose, &pathspec);
goto finish;
}
- if (implicit_dot && prefix)
- refresh_cache(REFRESH_QUIET);
if (pathspec.nr) {
int i;
@@ -540,10 +409,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
PATHSPEC_FROMTOP |
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
- PATHSPEC_ICASE);
+ PATHSPEC_ICASE |
+ PATHSPEC_EXCLUDE);
for (i = 0; i < pathspec.nr; i++) {
const char *path = pathspec.items[i].match;
+ if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
+ continue;
if (!seen[i] && path[0] &&
((pathspec.items[i].magic &
(PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
@@ -562,21 +434,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
plug_bulk_checkin();
- if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
- /*
- * Check for modified files throughout the worktree so
- * update_callback has a chance to warn about changes
- * outside the cwd.
- */
- update_data.implicit_dot = prefix;
- update_data.implicit_dot_len = strlen(prefix);
- free_pathspec(&pathspec);
- memset(&pathspec, 0, sizeof(pathspec));
- }
- update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
- update_files_in_cache(prefix, &pathspec, &update_data);
+ exit_status |= add_files_to_cache(prefix, &pathspec, flags);
- exit_status |= !!update_data.add_errors;
if (add_new_files)
exit_status |= add_files(&dir, flags);
diff --git a/builtin/apply.c b/builtin/apply.c
index ef32e4f..9c5724e 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -300,11 +300,13 @@ static int fuzzy_matchlines(const char *s1, size_t n1,
while ((*last2 == '\r') || (*last2 == '\n'))
last2--;
- /* skip leading whitespace */
- while (isspace(*s1) && (s1 <= last1))
- s1++;
- while (isspace(*s2) && (s2 <= last2))
- s2++;
+ /* skip leading whitespaces, if both begin with whitespace */
+ if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) {
+ while (isspace(*s1) && (s1 <= last1))
+ s1++;
+ while (isspace(*s2) && (s2 <= last2))
+ s2++;
+ }
/* early return if both lines are empty */
if ((s1 > last1) && (s2 > last2))
return 1;
@@ -1409,10 +1411,10 @@ static void recount_diff(const char *line, int size, struct fragment *fragment)
case '\\':
continue;
case '@':
- ret = size < 3 || prefixcmp(line, "@@ ");
+ ret = size < 3 || !starts_with(line, "@@ ");
break;
case 'd':
- ret = size < 5 || prefixcmp(line, "diff ");
+ ret = size < 5 || !starts_with(line, "diff ");
break;
default:
ret = -1;
@@ -1798,11 +1800,11 @@ static struct fragment *parse_binary_hunk(char **buf_p,
*status_p = 0;
- if (!prefixcmp(buffer, "delta ")) {
+ if (starts_with(buffer, "delta ")) {
patch_method = BINARY_DELTA_DEFLATED;
origlen = strtoul(buffer + 6, NULL, 10);
}
- else if (!prefixcmp(buffer, "literal ")) {
+ else if (starts_with(buffer, "literal ")) {
patch_method = BINARY_LITERAL_DEFLATED;
origlen = strtoul(buffer + 8, NULL, 10);
}
@@ -1943,13 +1945,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
size - offset - hdrsize, patch);
if (!patchsize) {
- static const char *binhdr[] = {
- "Binary files ",
- "Files ",
- NULL,
- };
static const char git_binary[] = "GIT binary patch\n";
- int i;
int hd = hdrsize + offset;
unsigned long llen = linelen(buffer + hd, size - hd);
@@ -1965,6 +1961,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
patchsize = 0;
}
else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
+ static const char *binhdr[] = {
+ "Binary files ",
+ "Files ",
+ NULL,
+ };
+ int i;
for (i = 0; binhdr[i]; i++) {
int len = strlen(binhdr[i]);
if (len < size - hd &&
@@ -3627,12 +3629,12 @@ static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20
hunk->oldpos == 1 && hunk->oldlines == 1 &&
/* does preimage begin with the heading? */
(preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
- !prefixcmp(++preimage, heading) &&
+ starts_with(++preimage, heading) &&
/* does it record full SHA-1? */
!get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
preimage[sizeof(heading) + 40 - 1] == '\n' &&
/* does the abbreviated name on the index line agree with it? */
- !prefixcmp(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
+ starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
return 0; /* it all looks fine */
/* we may have full object name on the index line */
@@ -4061,7 +4063,7 @@ static int write_out_one_reject(struct patch *patch)
return error(_("cannot open %s: %s"), namebuf, strerror(errno));
/* Normal git tools never deal with .rej, so do not pretend
- * this is a git patch by saying --git nor give extended
+ * this is a git patch by saying --git or giving extended
* headers. While at it, maybe please "kompare" that wants
* the trailing TAB and some garbage at the end of line ;-).
*/
@@ -4152,7 +4154,7 @@ static int use_patch(struct patch *p)
/* See if it matches any of exclude/include rule */
for (i = 0; i < limit_by_name.nr; i++) {
struct string_list_item *it = &limit_by_name.items[i];
- if (!fnmatch(it->string, pathname, 0))
+ if (!wildmatch(it->string, pathname, 0, NULL))
return (it->util != NULL);
}
diff --git a/builtin/archive.c b/builtin/archive.c
index 49178f1..a1e3b94 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -57,9 +57,9 @@ static int run_remote_archiver(int argc, const char **argv,
if (!buf)
die(_("git archive: expected ACK/NAK, got EOF"));
if (strcmp(buf, "ACK")) {
- if (!prefixcmp(buf, "NACK "))
+ if (starts_with(buf, "NACK "))
die(_("git archive: NACK %s"), buf + 5);
- if (!prefixcmp(buf, "ERR "))
+ if (starts_with(buf, "ERR "))
die(_("remote error: %s"), buf + 4);
die(_("git archive: protocol error"));
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 9047b6e..d8b2760 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -74,7 +74,7 @@ static unsigned blame_copy_score;
#define BLAME_DEFAULT_MOVE_SCORE 20
#define BLAME_DEFAULT_COPY_SCORE 40
-/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */
+/* Remember to update object flag allocation in object.h */
#define METAINFO_SHOWN (1u<<12)
#define MORE_THAN_ONE_PATH (1u<<13)
@@ -197,7 +197,6 @@ static void drop_origin_blob(struct origin *o)
* scoreboard structure, sorted by the target line number.
*/
struct blame_entry {
- struct blame_entry *prev;
struct blame_entry *next;
/* the first line of this group in the final image;
@@ -256,15 +255,6 @@ struct scoreboard {
int *lineno;
};
-static inline int same_suspect(struct origin *a, struct origin *b)
-{
- if (a == b)
- return 1;
- if (a->commit != b->commit)
- return 0;
- return !strcmp(a->path, b->path);
-}
-
static void sanity_check_refcnt(struct scoreboard *);
/*
@@ -277,13 +267,11 @@ static void coalesce(struct scoreboard *sb)
struct blame_entry *ent, *next;
for (ent = sb->ent; ent && (next = ent->next); ent = next) {
- if (same_suspect(ent->suspect, next->suspect) &&
+ if (ent->suspect == next->suspect &&
ent->guilty == next->guilty &&
ent->s_lno + ent->num_lines == next->s_lno) {
ent->num_lines += next->num_lines;
ent->next = next->next;
- if (ent->next)
- ent->next->prev = ent;
origin_decref(next->suspect);
free(next);
ent->score = 0;
@@ -534,7 +522,7 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
prev = ent;
/* prev, if not NULL, is the last one that is below e */
- e->prev = prev;
+
if (prev) {
e->next = prev->next;
prev->next = e;
@@ -543,8 +531,6 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
e->next = sb->ent;
sb->ent = e;
}
- if (e->next)
- e->next->prev = e;
}
/*
@@ -555,14 +541,12 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
*/
static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
{
- struct blame_entry *p, *n;
+ struct blame_entry *n;
- p = dst->prev;
n = dst->next;
origin_incref(src->suspect);
origin_decref(dst->suspect);
memcpy(dst, src, sizeof(*src));
- dst->prev = p;
dst->next = n;
dst->score = 0;
}
@@ -742,7 +726,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target)
int last_in_target = -1;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target))
+ if (e->guilty || e->suspect != target)
continue;
if (last_in_target < e->s_lno + e->num_lines)
last_in_target = e->s_lno + e->num_lines;
@@ -762,7 +746,7 @@ static void blame_chunk(struct scoreboard *sb,
struct blame_entry *e;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target))
+ if (e->guilty || e->suspect != target)
continue;
if (same <= e->s_lno)
continue;
@@ -939,7 +923,6 @@ static void find_copy_in_blob(struct scoreboard *sb,
mmfile_t *file_p)
{
const char *cp;
- int cnt;
mmfile_t file_o;
struct handle_split_cb_data d;
@@ -950,13 +933,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
*/
cp = nth_line(sb, ent->lno);
file_o.ptr = (char *) cp;
- cnt = ent->num_lines;
-
- while (cnt && cp < sb->final_buf + sb->final_buf_size) {
- if (*cp++ == '\n')
- cnt--;
- }
- file_o.size = cp - file_o.ptr;
+ file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
/*
* file_o is a part of final image we are annotating.
@@ -992,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb,
while (made_progress) {
made_progress = 0;
for (e = sb->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target) ||
+ if (e->guilty || e->suspect != target ||
ent_score(sb, e) < blame_move_score)
continue;
find_copy_in_blob(sb, e, parent, split, &file_p);
@@ -1027,14 +1004,14 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,
for (e = sb->ent, num_ents = 0; e; e = e->next)
if (!e->scanned && !e->guilty &&
- same_suspect(e->suspect, target) &&
+ e->suspect == target &&
min_score < ent_score(sb, e))
num_ents++;
if (num_ents) {
blame_list = xcalloc(num_ents, sizeof(struct blame_list));
for (e = sb->ent, i = 0; e; e = e->next)
if (!e->scanned && !e->guilty &&
- same_suspect(e->suspect, target) &&
+ e->suspect == target &&
min_score < ent_score(sb, e))
blame_list[i++].ent = e;
}
@@ -1178,7 +1155,7 @@ static void pass_whole_blame(struct scoreboard *sb,
origin->file.ptr = NULL;
}
for (e = sb->ent; e; e = e->next) {
- if (!same_suspect(e->suspect, origin))
+ if (e->suspect != origin)
continue;
origin_incref(porigin);
origin_decref(e->suspect);
@@ -1551,8 +1528,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
*/
origin_incref(suspect);
commit = suspect->commit;
- if (!commit->object.parsed)
- parse_commit(commit);
+ parse_commit(commit);
if (reverse ||
(!(commit->object.flags & UNINTERESTING) &&
!(revs->max_age != -1 && commit->date < revs->max_age)))
@@ -1568,7 +1544,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
/* Take responsibility for the remaining entries */
for (ent = sb->ent; ent; ent = ent->next)
- if (same_suspect(ent->suspect, suspect))
+ if (ent->suspect == suspect)
found_guilty_entry(ent);
origin_decref(suspect);
@@ -1580,22 +1556,29 @@ static void assign_blame(struct scoreboard *sb, int opt)
static const char *format_time(unsigned long time, const char *tz_str,
int show_raw_time)
{
- static char time_buf[128];
- const char *time_str;
- int time_len;
- int tz;
+ static struct strbuf time_buf = STRBUF_INIT;
+ strbuf_reset(&time_buf);
if (show_raw_time) {
- snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
+ strbuf_addf(&time_buf, "%lu %s", time, tz_str);
}
else {
+ const char *time_str;
+ size_t time_width;
+ int tz;
tz = atoi(tz_str);
time_str = show_date(time, tz, blame_date_mode);
- time_len = strlen(time_str);
- memcpy(time_buf, time_str, time_len);
- memset(time_buf + time_len, ' ', blame_date_width - time_len);
+ strbuf_addstr(&time_buf, time_str);
+ /*
+ * Add space paddings to time_buf to display a fixed width
+ * string, and use time_width for display width calibration.
+ */
+ for (time_width = utf8_strwidth(time_str);
+ time_width < blame_date_width;
+ time_width++)
+ strbuf_addch(&time_buf, ' ');
}
- return time_buf;
+ return time_buf.buf;
}
#define OUTPUT_ANNOTATE_COMPAT 001
@@ -1773,25 +1756,41 @@ static int prepare_lines(struct scoreboard *sb)
{
const char *buf = sb->final_buf;
unsigned long len = sb->final_buf_size;
- int num = 0, incomplete = 0, bol = 1;
+ const char *end = buf + len;
+ const char *p;
+ int *lineno;
+ int num = 0, incomplete = 0;
- if (len && buf[len-1] != '\n')
- incomplete++; /* incomplete line at the end */
- while (len--) {
- if (bol) {
- sb->lineno = xrealloc(sb->lineno,
- sizeof(int *) * (num + 1));
- sb->lineno[num] = buf - sb->final_buf;
- bol = 0;
- }
- if (*buf++ == '\n') {
+ for (p = buf;;) {
+ p = memchr(p, '\n', end - p);
+ if (p) {
+ p++;
num++;
- bol = 1;
+ continue;
}
+ break;
}
- sb->lineno = xrealloc(sb->lineno,
- sizeof(int *) * (num + incomplete + 1));
- sb->lineno[num + incomplete] = buf - sb->final_buf;
+
+ if (len && end[-1] != '\n')
+ incomplete++; /* incomplete line at the end */
+
+ sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
+ lineno = sb->lineno;
+
+ *lineno++ = 0;
+ for (p = buf;;) {
+ p = memchr(p, '\n', end - p);
+ if (p) {
+ p++;
+ *lineno++ = p - buf;
+ continue;
+ }
+ break;
+ }
+
+ if (incomplete)
+ *lineno++ = len;
+
sb->num_lines = num + incomplete;
return sb->num_lines;
}
@@ -2096,7 +2095,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
if (strbuf_read(&buf, 0, 0) < 0)
die_errno("failed to read from stdin");
}
- convert_to_git(path, buf.buf, buf.len, &buf, 0);
origin->file.ptr = buf.buf;
origin->file.size = buf.len;
pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
@@ -2339,7 +2337,14 @@ parse_done:
blame_date_width = sizeof("2006-10-19");
break;
case DATE_RELATIVE:
- /* "normal" is used as the fallback for "relative" */
+ /* TRANSLATORS: This string is used to tell us the maximum
+ display width for a relative timestamp in "git blame"
+ output. For C locale, "4 years, 11 months ago", which
+ takes 22 places, is the longest among various forms of
+ relative timestamps, but your language may need more or
+ fewer display columns. */
+ blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
+ break;
case DATE_LOCAL:
case DATE_NORMAL:
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
@@ -2503,8 +2508,6 @@ parse_done:
ent->suspect = o;
ent->s_lno = bottom;
ent->next = next;
- if (next)
- next->prev = ent;
origin_incref(o);
}
origin_decref(o);
diff --git a/builtin/branch.c b/builtin/branch.c
index f157f92..652b1d2 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -81,13 +81,13 @@ static int parse_branch_color_slot(const char *var, int ofs)
static int git_branch_config(const char *var, const char *value, void *cb)
{
- if (!prefixcmp(var, "column."))
+ if (starts_with(var, "column."))
return git_column_config(var, value, "branch", &colopts);
if (!strcmp(var, "color.branch")) {
branch_use_color = git_config_colorbool(var, value);
return 0;
}
- if (!prefixcmp(var, "color.branch.")) {
+ if (starts_with(var, "color.branch.")) {
int slot = parse_branch_color_slot(var, 13);
if (slot < 0)
return 0;
@@ -315,7 +315,7 @@ static int match_patterns(const char **pattern, const char *refname)
if (!*pattern)
return 1; /* no pattern always matches */
while (*pattern) {
- if (!fnmatch(*pattern, refname, 0))
+ if (!wildmatch(*pattern, refname, 0, NULL))
return 1;
pattern++;
}
@@ -502,7 +502,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item,
const char *sub = _(" **** invalid ref ****");
struct commit *commit = item->commit;
- if (commit && !parse_commit(commit)) {
+ if (!parse_commit(commit)) {
pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
sub = subject.buf;
}
@@ -868,7 +868,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!strcmp(head, "HEAD")) {
detached = 1;
} else {
- if (prefixcmp(head, "refs/heads/"))
+ if (!starts_with(head, "refs/heads/"))
die(_("HEAD not found below refs/heads!"));
head += 11;
}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index f8288c8..7073304 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -118,6 +118,7 @@ struct expand_data {
unsigned long size;
unsigned long disk_size;
const char *rest;
+ unsigned char delta_base_sha1[20];
/*
* If mark_query is true, we do not expand anything, but rather
@@ -174,6 +175,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
data->split_on_whitespace = 1;
else if (data->rest)
strbuf_addstr(sb, data->rest);
+ } else if (is_atom("deltabase", atom, len)) {
+ if (data->mark_query)
+ data->info.delta_base_sha1 = data->delta_base_sha1;
+ else
+ strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1));
} else
die("unknown format element: %.*s", len, atom);
}
@@ -241,7 +247,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
return 0;
}
- if (sha1_object_info_extended(data->sha1, &data->info) < 0) {
+ if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
printf("%s missing\n", obj_name);
fflush(stdout);
return 0;
@@ -263,6 +269,8 @@ static int batch_objects(struct batch_options *opt)
{
struct strbuf buf = STRBUF_INIT;
struct expand_data data;
+ int save_warning;
+ int retval = 0;
if (!opt->format)
opt->format = "%(objectname) %(objecttype) %(objectsize)";
@@ -291,11 +299,10 @@ static int batch_objects(struct batch_options *opt)
* warn) ends up dwarfing the actual cost of the object lookups
* themselves. We can work around it by just turning off the warning.
*/
+ save_warning = warn_on_object_refname_ambiguity;
warn_on_object_refname_ambiguity = 0;
while (strbuf_getline(&buf, stdin, '\n') != EOF) {
- int error;
-
if (data.split_on_whitespace) {
/*
* Split at first whitespace, tying off the beginning
@@ -310,12 +317,14 @@ static int batch_objects(struct batch_options *opt)
data.rest = p;
}
- error = batch_one_object(buf.buf, opt, &data);
- if (error)
- return error;
+ retval = batch_one_object(buf.buf, opt, &data);
+ if (retval)
+ break;
}
- return 0;
+ strbuf_release(&buf);
+ warn_on_object_refname_ambiguity = save_warning;
+ return retval;
}
static const char * const cat_file_usage[] = {
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index e9af7b2..5600ec3 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -102,6 +102,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
struct git_attr_check *check;
int cnt, i, doubledash, filei;
+ if (!is_bare_repository())
+ setup_work_tree();
+
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, check_attr_options,
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 54f80bd..07cf555 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -53,10 +53,10 @@ struct checkout_opts {
static int post_checkout_hook(struct commit *old, struct commit *new,
int changed)
{
- return run_hook(NULL, "post-checkout",
- sha1_to_hex(old ? old->object.sha1 : null_sha1),
- sha1_to_hex(new ? new->object.sha1 : null_sha1),
- changed ? "1" : "0", NULL);
+ return run_hook_le(NULL, "post-checkout",
+ sha1_to_hex(old ? old->object.sha1 : null_sha1),
+ sha1_to_hex(new ? new->object.sha1 : null_sha1),
+ changed ? "1" : "0", NULL);
/* "new" can be NULL when checking out from the index before
a commit exists. */
@@ -297,8 +297,7 @@ static int checkout_paths(const struct checkout_opts *opts,
* match_pathspec() for _all_ entries when
* opts->source_tree != NULL.
*/
- if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce),
- 0, ps_matched))
+ if (ce_path_match(ce, &opts->pathspec, ps_matched))
ce->ce_flags |= CE_MATCHED;
}
@@ -380,8 +379,8 @@ static void show_local_changes(struct object *head,
static void describe_detached_head(const char *msg, struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
- parse_commit(commit);
- pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
+ if (!parse_commit(commit))
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
fprintf(stderr, "%s %s... %s\n", msg,
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
strbuf_release(&sb);
@@ -677,12 +676,12 @@ static int add_pending_uninteresting_ref(const char *refname,
static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
{
- parse_commit(commit);
strbuf_addstr(sb, " ");
strbuf_addstr(sb,
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
strbuf_addch(sb, ' ');
- pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
+ if (!parse_commit(commit))
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
strbuf_addch(sb, '\n');
}
@@ -781,7 +780,7 @@ static int switch_branches(const struct checkout_opts *opts,
if (!(flag & REF_ISSYMREF))
old.path = NULL;
- if (old.path && !prefixcmp(old.path, "refs/heads/"))
+ if (old.path && starts_with(old.path, "refs/heads/"))
old.name = old.path + strlen("refs/heads/");
if (!new->name) {
@@ -789,7 +788,7 @@ static int switch_branches(const struct checkout_opts *opts,
new->commit = old.commit;
if (!new->commit)
die(_("You are on a branch yet to be born"));
- parse_commit(new->commit);
+ parse_commit_or_die(new->commit);
}
ret = merge_working_tree(opts, &old, new, &writeout_error);
@@ -816,7 +815,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
return 0;
}
- if (!prefixcmp(var, "submodule."))
+ if (starts_with(var, "submodule."))
return parse_submodule_config_option(var, value);
return git_xmerge_config(var, value, NULL);
@@ -896,7 +895,7 @@ static int parse_branchname_arg(int argc, const char **argv,
* between A and B, A...B names that merge base.
*
* (b) If <something> is _not_ a commit, either "--" is present
- * or <something> is not a path, no -t nor -b was given, and
+ * or <something> is not a path, no -t or -b was given, and
* and there is a tracking branch whose name is <something>
* in one and only one remote, then this is a short-hand to
* fork local <something> from that remote-tracking branch.
@@ -995,7 +994,7 @@ static int parse_branchname_arg(int argc, const char **argv,
/* not a commit */
*source_tree = parse_tree_indirect(rev);
} else {
- parse_commit(new->commit);
+ parse_commit_or_die(new->commit);
*source_tree = new->commit->tree;
}
@@ -1096,7 +1095,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"),
BRANCH_TRACK_EXPLICIT),
- OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new branch"), N_("new unparented branch")),
+ OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"),
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
@@ -1151,9 +1150,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
const char *argv0 = argv[0];
if (!argc || !strcmp(argv0, "--"))
die (_("--track needs a branch name"));
- if (!prefixcmp(argv0, "refs/"))
+ if (starts_with(argv0, "refs/"))
argv0 += 5;
- if (!prefixcmp(argv0, "remotes/"))
+ if (starts_with(argv0, "remotes/"))
argv0 += 8;
argv0 = strchr(argv0, '/');
if (!argv0 || !argv0[1])
diff --git a/builtin/clean.c b/builtin/clean.c
index ceab182..27701d2 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -100,7 +100,7 @@ static int parse_clean_color_slot(const char *var)
static int git_clean_config(const char *var, const char *value, void *cb)
{
- if (!prefixcmp(var, "column."))
+ if (starts_with(var, "column."))
return git_column_config(var, value, "clean", &colopts);
/* honors the color.interactive* config variables which also
@@ -109,7 +109,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
clean_use_color = git_config_colorbool(var, value);
return 0;
}
- if (!prefixcmp(var, "color.interactive.")) {
+ if (starts_with(var, "color.interactive.")) {
int slot = parse_clean_color_slot(var +
strlen("color.interactive."));
if (slot < 0)
@@ -154,7 +154,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
DIR *dir;
struct strbuf quoted = STRBUF_INIT;
struct dirent *e;
- int res = 0, ret = 0, gone = 1, original_len = path->len, len, i;
+ int res = 0, ret = 0, gone = 1, original_len = path->len, len;
unsigned char submodule_head[20];
struct string_list dels = STRING_LIST_INIT_DUP;
@@ -242,6 +242,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
}
if (!*dir_gone && !quiet) {
+ int i;
for (i = 0; i < dels.nr; i++)
printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
}
@@ -903,11 +904,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (!interactive && !dry_run && !force) {
if (config_set)
- die(_("clean.requireForce set to true and neither -i, -n nor -f given; "
+ die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
"refusing to clean"));
else
- die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; "
- "refusing to clean"));
+ die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;"
+ " refusing to clean"));
}
if (force > 1)
@@ -933,48 +934,28 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
- int len, pos;
int matches = 0;
- const struct cache_entry *ce;
struct stat st;
const char *rel;
- /*
- * Remove the '/' at the end that directory
- * walking adds for directory entries.
- */
- len = ent->len;
- if (len && ent->name[len-1] == '/')
- len--;
- pos = cache_name_pos(ent->name, len);
- if (0 <= pos)
- continue; /* exact match */
- pos = -pos - 1;
- if (pos < active_nr) {
- ce = active_cache[pos];
- if (ce_namelen(ce) == len &&
- !memcmp(ce->name, ent->name, len))
- continue; /* Yup, this one exists unmerged */
- }
+ if (!cache_name_is_other(ent->name, ent->len))
+ continue;
if (lstat(ent->name, &st))
die_errno("Cannot lstat '%s'", ent->name);
if (pathspec.nr)
- matches = match_pathspec_depth(&pathspec, ent->name,
- len, 0, NULL);
+ matches = dir_path_match(ent, &pathspec, 0, NULL);
- if (S_ISDIR(st.st_mode)) {
- if (remove_directories || (matches == MATCHED_EXACTLY)) {
- rel = relative_path(ent->name, prefix, &buf);
- string_list_append(&del_list, rel);
- }
- } else {
- if (pathspec.nr && !matches)
- continue;
- rel = relative_path(ent->name, prefix, &buf);
- string_list_append(&del_list, rel);
- }
+ if (pathspec.nr && !matches)
+ continue;
+
+ if (S_ISDIR(st.st_mode) && !remove_directories &&
+ matches != MATCHED_EXACTLY)
+ continue;
+
+ rel = relative_path(ent->name, prefix, &buf);
+ string_list_append(&del_list, rel);
}
if (interactive && del_list.nr > 0)
diff --git a/builtin/clone.c b/builtin/clone.c
index cc11104..9b3c04d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -252,6 +252,12 @@ static int add_one_reference(struct string_list_item *item, void *cb_data)
die(_("reference repository '%s' is not a local repository."),
item->string);
+ if (!access(mkpath("%s/shallow", ref_git), F_OK))
+ die(_("reference repository '%s' is shallow"), item->string);
+
+ if (!access(mkpath("%s/info/grafts", ref_git), F_OK))
+ die(_("reference repository '%s' is grafted"), item->string);
+
strbuf_addf(&alternate, "%s/objects", ref_git);
add_to_alternates_file(alternate.buf);
strbuf_release(&alternate);
@@ -508,9 +514,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
{
const struct ref *ref;
for (ref = refs; ref; ref = ref->next) {
- if (prefixcmp(ref->name, "refs/tags/"))
+ if (!starts_with(ref->name, "refs/tags/"))
continue;
- if (!suffixcmp(ref->name, "^{}"))
+ if (ends_with(ref->name, "^{}"))
continue;
if (!has_sha1_file(ref->old_sha1))
continue;
@@ -578,7 +584,7 @@ static void update_remote_refs(const struct ref *refs,
static void update_head(const struct ref *our, const struct ref *remote,
const char *msg)
{
- if (our && !prefixcmp(our->name, "refs/heads/")) {
+ if (our && starts_with(our->name, "refs/heads/")) {
/* Local default branch link */
create_symref("HEAD", our->name, NULL);
if (!option_bare) {
@@ -625,7 +631,7 @@ static int checkout(void)
if (advice_detached_head)
detach_advice(sha1_to_hex(sha1));
} else {
- if (prefixcmp(head, "refs/heads/"))
+ if (!starts_with(head, "refs/heads/"))
die(_("HEAD not found below refs/heads!"));
}
free(head);
@@ -654,8 +660,8 @@ static int checkout(void)
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
- sha1_to_hex(sha1), "1", NULL);
+ err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
+ sha1_to_hex(sha1), "1", NULL);
if (!err && option_recursive)
err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
@@ -791,8 +797,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
else
repo = repo_name;
is_local = option_local != 0 && path && !is_bundle;
- if (is_local && option_depth)
- warning(_("--depth is ignored in local clones; use file:// instead."));
+ if (is_local) {
+ if (option_depth)
+ warning(_("--depth is ignored in local clones; use file:// instead."));
+ if (!access(mkpath("%s/shallow", path), F_OK)) {
+ if (option_local > 0)
+ warning(_("source repository is shallow, ignoring --local"));
+ is_local = 0;
+ }
+ }
if (option_local > 0 && !is_local)
warning(_("--local is ignored"));
@@ -887,6 +900,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
+ transport->cloning = 1;
if (!transport->get_refs_list || (!is_local && !transport->fetch))
die(_("Don't know how to clone %s"), transport->url);
diff --git a/builtin/column.c b/builtin/column.c
index e125a55..7581852 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -34,7 +34,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
};
/* This one is special and must be the first one */
- if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
+ if (argc > 1 && starts_with(argv[1], "--command=")) {
command = argv[1] + 10;
git_config(column_config, (void *)command);
} else
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index f641ff2..987a4c3 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -12,6 +12,8 @@
static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1> <changelog";
+static const char *sign_commit;
+
static void new_parent(struct commit *parent, struct commit_list **parents_p)
{
unsigned char *sha1 = parent->object.sha1;
@@ -31,6 +33,10 @@ static int commit_tree_config(const char *var, const char *value, void *cb)
int status = git_gpg_config(var, value, NULL);
if (status)
return status;
+ if (!strcmp(var, "commit.gpgsign")) {
+ sign_commit = git_config_bool(var, value) ? "" : NULL;
+ return 0;
+ }
return git_default_config(var, value, cb);
}
@@ -41,7 +47,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
unsigned char tree_sha1[20];
unsigned char commit_sha1[20];
struct strbuf buffer = STRBUF_INIT;
- const char *sign_commit = NULL;
git_config(commit_tree_config, NULL);
@@ -66,6 +71,11 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
continue;
}
+ if (!strcmp(arg, "--no-gpg-sign")) {
+ sign_commit = NULL;
+ continue;
+ }
+
if (!strcmp(arg, "-m")) {
if (argc <= ++i)
usage(commit_tree_usage);
diff --git a/builtin/commit.c b/builtin/commit.c
index fedb45a..12afc42 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,6 +113,7 @@ static char *sign_commit;
static enum {
CLEANUP_SPACE,
CLEANUP_NONE,
+ CLEANUP_SCISSORS,
CLEANUP_ALL
} cleanup_mode;
static const char *cleanup_arg;
@@ -234,7 +235,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
if (ce->ce_flags & CE_UPDATE)
continue;
- if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
+ if (!ce_path_match(ce, pattern, m))
continue;
item = string_list_insert(list, ce->name);
if (ce_skip_worktree(ce))
@@ -307,7 +308,6 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
int fd;
struct string_list partial;
struct pathspec pathspec;
- char *old_index_env = NULL;
int refresh_flags = REFRESH_QUIET;
if (is_status)
@@ -320,6 +320,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
die(_("index file corrupt"));
if (interactive) {
+ char *old_index_env = NULL;
fd = hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
@@ -600,19 +601,17 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
{
struct stat statbuf;
struct strbuf committer_ident = STRBUF_INIT;
- int commitable, saved_color_setting;
+ int commitable;
struct strbuf sb = STRBUF_INIT;
- char *buffer;
const char *hook_arg1 = NULL;
const char *hook_arg2 = NULL;
- int ident_shown = 0;
int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
int old_display_comment_prefix;
/* This checks and barfs if author is badly specified */
determine_author_info(author_ident);
- if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+ if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL))
return 0;
if (squash_message) {
@@ -649,10 +648,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
logfile);
hook_arg1 = "message";
} else if (use_message) {
+ char *buffer;
buffer = strstr(use_message_buffer, "\n\n");
- if (!use_editor && (!buffer || buffer[2] == '\0'))
- die(_("commit has empty message"));
- strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+ if (buffer)
+ strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
hook_arg1 = "commit";
hook_arg2 = use_message;
} else if (fixup_message) {
@@ -733,7 +732,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
eol = nl - sb.buf;
else
eol = sb.len;
- if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
+ if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
ignore_footer = sb.len - previous;
break;
}
@@ -753,8 +752,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
/* This checks if committer ident is explicitly given */
strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT));
if (use_editor && include_status) {
+ int ident_shown = 0;
+ int saved_color_setting;
char *ai_tmp, *ci_tmp;
- if (whence != FROM_COMMIT)
+ if (whence != FROM_COMMIT) {
+ if (cleanup_mode == CLEANUP_SCISSORS)
+ wt_status_add_cut_line(s->fp);
status_printf_ln(s, GIT_COLOR_NORMAL,
whence == FROM_MERGE
? _("\n"
@@ -770,6 +773,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
git_path(whence == FROM_MERGE
? "MERGE_HEAD"
: "CHERRY_PICK_HEAD"));
+ }
fprintf(s->fp, "\n");
if (cleanup_mode == CLEANUP_ALL)
@@ -777,6 +781,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
_("Please enter the commit message for your changes."
" Lines starting\nwith '%c' will be ignored, and an empty"
" message aborts the commit.\n"), comment_line_char);
+ else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
+ wt_status_add_cut_line(s->fp);
else /* CLEANUP_SPACE, that is. */
status_printf(s, GIT_COLOR_NORMAL,
_("Please enter the commit message for your changes."
@@ -826,8 +832,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (get_sha1(parent, sha1))
commitable = !!active_nr;
- else
- commitable = index_differs_from(parent, 0);
+ else {
+ /*
+ * Unless the user did explicitly request a submodule
+ * ignore mode by passing a command line option we do
+ * not ignore any changed submodule SHA-1s when
+ * comparing index and parent, no matter what is
+ * configured. Otherwise we won't commit any
+ * submodules which were manually staged, which would
+ * be really confusing.
+ */
+ int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ if (ignore_submodule_arg &&
+ !strcmp(ignore_submodule_arg, "all"))
+ diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
+ commitable = index_differs_from(parent, diff_flags);
+ }
}
strbuf_release(&committer_ident);
@@ -866,8 +886,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
}
- if (run_hook(index_file, "prepare-commit-msg",
- git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+ if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
+ git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
return 0;
if (use_editor) {
@@ -883,7 +903,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
if (!no_verify &&
- run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+ run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
return 0;
}
@@ -904,7 +924,7 @@ static int rest_is_empty(struct strbuf *sb, int start)
eol = sb->len;
if (strlen(sign_off_header) <= eol - i &&
- !prefixcmp(sb->buf + i, sign_off_header)) {
+ starts_with(sb->buf + i, sign_off_header)) {
i = eol;
continue;
}
@@ -1067,8 +1087,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
use_editor = 0;
if (0 <= edit_flag)
use_editor = edit_flag;
- if (!use_editor)
- setenv("GIT_EDITOR", ":", 1);
/* Sanity check options */
if (amend && !current_head)
@@ -1123,7 +1141,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (argc == 0 && only && amend)
only_include_assumed = _("Clever... amending the last one with dirty index.");
if (argc > 0 && !also && !only)
- only_include_assumed = _("Explicit paths specified without -i nor -o; assuming --only paths...");
+ only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths...");
if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
else if (!strcmp(cleanup_arg, "verbatim"))
@@ -1132,6 +1150,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
cleanup_mode = CLEANUP_SPACE;
else if (!strcmp(cleanup_arg, "strip"))
cleanup_mode = CLEANUP_ALL;
+ else if (!strcmp(cleanup_arg, "scissors"))
+ cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
else
die(_("Invalid cleanup mode %s"), cleanup_arg);
@@ -1183,7 +1203,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
{
struct wt_status *s = cb;
- if (!prefixcmp(k, "column."))
+ if (starts_with(k, "column."))
return git_column_config(k, v, "status", &s->colopts);
if (!strcmp(k, "status.submodulesummary")) {
int is_bool;
@@ -1211,7 +1231,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
s->display_comment_prefix = git_config_bool(k, v);
return 0;
}
- if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+ if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
int slot = parse_status_slot(k, 13);
if (slot < 0)
return 0;
@@ -1338,7 +1358,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
commit = lookup_commit(sha1);
if (!commit)
die(_("couldn't look up newly created commit"));
- if (!commit || parse_commit(commit))
+ if (parse_commit(commit))
die(_("could not parse newly created commit"));
strbuf_addstr(&format, "format:%h] %s");
@@ -1377,7 +1397,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
printf("[%s%s ",
- !prefixcmp(head, "refs/heads/") ?
+ starts_with(head, "refs/heads/") ?
head + 11 :
!strcmp(head, "HEAD") ?
_("detached HEAD") :
@@ -1406,6 +1426,10 @@ static int git_commit_config(const char *k, const char *v, void *cb)
}
if (!strcmp(k, "commit.cleanup"))
return git_config_string(&cleanup_arg, k, v);
+ if (!strcmp(k, "commit.gpgsign")) {
+ sign_commit = git_config_bool(k, v) ? "" : NULL;
+ return 0;
+ }
status = git_gpg_config(k, v, NULL);
if (status)
@@ -1445,6 +1469,29 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
return finish_command(&proc);
}
+int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
+{
+ const char *hook_env[3] = { NULL };
+ char index[PATH_MAX];
+ va_list args;
+ int ret;
+
+ snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+ hook_env[0] = index;
+
+ /*
+ * Let the hook know that no editor will be launched.
+ */
+ if (!editor_is_used)
+ hook_env[1] = "GIT_EDITOR=:";
+
+ va_start(args, name);
+ ret = run_hook_ve(hook_env, name, args);
+ va_end(args);
+
+ return ret;
+}
+
int cmd_commit(int argc, const char **argv, const char *prefix)
{
static struct wt_status s;
@@ -1467,7 +1514,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
- { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
+ { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
/* end commit message options */
@@ -1510,7 +1557,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct ref_lock *ref_lock;
struct commit_list *parents = NULL, **pptr = &parents;
struct stat statbuf;
- int allow_fast_forward = 1;
struct commit *current_head = NULL;
struct commit_extra_header *extra = NULL;
@@ -1525,7 +1571,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
current_head = NULL;
else {
current_head = lookup_commit_or_die(sha1, "HEAD");
- if (!current_head || parse_commit(current_head))
+ if (parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
argc = parse_and_validate_options(argc, argv, builtin_commit_options,
@@ -1558,6 +1604,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
} else if (whence == FROM_MERGE) {
struct strbuf m = STRBUF_INIT;
FILE *fp;
+ int allow_fast_forward = 1;
if (!reflog_msg)
reflog_msg = "commit (merge)";
@@ -1600,8 +1647,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die(_("could not read commit message: %s"), strerror(saved_errno));
}
- /* Truncate the message just before the diff, if any. */
- if (verbose)
+ if (verbose || /* Truncate the message just before the diff, if any. */
+ cleanup_mode == CLEANUP_SCISSORS)
wt_status_truncate_message_at_cut_line(&sb);
if (cleanup_mode != CLEANUP_NONE)
@@ -1669,7 +1716,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"not exceeded, and then \"git reset HEAD\" to recover."));
rerere(0);
- run_hook(get_index_file(), "post-commit", NULL);
+ run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
if (amend && !no_post_rewrite) {
struct notes_rewrite_cfg *cfg;
cfg = init_copy_notes_for_rewrite("amend");
diff --git a/builtin/config.c b/builtin/config.c
index 20e89fe..5677c94 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -21,8 +21,7 @@ static char key_delim = ' ';
static char term = '\n';
static int use_global_config, use_system_config, use_local_config;
-static const char *given_config_file;
-static const char *given_config_blob;
+static struct git_config_source given_config_source;
static int actions, types;
static const char *get_color_slot, *get_colorbool_slot;
static int end_null;
@@ -55,8 +54,8 @@ static struct option builtin_config_options[] = {
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
- OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
- OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
+ OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
+ OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
OPT_GROUP(N_("Action")),
OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
@@ -224,8 +223,7 @@ static int get_value(const char *key_, const char *regex_)
}
git_config_with_options(collect_config, &values,
- given_config_file, given_config_blob,
- respect_includes);
+ &given_config_source, respect_includes);
ret = !values.nr;
@@ -309,8 +307,7 @@ static void get_color(const char *def_color)
get_color_found = 0;
parsed_color[0] = '\0';
git_config_with_options(git_get_color_config, NULL,
- given_config_file, given_config_blob,
- respect_includes);
+ &given_config_source, respect_includes);
if (!get_color_found && def_color)
color_parse(def_color, "command line", parsed_color);
@@ -339,8 +336,7 @@ static int get_colorbool(int print)
get_diff_color_found = -1;
get_color_ui_found = -1;
git_config_with_options(git_get_colorbool_config, NULL,
- given_config_file, given_config_blob,
- respect_includes);
+ &given_config_source, respect_includes);
if (get_colorbool_found < 0) {
if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -362,9 +358,12 @@ static int get_colorbool(int print)
return get_colorbool_found ? 0 : 1;
}
-static void check_blob_write(void)
+static void check_write(void)
{
- if (given_config_blob)
+ if (given_config_source.use_stdin)
+ die("writing to stdin is not supported");
+
+ if (given_config_source.blob)
die("writing config blobs is not supported");
}
@@ -435,7 +434,7 @@ static int get_urlmatch(const char *var, const char *url)
}
git_config_with_options(urlmatch_config_entry, &config,
- given_config_file, NULL, respect_includes);
+ &given_config_source, respect_includes);
for_each_string_list_item(item, &values) {
struct urlmatch_current_candidate_value *matched = item->util;
@@ -464,18 +463,24 @@ int cmd_config(int argc, const char **argv, const char *prefix)
int nongit = !startup_info->have_repository;
char *value;
- given_config_file = getenv(CONFIG_ENVIRONMENT);
+ given_config_source.file = getenv(CONFIG_ENVIRONMENT);
argc = parse_options(argc, argv, prefix, builtin_config_options,
builtin_config_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (use_global_config + use_system_config + use_local_config +
- !!given_config_file + !!given_config_blob > 1) {
+ !!given_config_source.file + !!given_config_source.blob > 1) {
error("only one config file at a time.");
usage_with_options(builtin_config_usage, builtin_config_options);
}
+ if (given_config_source.file &&
+ !strcmp(given_config_source.file, "-")) {
+ given_config_source.file = NULL;
+ given_config_source.use_stdin = 1;
+ }
+
if (use_global_config) {
char *user_config = NULL;
char *xdg_config = NULL;
@@ -493,24 +498,24 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (access_or_warn(user_config, R_OK, 0) &&
xdg_config && !access_or_warn(xdg_config, R_OK, 0))
- given_config_file = xdg_config;
+ given_config_source.file = xdg_config;
else
- given_config_file = user_config;
+ given_config_source.file = user_config;
}
else if (use_system_config)
- given_config_file = git_etc_gitconfig();
+ given_config_source.file = git_etc_gitconfig();
else if (use_local_config)
- given_config_file = git_pathdup("config");
- else if (given_config_file) {
- if (!is_absolute_path(given_config_file) && prefix)
- given_config_file =
+ given_config_source.file = git_pathdup("config");
+ else if (given_config_source.file) {
+ if (!is_absolute_path(given_config_source.file) && prefix)
+ given_config_source.file =
xstrdup(prefix_filename(prefix,
strlen(prefix),
- given_config_file));
+ given_config_source.file));
}
if (respect_includes == -1)
- respect_includes = !given_config_file;
+ respect_includes = !given_config_source.file;
if (end_null) {
term = '\0';
@@ -549,57 +554,58 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (actions == ACTION_LIST) {
check_argc(argc, 0, 0);
if (git_config_with_options(show_all_config, NULL,
- given_config_file,
- given_config_blob,
+ &given_config_source,
respect_includes) < 0) {
- if (given_config_file)
+ if (given_config_source.file)
die_errno("unable to read config file '%s'",
- given_config_file);
+ given_config_source.file);
else
die("error processing config file(s)");
}
}
else if (actions == ACTION_EDIT) {
check_argc(argc, 0, 0);
- if (!given_config_file && nongit)
+ if (!given_config_source.file && nongit)
die("not in a git directory");
- if (given_config_blob)
+ if (given_config_source.use_stdin)
+ die("editing stdin is not supported");
+ if (given_config_source.blob)
die("editing blobs is not supported");
git_config(git_default_config, NULL);
- launch_editor(given_config_file ?
- given_config_file : git_path("config"),
+ launch_editor(given_config_source.file ?
+ given_config_source.file : git_path("config"),
NULL, NULL);
}
else if (actions == ACTION_SET) {
int ret;
- check_blob_write();
+ check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
- ret = git_config_set_in_file(given_config_file, argv[0], value);
+ ret = git_config_set_in_file(given_config_source.file, argv[0], value);
if (ret == CONFIG_NOTHING_SET)
error("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s.", argv[0]);
return ret;
}
else if (actions == ACTION_SET_ALL) {
- check_blob_write();
+ check_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
- return git_config_set_multivar_in_file(given_config_file,
+ return git_config_set_multivar_in_file(given_config_source.file,
argv[0], value, argv[2], 0);
}
else if (actions == ACTION_ADD) {
- check_blob_write();
+ check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
- return git_config_set_multivar_in_file(given_config_file,
+ return git_config_set_multivar_in_file(given_config_source.file,
argv[0], value, "^$", 0);
}
else if (actions == ACTION_REPLACE_ALL) {
- check_blob_write();
+ check_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
- return git_config_set_multivar_in_file(given_config_file,
+ return git_config_set_multivar_in_file(given_config_source.file,
argv[0], value, argv[2], 1);
}
else if (actions == ACTION_GET) {
@@ -623,26 +629,26 @@ int cmd_config(int argc, const char **argv, const char *prefix)
return get_urlmatch(argv[0], argv[1]);
}
else if (actions == ACTION_UNSET) {
- check_blob_write();
+ check_write();
check_argc(argc, 1, 2);
if (argc == 2)
- return git_config_set_multivar_in_file(given_config_file,
+ return git_config_set_multivar_in_file(given_config_source.file,
argv[0], NULL, argv[1], 0);
else
- return git_config_set_in_file(given_config_file,
+ return git_config_set_in_file(given_config_source.file,
argv[0], NULL);
}
else if (actions == ACTION_UNSET_ALL) {
- check_blob_write();
+ check_write();
check_argc(argc, 1, 2);
- return git_config_set_multivar_in_file(given_config_file,
+ return git_config_set_multivar_in_file(given_config_source.file,
argv[0], NULL, argv[1], 1);
}
else if (actions == ACTION_RENAME_SECTION) {
int ret;
- check_blob_write();
+ check_write();
check_argc(argc, 2, 2);
- ret = git_config_rename_section_in_file(given_config_file,
+ ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], argv[1]);
if (ret < 0)
return ret;
@@ -651,9 +657,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
else if (actions == ACTION_REMOVE_SECTION) {
int ret;
- check_blob_write();
+ check_write();
check_argc(argc, 1, 1);
- ret = git_config_rename_section_in_file(given_config_file,
+ ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], NULL);
if (ret < 0)
return ret;
@@ -671,9 +677,3 @@ int cmd_config(int argc, const char **argv, const char *prefix)
return 0;
}
-
-int cmd_repo_config(int argc, const char **argv, const char *prefix)
-{
- fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n");
- return cmd_config(argc, argv, prefix);
-}
diff --git a/builtin/describe.c b/builtin/describe.c
index 6f62109..24d740c 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -6,7 +6,7 @@
#include "exec_cmd.h"
#include "parse-options.h"
#include "diff.h"
-#include "hash.h"
+#include "hashmap.h"
#include "argv-array.h"
#define SEEN (1u << 0)
@@ -25,7 +25,7 @@ static int longformat;
static int first_parent;
static int abbrev = -1; /* unspecified */
static int max_candidates = 10;
-static struct hash_table names;
+static struct hashmap names;
static int have_util;
static const char *pattern;
static int always;
@@ -37,7 +37,7 @@ static const char *diff_index_args[] = {
};
struct commit_name {
- struct commit_name *next;
+ struct hashmap_entry entry;
unsigned char peeled[20];
struct tag *tag;
unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
@@ -50,6 +50,12 @@ static const char *prio_names[] = {
"head", "lightweight", "annotated",
};
+static int commit_name_cmp(const struct commit_name *cn1,
+ const struct commit_name *cn2, const void *peeled)
+{
+ return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled);
+}
+
static inline unsigned int hash_sha1(const unsigned char *sha1)
{
unsigned int hash;
@@ -59,21 +65,9 @@ static inline unsigned int hash_sha1(const unsigned char *sha1)
static inline struct commit_name *find_commit_name(const unsigned char *peeled)
{
- struct commit_name *n = lookup_hash(hash_sha1(peeled), &names);
- while (n && !!hashcmp(peeled, n->peeled))
- n = n->next;
- return n;
-}
-
-static int set_util(void *chain, void *data)
-{
- struct commit_name *n;
- for (n = chain; n; n = n->next) {
- struct commit *c = lookup_commit_reference_gently(n->peeled, 1);
- if (c)
- c->util = n;
- }
- return 0;
+ struct commit_name key;
+ hashmap_entry_init(&key, hash_sha1(peeled));
+ return hashmap_get(&names, &key, peeled);
}
static int replace_name(struct commit_name *e,
@@ -118,16 +112,10 @@ static void add_to_known_names(const char *path,
struct tag *tag = NULL;
if (replace_name(e, prio, sha1, &tag)) {
if (!e) {
- void **pos;
e = xmalloc(sizeof(struct commit_name));
hashcpy(e->peeled, peeled);
- pos = insert_hash(hash_sha1(peeled), e, &names);
- if (pos) {
- e->next = *pos;
- *pos = e;
- } else {
- e->next = NULL;
- }
+ hashmap_entry_init(e, hash_sha1(peeled));
+ hashmap_add(&names, e);
e->path = NULL;
}
e->tag = tag;
@@ -141,7 +129,7 @@ static void add_to_known_names(const char *path,
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
- int is_tag = !prefixcmp(path, "refs/tags/");
+ int is_tag = starts_with(path, "refs/tags/");
unsigned char peeled[20];
int is_annotated, prio;
@@ -150,7 +138,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
return 0;
/* Accept only tags that match the pattern, if given */
- if (pattern && (!is_tag || fnmatch(pattern, path + 10, 0)))
+ if (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL)))
return 0;
/* Is it annotated? */
@@ -292,7 +280,14 @@ static void describe(const char *arg, int last_one)
fprintf(stderr, _("searching to describe %s\n"), arg);
if (!have_util) {
- for_each_hash(&names, set_util, NULL);
+ struct hashmap_iter iter;
+ struct commit *c;
+ struct commit_name *n = hashmap_iter_first(&names, &iter);
+ for (; n; n = hashmap_iter_next(&iter)) {
+ c = lookup_commit_reference_gently(n->peeled, 1);
+ if (c)
+ c->util = n;
+ }
have_util = 1;
}
@@ -463,9 +458,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
return cmd_name_rev(args.argc, args.argv, prefix);
}
- init_hash(&names);
+ hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0);
for_each_rawref(get_name, NULL);
- if (!names.nr && !always)
+ if (!names.size && !always)
die(_("No names found, cannot describe anything."));
if (argc == 0) {
diff --git a/builtin/diff.c b/builtin/diff.c
index fe0cc7f..0f247d2 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -16,6 +16,9 @@
#include "submodule.h"
#include "sha1-array.h"
+#define DIFF_NO_INDEX_EXPLICIT 1
+#define DIFF_NO_INDEX_IMPLICIT 2
+
struct blobinfo {
unsigned char sha1[20];
const char *name;
@@ -259,7 +262,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
struct object_array ent = OBJECT_ARRAY_INIT;
int blobs = 0, paths = 0;
struct blobinfo blob[2];
- int nongit;
+ int nongit = 0, no_index = 0;
int result = 0;
/*
@@ -285,14 +288,59 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
* Other cases are errors.
*/
- prefix = setup_git_directory_gently(&nongit);
- gitmodules_config();
+ /* Were we asked to do --no-index explicitly? */
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--")) {
+ i++;
+ break;
+ }
+ if (!strcmp(argv[i], "--no-index"))
+ no_index = DIFF_NO_INDEX_EXPLICIT;
+ if (argv[i][0] != '-')
+ break;
+ }
+
+ if (!no_index)
+ prefix = setup_git_directory_gently(&nongit);
+
+ /*
+ * Treat git diff with at least one path outside of the
+ * repo the same as if the command would have been executed
+ * outside of a git repository. In this case it behaves
+ * the same way as "git diff --no-index <a> <b>", which acts
+ * as a colourful "diff" replacement.
+ */
+ if (nongit || ((argc == i + 2) &&
+ (!path_inside_repo(prefix, argv[i]) ||
+ !path_inside_repo(prefix, argv[i + 1]))))
+ no_index = DIFF_NO_INDEX_IMPLICIT;
+
+ if (!no_index)
+ gitmodules_config();
git_config(git_diff_ui_config, NULL);
init_revisions(&rev, prefix);
- /* If this is a no-index diff, just run it and exit there. */
- diff_no_index(&rev, argc, argv, nongit, prefix);
+ if (no_index && argc != i + 2) {
+ if (no_index == DIFF_NO_INDEX_IMPLICIT) {
+ /*
+ * There was no --no-index and there were not two
+ * paths. It is possible that the user intended
+ * to do an inside-repository operation.
+ */
+ fprintf(stderr, "Not a git repository\n");
+ fprintf(stderr,
+ "To compare two paths outside a working tree:\n");
+ }
+ /* Give the usage message for non-repository usage and exit. */
+ usagef("git diff %s <path> <path>",
+ no_index == DIFF_NO_INDEX_EXPLICIT ?
+ "--no-index" : "[--no-index]");
+
+ }
+ if (no_index)
+ /* If this is a no-index diff, just run it and exit there. */
+ diff_no_index(&rev, argc, argv, prefix);
/* Otherwise, we are doing the usual "git" diff */
rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 78250ea..b8d8a3a 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -287,7 +287,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
- parse_commit(commit);
+ parse_commit_or_die(commit);
author = strstr(commit->buffer, "\nauthor ");
if (!author)
die ("Could not find author in commit %s",
@@ -308,7 +308,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
if (commit->parents &&
get_object_mark(&commit->parents->item->object) != 0 &&
!full_tree) {
- parse_commit(commit->parents->item);
+ parse_commit_or_die(commit->parents->item);
diff_tree_sha1(commit->parents->item->tree->object.sha1,
commit->tree->object.sha1, "", &rev->diffopt);
}
@@ -476,7 +476,7 @@ static void handle_tag(const char *name, struct tag *tag)
}
}
- if (!prefixcmp(name, "refs/tags/"))
+ if (starts_with(name, "refs/tags/"))
name += 10;
printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
name, tagged_mark,
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 758b5ac..1262b40 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -3,6 +3,7 @@
#include "fetch-pack.h"
#include "remote.h"
#include "connect.h"
+#include "sha1-array.h"
static const char fetch_pack_usage[] =
"git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
@@ -13,6 +14,13 @@ static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
const char *name, int namelen)
{
struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
+ unsigned char sha1[20];
+
+ if (namelen > 41 && name[40] == ' ' && !get_sha1_hex(name, sha1)) {
+ hashcpy(ref->old_sha1, sha1);
+ name += 41;
+ namelen -= 41;
+ }
memcpy(ref->name, name, namelen);
ref->name[namelen] = '\0';
@@ -39,6 +47,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
char **pack_lockfile_ptr = NULL;
struct child_process *conn;
struct fetch_pack_args args;
+ struct sha1_array shallow = SHA1_ARRAY_INIT;
packet_trace_identity("fetch-pack");
@@ -48,11 +57,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc && *argv[i] == '-'; i++) {
const char *arg = argv[i];
- if (!prefixcmp(arg, "--upload-pack=")) {
+ if (starts_with(arg, "--upload-pack=")) {
args.uploadpack = arg + 14;
continue;
}
- if (!prefixcmp(arg, "--exec=")) {
+ if (starts_with(arg, "--exec=")) {
args.uploadpack = arg + 7;
continue;
}
@@ -89,7 +98,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.verbose = 1;
continue;
}
- if (!prefixcmp(arg, "--depth=")) {
+ if (starts_with(arg, "--depth=")) {
args.depth = strtol(arg + 8, NULL, 0);
continue;
}
@@ -110,6 +119,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.check_self_contained_and_connected = 1;
continue;
}
+ if (!strcmp("--cloning", arg)) {
+ args.cloning = 1;
+ continue;
+ }
+ if (!strcmp("--update-shallow", arg)) {
+ args.update_shallow = 1;
+ continue;
+ }
usage(fetch_pack_usage);
}
@@ -158,10 +175,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
if (!conn)
return args.diag_url ? 0 : 1;
}
- get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
+ get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow);
- ref = fetch_pack(&args, fd, conn, ref, dest,
- sought, nr_sought, pack_lockfile_ptr);
+ ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
+ &shallow, pack_lockfile_ptr);
if (pack_lockfile) {
printf("lock %s\n", pack_lockfile);
fflush(stdout);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5bd00d0..55f457c 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -36,7 +36,7 @@ static int prune = -1; /* unspecified */
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int tags = TAGS_DEFAULT, unshallow;
+static int tags = TAGS_DEFAULT, unshallow, update_shallow;
static const char *depth;
static const char *upload_pack;
static struct strbuf default_rla = STRBUF_INIT;
@@ -44,6 +44,7 @@ static struct transport *gtransport;
static struct transport *gsecondary;
static const char *submodule_prefix = "";
static const char *recurse_submodules_default;
+static int shown_url = 0;
static int option_parse_recurse_submodules(const struct option *opt,
const char *arg, int unset)
@@ -104,6 +105,8 @@ static struct option builtin_fetch_options[] = {
{ OPTION_STRING, 0, "recurse-submodules-default",
&recurse_submodules_default, NULL,
N_("default mode for recursion"), PARSE_OPT_HIDDEN },
+ OPT_BOOL(0, "update-shallow", &update_shallow,
+ N_("accept refs that update .git/shallow")),
OPT_END()
};
@@ -160,48 +163,156 @@ static void add_merge_config(struct ref **head,
}
}
+static int add_existing(const char *refname, const unsigned char *sha1,
+ int flag, void *cbdata)
+{
+ struct string_list *list = (struct string_list *)cbdata;
+ struct string_list_item *item = string_list_insert(list, refname);
+ item->util = xmalloc(20);
+ hashcpy(item->util, sha1);
+ return 0;
+}
+
+static int will_fetch(struct ref **head, const unsigned char *sha1)
+{
+ struct ref *rm = *head;
+ while (rm) {
+ if (!hashcmp(rm->old_sha1, sha1))
+ return 1;
+ rm = rm->next;
+ }
+ return 0;
+}
+
static void find_non_local_tags(struct transport *transport,
struct ref **head,
- struct ref ***tail);
+ struct ref ***tail)
+{
+ struct string_list existing_refs = STRING_LIST_INIT_DUP;
+ struct string_list remote_refs = STRING_LIST_INIT_NODUP;
+ const struct ref *ref;
+ struct string_list_item *item = NULL;
+
+ for_each_ref(add_existing, &existing_refs);
+ for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
+ if (!starts_with(ref->name, "refs/tags/"))
+ continue;
+
+ /*
+ * The peeled ref always follows the matching base
+ * ref, so if we see a peeled ref that we don't want
+ * to fetch then we can mark the ref entry in the list
+ * as one to ignore by setting util to NULL.
+ */
+ if (ends_with(ref->name, "^{}")) {
+ if (item && !has_sha1_file(ref->old_sha1) &&
+ !will_fetch(head, ref->old_sha1) &&
+ !has_sha1_file(item->util) &&
+ !will_fetch(head, item->util))
+ item->util = NULL;
+ item = NULL;
+ continue;
+ }
+
+ /*
+ * If item is non-NULL here, then we previously saw a
+ * ref not followed by a peeled reference, so we need
+ * to check if it is a lightweight tag that we want to
+ * fetch.
+ */
+ if (item && !has_sha1_file(item->util) &&
+ !will_fetch(head, item->util))
+ item->util = NULL;
+
+ item = NULL;
+
+ /* skip duplicates and refs that we already have */
+ if (string_list_has_string(&remote_refs, ref->name) ||
+ string_list_has_string(&existing_refs, ref->name))
+ continue;
+
+ item = string_list_insert(&remote_refs, ref->name);
+ item->util = (void *)ref->old_sha1;
+ }
+ string_list_clear(&existing_refs, 1);
+
+ /*
+ * We may have a final lightweight tag that needs to be
+ * checked to see if it needs fetching.
+ */
+ if (item && !has_sha1_file(item->util) &&
+ !will_fetch(head, item->util))
+ item->util = NULL;
+
+ /*
+ * For all the tags in the remote_refs string list,
+ * add them to the list of refs to be fetched
+ */
+ for_each_string_list_item(item, &remote_refs) {
+ /* Unless we have already decided to ignore this item... */
+ if (item->util)
+ {
+ struct ref *rm = alloc_ref(item->string);
+ rm->peer_ref = alloc_ref(item->string);
+ hashcpy(rm->old_sha1, item->util);
+ **tail = rm;
+ *tail = &rm->next;
+ }
+ }
+
+ string_list_clear(&remote_refs, 0);
+}
static struct ref *get_ref_map(struct transport *transport,
- struct refspec *refs, int ref_count, int tags,
- int *autotags)
+ struct refspec *refspecs, int refspec_count,
+ int tags, int *autotags)
{
int i;
struct ref *rm;
struct ref *ref_map = NULL;
struct ref **tail = &ref_map;
- const struct ref *remote_refs = transport_get_remote_refs(transport);
+ /* opportunistically-updated references: */
+ struct ref *orefs = NULL, **oref_tail = &orefs;
- if (ref_count || tags == TAGS_SET) {
- struct ref **old_tail;
+ const struct ref *remote_refs = transport_get_remote_refs(transport);
- for (i = 0; i < ref_count; i++) {
- get_fetch_map(remote_refs, &refs[i], &tail, 0);
- if (refs[i].dst && refs[i].dst[0])
+ if (refspec_count) {
+ for (i = 0; i < refspec_count; i++) {
+ get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
+ if (refspecs[i].dst && refspecs[i].dst[0])
*autotags = 1;
}
- /* Merge everything on the command line, but not --tags */
+ /* Merge everything on the command line (but not --tags) */
for (rm = ref_map; rm; rm = rm->next)
rm->fetch_head_status = FETCH_HEAD_MERGE;
- if (tags == TAGS_SET)
- get_fetch_map(remote_refs, tag_refspec, &tail, 0);
/*
- * For any refs that we happen to be fetching via command-line
- * arguments, take the opportunity to update their configured
- * counterparts. However, we do not want to mention these
- * entries in FETCH_HEAD at all, as they would simply be
- * duplicates of existing entries.
+ * For any refs that we happen to be fetching via
+ * command-line arguments, the destination ref might
+ * have been missing or have been different than the
+ * remote-tracking ref that would be derived from the
+ * configured refspec. In these cases, we want to
+ * take the opportunity to update their configured
+ * remote-tracking reference. However, we do not want
+ * to mention these entries in FETCH_HEAD at all, as
+ * they would simply be duplicates of existing
+ * entries, so we set them FETCH_HEAD_IGNORE below.
+ *
+ * We compute these entries now, based only on the
+ * refspecs specified on the command line. But we add
+ * them to the list following the refspecs resulting
+ * from the tags option so that one of the latter,
+ * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed
+ * by ref_remove_duplicates() in favor of one of these
+ * opportunistic entries with FETCH_HEAD_IGNORE.
*/
- old_tail = tail;
for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
get_fetch_map(ref_map, &transport->remote->fetch[i],
- &tail, 1);
- for (rm = *old_tail; rm; rm = rm->next)
- rm->fetch_head_status = FETCH_HEAD_IGNORE;
+ &oref_tail, 1);
+
+ if (tags == TAGS_SET)
+ get_fetch_map(remote_refs, tag_refspec, &tail, 0);
} else {
/* Use the defaults */
struct remote *remote = transport->remote;
@@ -238,11 +349,21 @@ static struct ref *get_ref_map(struct transport *transport,
tail = &ref_map->next;
}
}
- if (tags == TAGS_DEFAULT && *autotags)
+
+ if (tags == TAGS_SET)
+ /* also fetch all tags */
+ get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+ else if (tags == TAGS_DEFAULT && *autotags)
find_non_local_tags(transport, &ref_map, &tail);
- ref_remove_duplicates(ref_map);
- return ref_map;
+ /* Now append any refs to be updated opportunistically: */
+ *tail = orefs;
+ for (rm = orefs; rm; rm = rm->next) {
+ rm->fetch_head_status = FETCH_HEAD_IGNORE;
+ tail = &rm->next;
+ }
+
+ return ref_remove_duplicates(ref_map);
}
#define STORE_REF_ERROR_OTHER 1
@@ -313,7 +434,7 @@ static int update_local_ref(struct ref *ref,
}
if (!is_null_sha1(ref->old_sha1) &&
- !prefixcmp(ref->name, "refs/tags/")) {
+ starts_with(ref->name, "refs/tags/")) {
int r;
r = s_update_ref("updating tag", ref, 0);
strbuf_addf(display, "%c %-*s %-*s -> %s%s",
@@ -336,10 +457,10 @@ static int update_local_ref(struct ref *ref,
* more likely to follow a standard layout.
*/
const char *name = remote_ref ? remote_ref->name : "";
- if (!prefixcmp(name, "refs/tags/")) {
+ if (starts_with(name, "refs/tags/")) {
msg = "storing tag";
what = _("[new tag]");
- } else if (!prefixcmp(name, "refs/heads/")) {
+ } else if (starts_with(name, "refs/heads/")) {
msg = "storing head";
what = _("[new branch]");
} else {
@@ -405,6 +526,8 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
struct ref **rm = cb_data;
struct ref *ref = *rm;
+ while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
+ ref = ref->next;
if (!ref)
return -1; /* end of the list */
*rm = ref->next;
@@ -417,7 +540,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
{
FILE *fp;
struct commit *commit;
- int url_len, i, shown_url = 0, rc = 0;
+ int url_len, i, rc = 0;
struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
@@ -451,6 +574,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
struct ref *ref = NULL;
const char *merge_status_marker = "";
+ if (rm->status == REF_STATUS_REJECT_SHALLOW) {
+ if (want_status == FETCH_HEAD_MERGE)
+ warning(_("reject %s because shallow roots are not allowed to be updated"),
+ rm->peer_ref ? rm->peer_ref->name : rm->name);
+ continue;
+ }
+
commit = lookup_commit_reference_gently(rm->old_sha1, 1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
@@ -471,15 +601,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
kind = "";
what = "";
}
- else if (!prefixcmp(rm->name, "refs/heads/")) {
+ else if (starts_with(rm->name, "refs/heads/")) {
kind = "branch";
what = rm->name + 11;
}
- else if (!prefixcmp(rm->name, "refs/tags/")) {
+ else if (starts_with(rm->name, "refs/tags/")) {
kind = "tag";
what = rm->name + 10;
}
- else if (!prefixcmp(rm->name, "refs/remotes/")) {
+ else if (starts_with(rm->name, "refs/remotes/")) {
kind = "remote-tracking branch";
what = rm->name + 13;
}
@@ -590,17 +720,36 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
return ret;
}
-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
+ const char *raw_url)
{
- int result = 0;
+ int url_len, i, result = 0;
struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
+ char *url;
const char *dangling_msg = dry_run
? _(" (%s will become dangling)")
: _(" (%s has become dangling)");
+ if (raw_url)
+ url = transport_anonymize_url(raw_url);
+ else
+ url = xstrdup("foreign");
+
+ url_len = strlen(url);
+ for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+ ;
+
+ url_len = i + 1;
+ if (4 < i && !strncmp(".git", url + i - 3, 4))
+ url_len = i - 3;
+
for (ref = stale_refs; ref; ref = ref->next) {
if (!dry_run)
result |= delete_ref(ref->name, NULL, 0);
+ if (verbosity >= 0 && !shown_url) {
+ fprintf(stderr, _("From %.*s\n"), url_len, url);
+ shown_url = 1;
+ }
if (verbosity >= 0) {
fprintf(stderr, " x %-*s %-*s -> %s\n",
TRANSPORT_SUMMARY(_("[deleted]")),
@@ -608,110 +757,11 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
+ free(url);
free_refs(stale_refs);
return result;
}
-static int add_existing(const char *refname, const unsigned char *sha1,
- int flag, void *cbdata)
-{
- struct string_list *list = (struct string_list *)cbdata;
- struct string_list_item *item = string_list_insert(list, refname);
- item->util = xmalloc(20);
- hashcpy(item->util, sha1);
- return 0;
-}
-
-static int will_fetch(struct ref **head, const unsigned char *sha1)
-{
- struct ref *rm = *head;
- while (rm) {
- if (!hashcmp(rm->old_sha1, sha1))
- return 1;
- rm = rm->next;
- }
- return 0;
-}
-
-static void find_non_local_tags(struct transport *transport,
- struct ref **head,
- struct ref ***tail)
-{
- struct string_list existing_refs = STRING_LIST_INIT_DUP;
- struct string_list remote_refs = STRING_LIST_INIT_NODUP;
- const struct ref *ref;
- struct string_list_item *item = NULL;
-
- for_each_ref(add_existing, &existing_refs);
- for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
- if (prefixcmp(ref->name, "refs/tags/"))
- continue;
-
- /*
- * The peeled ref always follows the matching base
- * ref, so if we see a peeled ref that we don't want
- * to fetch then we can mark the ref entry in the list
- * as one to ignore by setting util to NULL.
- */
- if (!suffixcmp(ref->name, "^{}")) {
- if (item && !has_sha1_file(ref->old_sha1) &&
- !will_fetch(head, ref->old_sha1) &&
- !has_sha1_file(item->util) &&
- !will_fetch(head, item->util))
- item->util = NULL;
- item = NULL;
- continue;
- }
-
- /*
- * If item is non-NULL here, then we previously saw a
- * ref not followed by a peeled reference, so we need
- * to check if it is a lightweight tag that we want to
- * fetch.
- */
- if (item && !has_sha1_file(item->util) &&
- !will_fetch(head, item->util))
- item->util = NULL;
-
- item = NULL;
-
- /* skip duplicates and refs that we already have */
- if (string_list_has_string(&remote_refs, ref->name) ||
- string_list_has_string(&existing_refs, ref->name))
- continue;
-
- item = string_list_insert(&remote_refs, ref->name);
- item->util = (void *)ref->old_sha1;
- }
- string_list_clear(&existing_refs, 1);
-
- /*
- * We may have a final lightweight tag that needs to be
- * checked to see if it needs fetching.
- */
- if (item && !has_sha1_file(item->util) &&
- !will_fetch(head, item->util))
- item->util = NULL;
-
- /*
- * For all the tags in the remote_refs string list,
- * add them to the list of refs to be fetched
- */
- for_each_string_list_item(item, &remote_refs) {
- /* Unless we have already decided to ignore this item... */
- if (item->util)
- {
- struct ref *rm = alloc_ref(item->string);
- rm->peer_ref = alloc_ref(item->string);
- hashcpy(rm->old_sha1, item->util);
- **tail = rm;
- *tail = &rm->next;
- }
- }
-
- string_list_clear(&remote_refs, 0);
-}
-
static void check_not_current_branch(struct ref *ref_map)
{
struct branch *current_branch = branch_get(NULL);
@@ -759,6 +809,8 @@ static struct transport *prepare_transport(struct remote *remote)
set_option(transport, TRANS_OPT_KEEP, "yes");
if (depth)
set_option(transport, TRANS_OPT_DEPTH, depth);
+ if (update_shallow)
+ set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
return transport;
}
@@ -824,39 +876,26 @@ static int do_fetch(struct transport *transport,
if (tags == TAGS_DEFAULT && autotags)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
- if (fetch_refs(transport, ref_map)) {
- free_refs(ref_map);
- retcode = 1;
- goto cleanup;
- }
if (prune) {
/*
- * If --tags was specified, pretend that the user gave us
- * the canonical tags refspec
+ * We only prune based on refspecs specified
+ * explicitly (via command line or configuration); we
+ * don't care whether --tags was specified.
*/
- if (tags == TAGS_SET) {
- const char *tags_str = "refs/tags/*:refs/tags/*";
- struct refspec *tags_refspec, *refspec;
-
- /* Copy the refspec and add the tags to it */
- refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
- tags_refspec = parse_fetch_refspec(1, &tags_str);
- memcpy(refspec, refs, ref_count * sizeof(struct refspec));
- memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
- ref_count++;
-
- prune_refs(refspec, ref_count, ref_map);
-
- ref_count--;
- /* The rest of the strings belong to fetch_one */
- free_refspec(1, tags_refspec);
- free(refspec);
- } else if (ref_count) {
- prune_refs(refs, ref_count, ref_map);
+ if (ref_count) {
+ prune_refs(refs, ref_count, ref_map, transport->url);
} else {
- prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
+ prune_refs(transport->remote->fetch,
+ transport->remote->fetch_refspec_nr,
+ ref_map,
+ transport->url);
}
}
+ if (fetch_refs(transport, ref_map)) {
+ free_refs(ref_map);
+ retcode = 1;
+ goto cleanup;
+ }
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -892,7 +931,7 @@ static int get_remote_group(const char *key, const char *value, void *priv)
{
struct remote_group_data *g = priv;
- if (!prefixcmp(key, "remotes.") &&
+ if (starts_with(key, "remotes.") &&
!strcmp(key + 8, g->name)) {
/* split list by white space */
int space = strcspn(value, " \t\n");
@@ -930,8 +969,8 @@ static void add_options_to_argv(struct argv_array *argv)
{
if (dry_run)
argv_array_push(argv, "--dry-run");
- if (prune > 0)
- argv_array_push(argv, "--prune");
+ if (prune != -1)
+ argv_array_push(argv, prune ? "--prune" : "--no-prune");
if (update_head_ok)
argv_array_push(argv, "--update-head-ok");
if (force)
@@ -987,7 +1026,6 @@ static int fetch_multiple(struct string_list *list)
static int fetch_one(struct remote *remote, int argc, const char **argv)
{
- int i;
static const char **refs = NULL;
struct refspec *refspec;
int ref_nr = 0;
@@ -1011,6 +1049,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
if (argc > 0) {
int j = 0;
+ int i;
refs = xcalloc(argc + 1, sizeof(const char *));
for (i = 0; i < argc; i++) {
if (!strcmp(argv[i], "tag")) {
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 1c04070..3906eda 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -109,7 +109,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
if (len < 43 || line[40] != '\t')
return 1;
- if (!prefixcmp(line + 41, "not-for-merge"))
+ if (starts_with(line + 41, "not-for-merge"))
return 0;
if (line[41] != '\t')
@@ -155,16 +155,16 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
if (pulling_head) {
origin = src;
src_data->head_status |= 1;
- } else if (!prefixcmp(line, "branch ")) {
+ } else if (starts_with(line, "branch ")) {
origin_data->is_local_branch = 1;
origin = line + 7;
string_list_append(&src_data->branch, origin);
src_data->head_status |= 2;
- } else if (!prefixcmp(line, "tag ")) {
+ } else if (starts_with(line, "tag ")) {
origin = line;
string_list_append(&src_data->tag, origin + 4);
src_data->head_status |= 2;
- } else if (!prefixcmp(line, "remote-tracking branch ")) {
+ } else if (starts_with(line, "remote-tracking branch ")) {
origin = line + strlen("remote-tracking branch ");
string_list_append(&src_data->r_branch, origin);
src_data->head_status |= 2;
@@ -605,7 +605,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
resolve_refdup("HEAD", head_sha1, 1, NULL);
if (!current_branch)
die("No current branch");
- if (!prefixcmp(current_branch, "refs/heads/"))
+ if (starts_with(current_branch, "refs/heads/"))
current_branch += 11;
find_merge_parents(&merge_parents, in, head_sha1);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 7557aa2..3e1d5c3 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -9,6 +9,7 @@
#include "quote.h"
#include "parse-options.h"
#include "remote.h"
+#include "color.h"
/* Quoting styles */
#define QUOTE_NONE 0
@@ -75,6 +76,8 @@ static struct {
{ "upstream" },
{ "symref" },
{ "flag" },
+ { "HEAD" },
+ { "color" },
};
/*
@@ -90,6 +93,7 @@ static struct {
static const char **used_atom;
static cmp_type *used_atom_type;
static int used_atom_cnt, need_tagged, need_symref;
+static int need_color_reset_at_eol;
/*
* Used to parse format string and sort specifiers
@@ -176,13 +180,21 @@ static const char *find_next(const char *cp)
static int verify_format(const char *format)
{
const char *cp, *sp;
+ static const char color_reset[] = "color:reset";
+
+ need_color_reset_at_eol = 0;
for (cp = format; *cp && (sp = find_next(cp)); ) {
const char *ep = strchr(sp, ')');
+ int at;
+
if (!ep)
return error("malformed format string %s", sp);
/* sp points at "%(" and ep points at the closing ")" */
- parse_atom(sp + 2, ep);
+ at = parse_atom(sp + 2, ep);
cp = ep + 1;
+
+ if (!memcmp(used_atom[at], "color:", 6))
+ need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset);
}
return 0;
}
@@ -441,7 +453,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
if (name[wholen] != 0 &&
strcmp(name + wholen, "name") &&
strcmp(name + wholen, "email") &&
- prefixcmp(name + wholen, "date"))
+ !starts_with(name + wholen, "date"))
continue;
if (!wholine)
wholine = find_wholine(who, wholen, buf, sz);
@@ -453,7 +465,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
v->s = copy_name(wholine);
else if (!strcmp(name + wholen, "email"))
v->s = copy_email(wholine);
- else if (!prefixcmp(name + wholen, "date"))
+ else if (starts_with(name + wholen, "date"))
grab_date(wholine, v, name);
}
@@ -475,7 +487,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
if (deref)
name++;
- if (!prefixcmp(name, "creatordate"))
+ if (starts_with(name, "creatordate"))
grab_date(wholine, v, name);
else if (!strcmp(name, "creator"))
v->s = copy_line(wholine);
@@ -649,20 +661,20 @@ static void populate_value(struct refinfo *ref)
int deref = 0;
const char *refname;
const char *formatp;
+ struct branch *branch = NULL;
if (*name == '*') {
deref = 1;
name++;
}
- if (!prefixcmp(name, "refname"))
+ if (starts_with(name, "refname"))
refname = ref->refname;
- else if (!prefixcmp(name, "symref"))
+ else if (starts_with(name, "symref"))
refname = ref->symref ? ref->symref : "";
- else if (!prefixcmp(name, "upstream")) {
- struct branch *branch;
+ else if (starts_with(name, "upstream")) {
/* only local branches may have an upstream */
- if (prefixcmp(ref->refname, "refs/heads/"))
+ if (!starts_with(ref->refname, "refs/heads/"))
continue;
branch = branch_get(ref->refname + 11);
@@ -670,8 +682,13 @@ static void populate_value(struct refinfo *ref)
!branch->merge[0]->dst)
continue;
refname = branch->merge[0]->dst;
- }
- else if (!strcmp(name, "flag")) {
+ } else if (starts_with(name, "color:")) {
+ char color[COLOR_MAXLEN] = "";
+
+ color_parse(name + 6, "--format", color);
+ v->s = xstrdup(color);
+ continue;
+ } else if (!strcmp(name, "flag")) {
char buf[256], *cp = buf;
if (ref->flag & REF_ISSYMREF)
cp = copy_advance(cp, ",symref");
@@ -684,20 +701,62 @@ static void populate_value(struct refinfo *ref)
v->s = xstrdup(buf + 1);
}
continue;
- }
- else if (!deref && grab_objectname(name, ref->objectname, v))
+ } else if (!deref && grab_objectname(name, ref->objectname, v)) {
continue;
- else
+ } else if (!strcmp(name, "HEAD")) {
+ const char *head;
+ unsigned char sha1[20];
+
+ head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ if (!strcmp(ref->refname, head))
+ v->s = "*";
+ else
+ v->s = " ";
+ continue;
+ } else
continue;
formatp = strchr(name, ':');
- /* look for "short" refname format */
if (formatp) {
+ int num_ours, num_theirs;
+
formatp++;
if (!strcmp(formatp, "short"))
refname = shorten_unambiguous_ref(refname,
warn_ambiguous_refs);
- else
+ else if (!strcmp(formatp, "track") &&
+ starts_with(name, "upstream")) {
+ char buf[40];
+
+ stat_tracking_info(branch, &num_ours, &num_theirs);
+ if (!num_ours && !num_theirs)
+ v->s = "";
+ else if (!num_ours) {
+ sprintf(buf, "[behind %d]", num_theirs);
+ v->s = xstrdup(buf);
+ } else if (!num_theirs) {
+ sprintf(buf, "[ahead %d]", num_ours);
+ v->s = xstrdup(buf);
+ } else {
+ sprintf(buf, "[ahead %d, behind %d]",
+ num_ours, num_theirs);
+ v->s = xstrdup(buf);
+ }
+ continue;
+ } else if (!strcmp(formatp, "trackshort") &&
+ starts_with(name, "upstream")) {
+ assert(branch);
+ stat_tracking_info(branch, &num_ours, &num_theirs);
+ if (!num_ours && !num_theirs)
+ v->s = "=";
+ else if (!num_ours)
+ v->s = "<";
+ else if (!num_theirs)
+ v->s = ">";
+ else
+ v->s = "<>";
+ continue;
+ } else
die("unknown %.*s format %s",
(int)(formatp - name), name, formatp);
}
@@ -805,7 +864,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
refname[plen] == '/' ||
p[plen-1] == '/'))
break;
- if (!fnmatch(p, refname, FNM_PATHNAME))
+ if (!wildmatch(p, refname, WM_PATHNAME, NULL))
break;
}
if (!*pattern)
@@ -875,11 +934,9 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs
qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
}
-static void print_value(struct refinfo *ref, int atom, int quote_style)
+static void print_value(struct atom_value *v, int quote_style)
{
- struct atom_value *v;
struct strbuf sb = STRBUF_INIT;
- get_value(ref, atom, &v);
switch (quote_style) {
case QUOTE_NONE:
fputs(v->s, stdout);
@@ -946,15 +1003,26 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style)
const char *cp, *sp, *ep;
for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
+ struct atom_value *atomv;
+
ep = strchr(sp, ')');
if (cp < sp)
emit(cp, sp);
- print_value(info, parse_atom(sp + 2, ep), quote_style);
+ get_value(info, parse_atom(sp + 2, ep), &atomv);
+ print_value(atomv, quote_style);
}
if (*cp) {
sp = cp + strlen(cp);
emit(cp, sp);
}
+ if (need_color_reset_at_eol) {
+ struct atom_value resetv;
+ char color[COLOR_MAXLEN] = "";
+
+ color_parse("reset", "--format", color);
+ resetv.s = color;
+ print_value(&resetv, quote_style);
+ }
putchar('\n');
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 97ce678..fc150c8 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -144,7 +144,7 @@ static int traverse_reachable(void)
unsigned int nr = 0;
int result = 0;
if (show_progress)
- progress = start_progress_delay("Checking connectivity", 0, 0, 2);
+ progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
while (pending.nr) {
struct object_array_entry *entry;
struct object *obj;
@@ -442,7 +442,7 @@ static void fsck_dir(int i, char *path)
add_sha1_list(sha1, DIRENT_SORT_HINT(de));
continue;
}
- if (!prefixcmp(de->d_name, "tmp_obj_"))
+ if (starts_with(de->d_name, "tmp_obj_"))
continue;
fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
}
@@ -484,7 +484,7 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in
static int is_branch(const char *refname)
{
- return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
+ return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
@@ -541,7 +541,7 @@ static void fsck_object_dir(const char *path)
fprintf(stderr, "Checking object directory\n");
if (show_progress)
- progress = start_progress("Checking object directories", 256);
+ progress = start_progress(_("Checking object directories"), 256);
for (i = 0; i < 256; i++) {
static char dir[4096];
sprintf(dir, "%s/%02x", path, i);
@@ -566,7 +566,7 @@ static int fsck_head_link(void)
if (!strcmp(head_points_at, "HEAD"))
/* detached HEAD */
null_is_error = 1;
- else if (prefixcmp(head_points_at, "refs/heads/"))
+ else if (!starts_with(head_points_at, "refs/heads/"))
return error("HEAD points to something strange (%s)",
head_points_at);
if (is_null_sha1(head_sha1)) {
@@ -630,7 +630,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
struct alternate_object_database *alt;
errors_found = 0;
- read_replace_refs = 0;
+ check_replace_refs = 0;
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
@@ -670,7 +670,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
total += p->num_objects;
}
- progress = start_progress("Checking objects", total);
+ progress = start_progress(_("Checking objects"), total);
}
for (p = packed_git; p; p = p->next) {
/* verify gives error messages itself */
diff --git a/builtin/gc.c b/builtin/gc.c
index 25f2237..8d219d8 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -16,6 +16,7 @@
#include "run-command.h"
#include "sigchain.h"
#include "argv-array.h"
+#include "commit.h"
#define FAILED_RUN "failed to run %s"
@@ -25,9 +26,12 @@ static const char * const builtin_gc_usage[] = {
};
static int pack_refs = 1;
+static int prune_reflogs = 1;
+static int aggressive_depth = 250;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
+static int detach_auto = 1;
static const char *prune_expire = "2.weeks.ago";
static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
@@ -64,6 +68,10 @@ static int gc_config(const char *var, const char *value, void *cb)
aggressive_window = git_config_int(var, value);
return 0;
}
+ if (!strcmp(var, "gc.aggressivedepth")) {
+ aggressive_depth = git_config_int(var, value);
+ return 0;
+ }
if (!strcmp(var, "gc.auto")) {
gc_auto_threshold = git_config_int(var, value);
return 0;
@@ -72,6 +80,10 @@ static int gc_config(const char *var, const char *value, void *cb)
gc_auto_pack_limit = git_config_int(var, value);
return 0;
}
+ if (!strcmp(var, "gc.autodetach")) {
+ detach_auto = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "gc.pruneexpire")) {
if (value && strcmp(value, "now")) {
unsigned long now = approxidate("now");
@@ -178,7 +190,7 @@ static int need_to_gc(void)
else if (!too_many_loose_objects())
return 0;
- if (run_hook(NULL, "pre-auto-gc", NULL))
+ if (run_hook_le(NULL, "pre-auto-gc", NULL))
return 0;
return 1;
}
@@ -187,13 +199,12 @@ static int need_to_gc(void)
static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
{
static struct lock_file lock;
- static char locking_host[128];
char my_host[128];
struct strbuf sb = STRBUF_INIT;
struct stat st;
uintmax_t pid;
FILE *fp;
- int fd, should_exit;
+ int fd;
if (pidfile)
/* already locked */
@@ -205,6 +216,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
LOCK_DIE_ON_ERROR);
if (!force) {
+ static char locking_host[128];
+ int should_exit;
fp = fopen(git_path("gc.pid"), "r");
memset(locking_host, 0, sizeof(locking_host));
should_exit =
@@ -246,6 +259,19 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
return NULL;
}
+static int gc_before_repack(void)
+{
+ if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, pack_refs_cmd.argv[0]);
+
+ if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
+ return error(FAILED_RUN, reflog.argv[0]);
+
+ pack_refs = 0;
+ prune_reflogs = 0;
+ return 0;
+}
+
int cmd_gc(int argc, const char **argv, const char *prefix)
{
int aggressive = 0;
@@ -287,7 +313,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (aggressive) {
argv_array_push(&repack, "-f");
- argv_array_push(&repack, "--depth=250");
+ if (aggressive_depth > 0)
+ argv_array_pushf(&repack, "--depth=%d", aggressive_depth);
if (aggressive_window > 0)
argv_array_pushf(&repack, "--window=%d", aggressive_window);
}
@@ -300,11 +327,22 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
*/
if (!need_to_gc())
return 0;
- if (!quiet)
- fprintf(stderr,
- _("Auto packing the repository for optimum performance. You may also\n"
- "run \"git gc\" manually. See "
- "\"git help gc\" for more information.\n"));
+ if (!quiet) {
+ if (detach_auto)
+ fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
+ else
+ fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
+ fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
+ }
+ if (detach_auto) {
+ if (gc_before_repack())
+ return -1;
+ /*
+ * failure to daemonize is ok, we'll continue
+ * in foreground
+ */
+ daemonize();
+ }
} else
add_repack_all_option();
@@ -316,11 +354,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
name, (uintmax_t)pid);
}
- if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, pack_refs_cmd.argv[0]);
-
- if (run_command_v_opt(reflog.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, reflog.argv[0]);
+ if (gc_before_repack())
+ return -1;
if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
return error(FAILED_RUN, repack.argv[0]);
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
new file mode 100644
index 0000000..aa72596
--- /dev/null
+++ b/builtin/get-tar-commit-id.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2005, 2006 Rene Scharfe
+ */
+#include "cache.h"
+#include "commit.h"
+#include "tar.h"
+#include "builtin.h"
+#include "quote.h"
+
+static const char builtin_get_tar_commit_id_usage[] =
+"git get-tar-commit-id < <tarfile>";
+
+/* ustar header + extended global header content */
+#define RECORDSIZE (512)
+#define HEADERSIZE (2 * RECORDSIZE)
+
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
+{
+ char buffer[HEADERSIZE];
+ struct ustar_header *header = (struct ustar_header *)buffer;
+ char *content = buffer + RECORDSIZE;
+ ssize_t n;
+
+ if (argc != 1)
+ usage(builtin_get_tar_commit_id_usage);
+
+ n = read_in_full(0, buffer, HEADERSIZE);
+ if (n < HEADERSIZE)
+ die("git get-tar-commit-id: read error");
+ if (header->typeflag[0] != 'g')
+ return 1;
+ if (memcmp(content, "52 comment=", 11))
+ return 1;
+
+ n = write_in_full(1, content + 11, 41);
+ if (n < 41)
+ die_errno("git get-tar-commit-id: write error");
+
+ return 0;
+}
diff --git a/builtin/grep.c b/builtin/grep.c
index 63f8603..b8d440d 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -379,7 +379,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
const struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
- if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+ if (!ce_path_match(ce, pathspec, NULL))
continue;
/*
* If CE_VALID is on, we assume worktree file and its cache entry
@@ -524,9 +524,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
fill_directory(&dir, pathspec);
for (i = 0; i < dir.nr; i++) {
- const char *name = dir.entries[i]->name;
- int namelen = strlen(name);
- if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL))
+ if (!dir_path_match(dir.entries[i], pathspec, 0, NULL))
continue;
hit |= grep_file(opt, dir.entries[i]->name);
if (hit && opt->status_only)
@@ -876,6 +874,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (len > 4 && is_dir_sep(pager[len - 5]))
pager += len - 4;
+ if (opt.ignore_case && !strcmp("less", pager))
+ string_list_append(&path_list, "-I");
+
if (!strcmp("less", pager) || !strcmp("vi", pager)) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "+/%s%s",
diff --git a/builtin/help.c b/builtin/help.c
index f1e236b..1fdefeb 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -100,7 +100,7 @@ static int check_emacsclient_version(void)
*/
finish_command(&ec_process);
- if (prefixcmp(buffer.buf, "emacsclient")) {
+ if (!starts_with(buffer.buf, "emacsclient")) {
strbuf_release(&buffer);
return error(_("Failed to parse emacsclient version."));
}
@@ -258,7 +258,7 @@ static int add_man_viewer_info(const char *var, const char *value)
static int git_help_config(const char *var, const char *value, void *cb)
{
- if (!prefixcmp(var, "column."))
+ if (starts_with(var, "column."))
return git_column_config(var, value, "help", &colopts);
if (!strcmp(var, "help.format")) {
if (!value)
@@ -278,7 +278,7 @@ static int git_help_config(const char *var, const char *value, void *cb)
add_man_viewer(value);
return 0;
}
- if (!prefixcmp(var, "man."))
+ if (starts_with(var, "man."))
return add_man_viewer_info(var, value);
return git_default_config(var, value, cb);
@@ -288,6 +288,10 @@ static struct cmdnames main_cmds, other_cmds;
static int is_git_command(const char *s)
{
+ if (is_builtin(s))
+ return 1;
+
+ load_command_list("git-", &main_cmds, &other_cmds);
return is_in_cmdlist(&main_cmds, s) ||
is_in_cmdlist(&other_cmds, s);
}
@@ -306,7 +310,7 @@ static const char *cmd_to_page(const char *git_cmd)
{
if (!git_cmd)
return "git";
- else if (!prefixcmp(git_cmd, "git"))
+ else if (starts_with(git_cmd, "git"))
return git_cmd;
else if (is_git_command(git_cmd))
return prepend("git-", git_cmd);
@@ -449,7 +453,6 @@ int cmd_help(int argc, const char **argv, const char *prefix)
int nongit;
const char *alias;
enum help_format parsed_help_format;
- load_command_list("git-", &main_cmds, &other_cmds);
argc = parse_options(argc, argv, prefix, builtin_help_options,
builtin_help_usage, 0);
@@ -458,6 +461,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
if (show_all) {
git_config(git_help_config, NULL);
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+ load_command_list("git-", &main_cmds, &other_cmds);
list_commands(colopts, &main_cmds, &other_cmds);
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 9e9eb4b..18f57de 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -40,17 +40,13 @@ struct base_data {
int ofs_first, ofs_last;
};
-#if !defined(NO_PTHREADS) && defined(NO_THREAD_SAFE_PREAD)
-/* pread() emulation is not thread-safe. Disable threading. */
-#define NO_PTHREADS
-#endif
-
struct thread_local {
#ifndef NO_PTHREADS
pthread_t thread;
#endif
struct base_data *base_cache;
size_t base_cache_used;
+ int pack_fd;
};
/*
@@ -91,7 +87,8 @@ static off_t consumed_bytes;
static unsigned deepest_delta;
static git_SHA_CTX input_ctx;
static uint32_t input_crc32;
-static int input_fd, output_fd, pack_fd;
+static int input_fd, output_fd;
+static const char *curr_pack;
#ifndef NO_PTHREADS
@@ -134,6 +131,7 @@ static inline void unlock_mutex(pthread_mutex_t *mutex)
*/
static void init_thread(void)
{
+ int i;
init_recursive_mutex(&read_mutex);
pthread_mutex_init(&counter_mutex, NULL);
pthread_mutex_init(&work_mutex, NULL);
@@ -141,11 +139,18 @@ static void init_thread(void)
pthread_mutex_init(&deepest_delta_mutex, NULL);
pthread_key_create(&key, NULL);
thread_data = xcalloc(nr_threads, sizeof(*thread_data));
+ for (i = 0; i < nr_threads; i++) {
+ thread_data[i].pack_fd = open(curr_pack, O_RDONLY);
+ if (thread_data[i].pack_fd == -1)
+ die_errno(_("unable to open %s"), curr_pack);
+ }
+
threads_active = 1;
}
static void cleanup_thread(void)
{
+ int i;
if (!threads_active)
return;
threads_active = 0;
@@ -154,6 +159,8 @@ static void cleanup_thread(void)
pthread_mutex_destroy(&work_mutex);
if (show_stat)
pthread_mutex_destroy(&deepest_delta_mutex);
+ for (i = 0; i < nr_threads; i++)
+ close(thread_data[i].pack_fd);
pthread_key_delete(key);
free(thread_data);
}
@@ -200,8 +207,13 @@ static unsigned check_object(struct object *obj)
if (!(obj->flags & FLAG_CHECKED)) {
unsigned long size;
int type = sha1_object_info(obj->sha1, &size);
- if (type != obj->type || type <= 0)
- die(_("object of unexpected type"));
+ if (type <= 0)
+ die(_("did not receive expected object %s"),
+ sha1_to_hex(obj->sha1));
+ if (type != obj->type)
+ die(_("object %s: expected type %s, found %s"),
+ sha1_to_hex(obj->sha1),
+ typename(obj->type), typename(type));
obj->flags |= FLAG_CHECKED;
return 1;
}
@@ -288,13 +300,13 @@ static const char *open_pack_file(const char *pack_name)
output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
if (output_fd < 0)
die_errno(_("unable to create '%s'"), pack_name);
- pack_fd = output_fd;
+ nothread_data.pack_fd = output_fd;
} else {
input_fd = open(pack_name, O_RDONLY);
if (input_fd < 0)
die_errno(_("cannot open packfile '%s'"), pack_name);
output_fd = -1;
- pack_fd = input_fd;
+ nothread_data.pack_fd = input_fd;
}
git_SHA1_Init(&input_ctx);
return pack_name;
@@ -542,7 +554,7 @@ static void *unpack_data(struct object_entry *obj,
do {
ssize_t n = (len < 64*1024) ? len : 64*1024;
- n = pread(pack_fd, inbuf, n, from);
+ n = xpread(get_thread_data()->pack_fd, inbuf, n, from);
if (n < 0)
die_errno(_("cannot pread pack file"));
if (!n)
@@ -1291,7 +1303,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
if (keep_fd < 0) {
if (errno != EEXIST)
die_errno(_("cannot write keep file '%s'"),
- keep_name);
+ keep_name ? keep_name : name);
} else {
if (keep_msg_len > 0) {
write_or_die(keep_fd, keep_msg, keep_msg_len);
@@ -1299,7 +1311,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
}
if (close(keep_fd) != 0)
die_errno(_("cannot close written keep file '%s'"),
- keep_name);
+ keep_name ? keep_name : name);
report = "keep";
}
}
@@ -1490,7 +1502,7 @@ static void show_pack_info(int stat_only)
int cmd_index_pack(int argc, const char **argv, const char *prefix)
{
int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
- const char *curr_pack, *curr_index;
+ const char *curr_index;
const char *index_name = NULL, *pack_name = NULL;
const char *keep_name = NULL, *keep_msg = NULL;
char *index_name_buf = NULL, *keep_name_buf = NULL;
@@ -1502,7 +1514,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
- read_replace_refs = 0;
+ check_replace_refs = 0;
reset_pack_idx_option(&opts);
git_config(git_index_pack_config, &opts);
@@ -1534,9 +1546,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
stat_only = 1;
} else if (!strcmp(arg, "--keep")) {
keep_msg = "";
- } else if (!prefixcmp(arg, "--keep=")) {
+ } else if (starts_with(arg, "--keep=")) {
keep_msg = arg + 7;
- } else if (!prefixcmp(arg, "--threads=")) {
+ } else if (starts_with(arg, "--threads=")) {
char *end;
nr_threads = strtoul(arg+10, &end, 0);
if (!arg[10] || *end || nr_threads < 0)
@@ -1547,7 +1559,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
"ignoring %s"), arg);
nr_threads = 1;
#endif
- } else if (!prefixcmp(arg, "--pack_header=")) {
+ } else if (starts_with(arg, "--pack_header=")) {
struct pack_header *hdr;
char *c;
@@ -1566,7 +1578,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (index_name || (i+1) >= argc)
usage(index_pack_usage);
index_name = argv[++i];
- } else if (!prefixcmp(arg, "--index-version=")) {
+ } else if (starts_with(arg, "--index-version=")) {
char *c;
opts.version = strtoul(arg + 16, &c, 10);
if (opts.version > 2)
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 78aa387..56f85e2 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -266,7 +266,7 @@ static int create_default_files(const char *template_path)
/* allow template config file to override the default */
if (log_all_ref_updates == -1)
git_config_set("core.logallrefupdates", "true");
- if (prefixcmp(git_dir, work_tree) ||
+ if (!starts_with(git_dir, work_tree) ||
strcmp(git_dir + strlen(work_tree), "/.git")) {
git_config_set("core.worktree", work_tree);
}
@@ -412,11 +412,9 @@ int init_db(const char *template_dir, unsigned int flags)
if (!(flags & INIT_DB_QUIET)) {
int len = strlen(git_dir);
- /*
- * TRANSLATORS: The first '%s' is either "Reinitialized
- * existing" or "Initialized empty", the second " shared" or
- * "", and the last '%s%s' is the verbatim directory name.
- */
+ /* TRANSLATORS: The first '%s' is either "Reinitialized
+ existing" or "Initialized empty", the second " shared" or
+ "", and the last '%s%s' is the verbatim directory name. */
printf(_("%s%s Git repository in %s%s\n"),
reinit ? _("Reinitialized existing") : _("Initialized empty"),
shared_repository ? _(" shared") : "",
@@ -515,13 +513,14 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
saved = shared_repository;
shared_repository = 0;
switch (safe_create_leading_directories_const(argv[0])) {
- case -3:
+ case SCLD_OK:
+ case SCLD_PERMS:
+ break;
+ case SCLD_EXISTS:
errno = EEXIST;
/* fallthru */
- case -1:
- die_errno(_("cannot mkdir %s"), argv[0]);
- break;
default:
+ die_errno(_("cannot mkdir %s"), argv[0]);
break;
}
shared_repository = saved;
diff --git a/builtin/log.c b/builtin/log.c
index b708517..3b6a6bb 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -158,13 +158,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (rev->show_notes)
init_display_notes(&rev->notes_opt);
- if (rev->diffopt.pickaxe || rev->diffopt.filter)
+ if (rev->diffopt.pickaxe || rev->diffopt.filter ||
+ DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
rev->always_show_header = 0;
- if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
- rev->always_show_header = 0;
- if (rev->diffopt.pathspec.nr != 1)
- usage("git logs can only follow renames on one pathname at a time");
- }
if (source)
rev->show_source = 1;
@@ -391,7 +387,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
default_show_root = git_config_bool(var, value);
return 0;
}
- if (!prefixcmp(var, "color.decorate."))
+ if (starts_with(var, "color.decorate."))
return parse_decorate_color_config(var, 15, value);
if (!strcmp(var, "log.mailmap")) {
use_mailmap_config = git_config_bool(var, value);
@@ -477,7 +473,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
int new_offset = offset + 1;
while (new_offset < size && buf[new_offset++] != '\n')
; /* do nothing */
- if (!prefixcmp(buf + offset, "tagger "))
+ if (starts_with(buf + offset, "tagger "))
show_tagger(buf + offset + 7,
new_offset - offset - 7, rev);
offset = new_offset;
@@ -503,7 +499,7 @@ static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *
/* There was no "-m" on the command line */
rev->ignore_merges = 0;
if (!rev->first_parent_only && !rev->combine_merges) {
- /* No "--first-parent", "-c", nor "--cc" */
+ /* No "--first-parent", "-c", or "--cc" */
rev->combine_merges = 1;
rev->dense_combined_merges = 1;
}
@@ -882,7 +878,7 @@ static char *find_branch_name(struct rev_info *rev)
ref = rev->cmdline.rev[positive].name;
tip_sha1 = rev->cmdline.rev[positive].item->sha1;
if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
- !prefixcmp(full_ref, "refs/heads/") &&
+ starts_with(full_ref, "refs/heads/") &&
!hashcmp(tip_sha1, branch_sha1))
branch = xstrdup(full_ref + strlen("refs/heads/"));
free(full_ref);
@@ -1388,7 +1384,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
unsigned char sha1[20];
const char *ref;
ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
- if (ref && !prefixcmp(ref, "refs/heads/"))
+ if (ref && starts_with(ref, "refs/heads/"))
branch_name = xstrdup(ref + strlen("refs/heads/"));
else
branch_name = xstrdup(""); /* no branch */
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index e1cf6d8..47c3880 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -64,7 +64,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
- if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched))
+ if (!dir_path_match(ent, &pathspec, len, ps_matched))
return;
fputs(tag, stdout);
@@ -139,7 +139,9 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
- if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched))
+ if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
+ len, ps_matched,
+ S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
return;
if (tag && *tag && show_valid_bit &&
@@ -195,7 +197,8 @@ static void show_ru_info(void)
len = strlen(path);
if (len < max_prefix_len)
continue; /* outside of the prefix */
- if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched))
+ if (!match_pathspec(&pathspec, path, len,
+ max_prefix_len, ps_matched, 0))
continue; /* uninterested */
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 25e83cf..3e9eefb 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -22,7 +22,7 @@ static int tail_match(const char **pattern, const char *path)
if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf))
return error("insanely long ref %.*s...", 20, path);
while ((p = *(pattern++)) != NULL) {
- if (!fnmatch(p, pathbuf, 0))
+ if (!wildmatch(p, pathbuf, 0, NULL))
return 1;
}
return 0;
@@ -50,11 +50,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
const char *arg = argv[i];
if (*arg == '-') {
- if (!prefixcmp(arg, "--upload-pack=")) {
+ if (starts_with(arg, "--upload-pack=")) {
uploadpack = arg + 14;
continue;
}
- if (!prefixcmp(arg, "--exec=")) {
+ if (starts_with(arg, "--exec=")) {
uploadpack = arg + 7;
continue;
}
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 65ec931..51184df 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -171,7 +171,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
* show_recursive() rolls its own matching code and is
* generally ignorant of 'struct pathspec'. The magic mask
* cannot be lifted until it is converted to use
- * match_pathspec_depth() or tree_entry_interesting()
+ * match_pathspec() or tree_entry_interesting()
*/
parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE,
PATHSPEC_PREFER_CWD,
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 24a772d..cf11c8d 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -328,13 +328,13 @@ static int check_header(const struct strbuf *line,
}
/* for inbody stuff */
- if (!prefixcmp(line->buf, ">From") && isspace(line->buf[5])) {
+ if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
ret = 1; /* Should this return 0? */
goto check_header_out;
}
- if (!prefixcmp(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+ if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
for (i = 0; header[i]; i++) {
- if (!memcmp("Subject", header[i], 7)) {
+ if (!strcmp("Subject", header[i])) {
handle_header(&hdr_data[i], line);
ret = 1;
goto check_header_out;
@@ -361,7 +361,7 @@ static int is_rfc2822_header(const struct strbuf *line)
char *cp = line->buf;
/* Count mbox From headers as headers */
- if (!prefixcmp(cp, "From ") || !prefixcmp(cp, ">From "))
+ if (starts_with(cp, "From ") || starts_with(cp, ">From "))
return 1;
while ((ch = *cp++)) {
@@ -671,11 +671,11 @@ static inline int patchbreak(const struct strbuf *line)
size_t i;
/* Beginning of a "diff -" header? */
- if (!prefixcmp(line->buf, "diff -"))
+ if (starts_with(line->buf, "diff -"))
return 1;
/* CVS "Index: " line? */
- if (!prefixcmp(line->buf, "Index: "))
+ if (starts_with(line->buf, "Index: "))
return 1;
/*
@@ -685,7 +685,7 @@ static inline int patchbreak(const struct strbuf *line)
if (line->len < 4)
return 0;
- if (!prefixcmp(line->buf, "---")) {
+ if (starts_with(line->buf, "---")) {
/* space followed by a filename? */
if (line->buf[3] == ' ' && !isspace(line->buf[4]))
return 1;
@@ -929,13 +929,13 @@ static void handle_info(void)
else
continue;
- if (!memcmp(header[i], "Subject", 7)) {
+ if (!strcmp(header[i], "Subject")) {
if (!keep_subject) {
cleanup_subject(hdr);
cleanup_space(hdr);
}
output_header_lines(fout, "Subject", hdr);
- } else if (!memcmp(header[i], "From", 4)) {
+ } else if (!strcmp(header[i], "From")) {
cleanup_space(hdr);
handle_from(hdr);
fprintf(fout, "Author: %s\n", name.buf);
@@ -986,7 +986,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
static int git_mailinfo_config(const char *var, const char *value, void *unused)
{
- if (prefixcmp(var, "mailinfo."))
+ if (!starts_with(var, "mailinfo."))
return git_default_config(var, value, unused);
if (!strcmp(var, "mailinfo.scissors")) {
use_scissors = git_config_bool(var, value);
@@ -1020,7 +1020,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
metainfo_charset = def_charset;
else if (!strcmp(argv[1], "-n"))
metainfo_charset = NULL;
- else if (!prefixcmp(argv[1], "--encoding="))
+ else if (starts_with(argv[1], "--encoding="))
metainfo_charset = argv[1] + 11;
else if (!strcmp(argv[1], "--scissors"))
use_scissors = 1;
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 57ac472..0ecde8d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -1,6 +1,9 @@
#include "builtin.h"
#include "cache.h"
#include "commit.h"
+#include "refs.h"
+#include "diff.h"
+#include "revision.h"
#include "parse-options.h"
static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
@@ -27,6 +30,7 @@ static const char * const merge_base_usage[] = {
N_("git merge-base [-a|--all] --octopus <commit>..."),
N_("git merge-base --independent <commit>..."),
N_("git merge-base --is-ancestor <commit> <commit>"),
+ N_("git merge-base --fork-point <ref> [<commit>]"),
NULL
};
@@ -102,40 +106,152 @@ static int handle_is_ancestor(int argc, const char **argv)
return 1;
}
+struct rev_collect {
+ struct commit **commit;
+ int nr;
+ int alloc;
+ unsigned int initial : 1;
+};
+
+static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
+{
+ struct commit *commit;
+
+ if (is_null_sha1(sha1))
+ return;
+
+ commit = lookup_commit(sha1);
+ if (!commit ||
+ (commit->object.flags & TMP_MARK) ||
+ parse_commit(commit))
+ return;
+
+ ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
+ revs->commit[revs->nr++] = commit;
+ commit->object.flags |= TMP_MARK;
+}
+
+static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+ const char *ident, unsigned long timestamp,
+ int tz, const char *message, void *cbdata)
+{
+ struct rev_collect *revs = cbdata;
+
+ if (revs->initial) {
+ revs->initial = 0;
+ add_one_commit(osha1, revs);
+ }
+ add_one_commit(nsha1, revs);
+ return 0;
+}
+
+static int handle_fork_point(int argc, const char **argv)
+{
+ unsigned char sha1[20];
+ char *refname;
+ const char *commitname;
+ struct rev_collect revs;
+ struct commit *derived;
+ struct commit_list *bases;
+ int i, ret = 0;
+
+ switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) {
+ case 0:
+ die("No such ref: '%s'", argv[0]);
+ case 1:
+ break; /* good */
+ default:
+ die("Ambiguous refname: '%s'", argv[0]);
+ }
+
+ commitname = (argc == 2) ? argv[1] : "HEAD";
+ if (get_sha1(commitname, sha1))
+ die("Not a valid object name: '%s'", commitname);
+
+ derived = lookup_commit_reference(sha1);
+ memset(&revs, 0, sizeof(revs));
+ revs.initial = 1;
+ for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
+
+ for (i = 0; i < revs.nr; i++)
+ revs.commit[i]->object.flags &= ~TMP_MARK;
+
+ bases = get_merge_bases_many(derived, revs.nr, revs.commit, 0);
+
+ /*
+ * There should be one and only one merge base, when we found
+ * a common ancestor among reflog entries.
+ */
+ if (!bases || bases->next) {
+ ret = 1;
+ goto cleanup_return;
+ }
+
+ /* And the found one must be one of the reflog entries */
+ for (i = 0; i < revs.nr; i++)
+ if (&bases->item->object == &revs.commit[i]->object)
+ break; /* found */
+ if (revs.nr <= i) {
+ ret = 1; /* not found */
+ goto cleanup_return;
+ }
+
+ printf("%s\n", sha1_to_hex(bases->item->object.sha1));
+
+cleanup_return:
+ free_commit_list(bases);
+ return ret;
+}
+
int cmd_merge_base(int argc, const char **argv, const char *prefix)
{
struct commit **rev;
int rev_nr = 0;
int show_all = 0;
- int octopus = 0;
- int reduce = 0;
- int is_ancestor = 0;
+ int cmdmode = 0;
struct option options[] = {
OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")),
- OPT_BOOL(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")),
- OPT_BOOL(0, "independent", &reduce, N_("list revs not reachable from others")),
- OPT_BOOL(0, "is-ancestor", &is_ancestor,
- N_("is the first one ancestor of the other?")),
+ OPT_CMDMODE(0, "octopus", &cmdmode,
+ N_("find ancestors for a single n-way merge"), 'o'),
+ OPT_CMDMODE(0, "independent", &cmdmode,
+ N_("list revs not reachable from others"), 'r'),
+ OPT_CMDMODE(0, "is-ancestor", &cmdmode,
+ N_("is the first one ancestor of the other?"), 'a'),
+ OPT_CMDMODE(0, "fork-point", &cmdmode,
+ N_("find where <commit> forked from reflog of <ref>"), 'f'),
OPT_END()
};
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
- if (!octopus && !reduce && argc < 2)
- usage_with_options(merge_base_usage, options);
- if (is_ancestor && (show_all || octopus || reduce))
- die("--is-ancestor cannot be used with other options");
- if (is_ancestor)
+
+ if (cmdmode == 'a') {
+ if (argc < 2)
+ usage_with_options(merge_base_usage, options);
+ if (show_all)
+ die("--is-ancestor cannot be used with --all");
return handle_is_ancestor(argc, argv);
- if (reduce && (show_all || octopus))
- die("--independent cannot be used with other options");
+ }
- if (octopus)
+ if (cmdmode == 'r' && show_all)
+ die("--independent cannot be used with --all");
+
+ if (cmdmode == 'o')
return handle_octopus(argc, argv, show_all);
- else if (reduce)
+
+ if (cmdmode == 'r')
return handle_independent(argc, argv);
+ if (cmdmode == 'f') {
+ if (argc < 1 || 2 < argc)
+ usage_with_options(merge_base_usage, options);
+ return handle_fork_point(argc, argv);
+ }
+
+ if (argc < 2)
+ usage_with_options(merge_base_usage, options);
+
rev = xmalloc(argc * sizeof(*rev));
while (argc-- > 0)
rev[rev_nr++] = get_commit_reference(*argv++);
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index 3a64f5d..a90f28f 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -29,7 +29,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
struct commit *result;
init_merge_options(&o);
- if (argv[0] && !suffixcmp(argv[0], "-subtree"))
+ if (argv[0] && ends_with(argv[0], "-subtree"))
o.subtree_shift = "";
if (argc < 4)
@@ -38,7 +38,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; ++i) {
const char *arg = argv[i];
- if (!prefixcmp(arg, "--")) {
+ if (starts_with(arg, "--")) {
if (!arg[2])
break;
if (parse_merge_opt(&o, arg + 2))
diff --git a/builtin/merge.c b/builtin/merge.c
index 41fb66d..66d8843 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -220,7 +220,7 @@ static struct option builtin_merge_options[] = {
OPT_BOOL(0, "abort", &abort_current_merge,
N_("abort the current in-progress merge")),
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
- { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
+ { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
OPT_END()
@@ -367,7 +367,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
sha1_to_hex(commit->object.sha1));
pretty_print_commit(&ctx, commit, &out);
}
- if (write(fd, out.buf, out.len) < 0)
+ if (write_in_full(fd, out.buf, out.len) != out.len)
die_errno(_("Writing SQUASH_MSG"));
if (close(fd))
die_errno(_("Finishing SQUASH_MSG"));
@@ -421,7 +421,7 @@ static void finish(struct commit *head_commit,
}
/* Run a post-merge hook */
- run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
+ run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
strbuf_release(&reflog_message);
}
@@ -446,17 +446,17 @@ static void merge_name(const char *remote, struct strbuf *msg)
die(_("'%s' does not point to a commit"), remote);
if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
- if (!prefixcmp(found_ref, "refs/heads/")) {
+ if (starts_with(found_ref, "refs/heads/")) {
strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
sha1_to_hex(branch_head), remote);
goto cleanup;
}
- if (!prefixcmp(found_ref, "refs/tags/")) {
+ if (starts_with(found_ref, "refs/tags/")) {
strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
sha1_to_hex(branch_head), remote);
goto cleanup;
}
- if (!prefixcmp(found_ref, "refs/remotes/")) {
+ if (starts_with(found_ref, "refs/remotes/")) {
strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
sha1_to_hex(branch_head), remote);
goto cleanup;
@@ -570,8 +570,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
{
int status;
- if (branch && !prefixcmp(k, "branch.") &&
- !prefixcmp(k + 7, branch) &&
+ if (branch && starts_with(k, "branch.") &&
+ starts_with(k + 7, branch) &&
!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
free(branch_mergeoptions);
branch_mergeoptions = xstrdup(v);
@@ -597,6 +597,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
} else if (!strcmp(k, "merge.defaulttoupstream")) {
default_to_upstream = git_config_bool(k, v);
return 0;
+ } else if (!strcmp(k, "commit.gpgsign")) {
+ sign_commit = git_config_bool(k, v) ? "" : NULL;
+ return 0;
}
status = fmt_merge_msg_config(k, v, cb);
@@ -821,8 +824,8 @@ static void prepare_to_commit(struct commit_list *remoteheads)
if (0 < option_edit)
strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
write_merge_msg(&msg);
- if (run_hook(get_index_file(), "prepare-commit-msg",
- git_path("MERGE_MSG"), "merge", NULL, NULL))
+ if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
+ git_path("MERGE_MSG"), "merge", NULL))
abort_commit(remoteheads, NULL);
if (0 < option_edit) {
if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
@@ -1106,7 +1109,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* current branch.
*/
branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
- if (branch && !prefixcmp(branch, "refs/heads/"))
+ if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
head_commit = NULL;
diff --git a/builtin/mktree.c b/builtin/mktree.c
index f92ba40..a964d6b 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -23,10 +23,7 @@ static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
if (strchr(path, '/'))
die("path %s contains slash", path);
- if (alloc <= used) {
- alloc = alloc_nr(used);
- entries = xrealloc(entries, sizeof(*entries) * alloc);
- }
+ ALLOC_GROW(entries, used + 1, alloc);
ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
ent->mode = mode;
ent->len = len;
diff --git a/builtin/mv.c b/builtin/mv.c
index 21c46d1..180ef99 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -162,7 +162,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
if (strncmp(path, src_w_slash, len_w_slash))
break;
}
- free((char *)src_w_slash);
+ if (src_w_slash != src)
+ free((char *)src_w_slash);
if (last - first < 1)
bad = _("source directory is empty");
@@ -179,6 +180,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
modes = xrealloc(modes,
(argc + last - first)
* sizeof(enum update_mode));
+ submodule_gitfile = xrealloc(submodule_gitfile,
+ (argc + last - first)
+ * sizeof(char *));
}
dst = add_slash(dst);
@@ -192,13 +196,15 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
prefix_path(dst, dst_len,
path + length + 1);
modes[argc + j] = INDEX;
+ submodule_gitfile[argc + j] = NULL;
}
argc += last - first;
}
}
} else if (cache_name_pos(src, length) < 0)
bad = _("not under version control");
- else if (lstat(dst, &st) == 0) {
+ else if (lstat(dst, &st) == 0 &&
+ (!ignore_case || strcasecmp(src, dst))) {
bad = _("destination exists");
if (force) {
/*
@@ -227,6 +233,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
memmove(destination + i,
destination + i + 1,
(argc - i) * sizeof(char *));
+ memmove(modes + i, modes + i + 1,
+ (argc - i) * sizeof(enum update_mode));
+ memmove(submodule_gitfile + i,
+ submodule_gitfile + i + 1,
+ (argc - i) * sizeof(char *));
i--;
}
} else
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 20fcf8c..c824d4e 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -27,8 +27,7 @@ static void name_rev(struct commit *commit,
struct commit_list *parents;
int parent_number = 1;
- if (!commit->object.parsed)
- parse_commit(commit);
+ parse_commit(commit);
if (commit->date < cutoff)
return;
@@ -88,7 +87,7 @@ static int subpath_matches(const char *path, const char *filter)
const char *subpath = path;
while (subpath) {
- if (!fnmatch(filter, subpath, 0))
+ if (!wildmatch(filter, subpath, 0, NULL))
return subpath - path;
subpath = strchr(subpath, '/');
if (subpath)
@@ -101,9 +100,9 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
{
if (shorten_unambiguous)
refname = shorten_unambiguous_ref(refname, 0);
- else if (!prefixcmp(refname, "refs/heads/"))
+ else if (starts_with(refname, "refs/heads/"))
refname = refname + 11;
- else if (!prefixcmp(refname, "refs/"))
+ else if (starts_with(refname, "refs/"))
refname = refname + 5;
return refname;
}
@@ -149,7 +148,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
- if (data->tags_only && prefixcmp(path, "refs/tags/"))
+ if (data->tags_only && !starts_with(path, "refs/tags/"))
return 0;
if (data->ref_filter) {
diff --git a/builtin/notes.c b/builtin/notes.c
index d459e23..39c8573 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -269,7 +269,11 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
die(_("Failed to resolve '%s' as a valid ref."), arg);
if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
free(buf);
- die(_("Failed to read object '%s'."), arg);;
+ die(_("Failed to read object '%s'."), arg);
+ }
+ if (type != OBJ_BLOB) {
+ free(buf);
+ die(_("Cannot read note data from non-blob object '%s'."), arg);
}
strbuf_add(&(msg->buf), buf, len);
free(buf);
@@ -347,7 +351,7 @@ static struct notes_tree *init_notes_check(const char *subcommand)
init_notes(NULL, NULL, NULL, 0);
t = &default_notes_tree;
- if (prefixcmp(t->ref, "refs/notes/"))
+ if (!starts_with(t->ref, "refs/notes/"))
die("Refusing to %s notes in %s (outside of refs/notes/)",
subcommand, t->ref);
return t;
@@ -935,7 +939,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
int result;
const char *override_notes_ref = NULL;
struct option options[] = {
- OPT_STRING(0, "ref", &override_notes_ref, N_("notes_ref"),
+ OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"),
N_("use notes from <notes_ref>")),
OPT_END()
};
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 36273dd..de36c60 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -14,10 +14,12 @@
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
+#include "pack-objects.h"
#include "progress.h"
#include "refs.h"
#include "streaming.h"
#include "thread-utils.h"
+#include "pack-bitmap.h"
static const char *pack_usage[] = {
N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@ -25,42 +27,15 @@ static const char *pack_usage[] = {
NULL
};
-struct object_entry {
- struct pack_idx_entry idx;
- unsigned long size; /* uncompressed size */
- struct packed_git *in_pack; /* already in pack */
- off_t in_pack_offset;
- struct object_entry *delta; /* delta base object */
- struct object_entry *delta_child; /* deltified objects who bases me */
- struct object_entry *delta_sibling; /* other deltified objects who
- * uses the same base as me
- */
- void *delta_data; /* cached delta (uncompressed) */
- unsigned long delta_size; /* delta data size (uncompressed) */
- unsigned long z_delta_size; /* delta data size (compressed) */
- enum object_type type;
- enum object_type in_pack_type; /* could be delta */
- uint32_t hash; /* name hint hash */
- unsigned char in_pack_header_size;
- unsigned preferred_base:1; /*
- * we do not pack this, but is available
- * to be used as the base object to delta
- * objects against.
- */
- unsigned no_try_delta:1;
- unsigned tagged:1; /* near the very tip of refs */
- unsigned filled:1; /* assigned write-order */
-};
-
/*
- * Objects we are going to pack are collected in objects array (dynamically
- * expanded). nr_objects & nr_alloc controls this array. They are stored
- * in the order we see -- typically rev-list --objects order that gives us
- * nice "minimum seek" order.
+ * Objects we are going to pack are collected in the `to_pack` structure.
+ * It contains an array (dynamically expanded) of the object data, and a map
+ * that can resolve SHA1s to their position in the array.
*/
-static struct object_entry *objects;
+static struct packing_data to_pack;
+
static struct pack_idx_entry **written_list;
-static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
+static uint32_t nr_result, nr_written;
static int non_empty;
static int reuse_delta = 1, reuse_object = 1;
@@ -83,6 +58,14 @@ static struct progress *progress_state;
static int pack_compression_level = Z_DEFAULT_COMPRESSION;
static int pack_compression_seen;
+static struct packed_git *reuse_packfile;
+static uint32_t reuse_packfile_objects;
+static off_t reuse_packfile_offset;
+
+static int use_bitmap_index = 1;
+static int write_bitmap_index;
+static uint16_t write_bitmap_options;
+
static unsigned long delta_cache_size = 0;
static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
static unsigned long cache_max_small_delta_size = 1000;
@@ -90,20 +73,28 @@ static unsigned long cache_max_small_delta_size = 1000;
static unsigned long window_memory_limit = 0;
/*
- * The object names in objects array are hashed with this hashtable,
- * to help looking up the entry by object name.
- * This hashtable is built after all the objects are seen.
- */
-static int *object_ix;
-static int object_ix_hashsz;
-static struct object_entry *locate_object_entry(const unsigned char *sha1);
-
-/*
* stats
*/
static uint32_t written, written_delta;
static uint32_t reused, reused_delta;
+/*
+ * Indexed commits
+ */
+static struct commit **indexed_commits;
+static unsigned int indexed_commits_nr;
+static unsigned int indexed_commits_alloc;
+
+static void index_commit_for_bitmap(struct commit *commit)
+{
+ if (indexed_commits_nr >= indexed_commits_alloc) {
+ indexed_commits_alloc = (indexed_commits_alloc + 32) * 2;
+ indexed_commits = xrealloc(indexed_commits,
+ indexed_commits_alloc * sizeof(struct commit *));
+ }
+
+ indexed_commits[indexed_commits_nr++] = commit;
+}
static void *get_delta(struct object_entry *entry)
{
@@ -553,12 +544,12 @@ static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
void *cb_data)
{
unsigned char peeled[20];
- struct object_entry *entry = locate_object_entry(sha1);
+ struct object_entry *entry = packlist_find(&to_pack, sha1, NULL);
if (entry)
entry->tagged = 1;
if (!peel_ref(path, peeled)) {
- entry = locate_object_entry(peeled);
+ entry = packlist_find(&to_pack, peeled, NULL);
if (entry)
entry->tagged = 1;
}
@@ -633,9 +624,10 @@ static struct object_entry **compute_write_order(void)
{
unsigned int i, wo_end, last_untagged;
- struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));
+ struct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo));
+ struct object_entry *objects = to_pack.objects;
- for (i = 0; i < nr_objects; i++) {
+ for (i = 0; i < to_pack.nr_objects; i++) {
objects[i].tagged = 0;
objects[i].filled = 0;
objects[i].delta_child = NULL;
@@ -647,7 +639,7 @@ static struct object_entry **compute_write_order(void)
* Make sure delta_sibling is sorted in the original
* recency order.
*/
- for (i = nr_objects; i > 0;) {
+ for (i = to_pack.nr_objects; i > 0;) {
struct object_entry *e = &objects[--i];
if (!e->delta)
continue;
@@ -665,7 +657,7 @@ static struct object_entry **compute_write_order(void)
* Give the objects in the original recency order until
* we see a tagged tip.
*/
- for (i = wo_end = 0; i < nr_objects; i++) {
+ for (i = wo_end = 0; i < to_pack.nr_objects; i++) {
if (objects[i].tagged)
break;
add_to_write_order(wo, &wo_end, &objects[i]);
@@ -675,7 +667,7 @@ static struct object_entry **compute_write_order(void)
/*
* Then fill all the tagged tips.
*/
- for (; i < nr_objects; i++) {
+ for (; i < to_pack.nr_objects; i++) {
if (objects[i].tagged)
add_to_write_order(wo, &wo_end, &objects[i]);
}
@@ -683,7 +675,7 @@ static struct object_entry **compute_write_order(void)
/*
* And then all remaining commits and tags.
*/
- for (i = last_untagged; i < nr_objects; i++) {
+ for (i = last_untagged; i < to_pack.nr_objects; i++) {
if (objects[i].type != OBJ_COMMIT &&
objects[i].type != OBJ_TAG)
continue;
@@ -693,7 +685,7 @@ static struct object_entry **compute_write_order(void)
/*
* And then all the trees.
*/
- for (i = last_untagged; i < nr_objects; i++) {
+ for (i = last_untagged; i < to_pack.nr_objects; i++) {
if (objects[i].type != OBJ_TREE)
continue;
add_to_write_order(wo, &wo_end, &objects[i]);
@@ -702,17 +694,70 @@ static struct object_entry **compute_write_order(void)
/*
* Finally all the rest in really tight order
*/
- for (i = last_untagged; i < nr_objects; i++) {
+ for (i = last_untagged; i < to_pack.nr_objects; i++) {
if (!objects[i].filled)
add_family_to_write_order(wo, &wo_end, &objects[i]);
}
- if (wo_end != nr_objects)
- die("ordered %u objects, expected %"PRIu32, wo_end, nr_objects);
+ if (wo_end != to_pack.nr_objects)
+ die("ordered %u objects, expected %"PRIu32, wo_end, to_pack.nr_objects);
return wo;
}
+static off_t write_reused_pack(struct sha1file *f)
+{
+ unsigned char buffer[8192];
+ off_t to_write, total;
+ int fd;
+
+ if (!is_pack_valid(reuse_packfile))
+ die("packfile is invalid: %s", reuse_packfile->pack_name);
+
+ fd = git_open_noatime(reuse_packfile->pack_name);
+ if (fd < 0)
+ die_errno("unable to open packfile for reuse: %s",
+ reuse_packfile->pack_name);
+
+ if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1)
+ die_errno("unable to seek in reused packfile");
+
+ if (reuse_packfile_offset < 0)
+ reuse_packfile_offset = reuse_packfile->pack_size - 20;
+
+ total = to_write = reuse_packfile_offset - sizeof(struct pack_header);
+
+ while (to_write) {
+ int read_pack = xread(fd, buffer, sizeof(buffer));
+
+ if (read_pack <= 0)
+ die_errno("unable to read from reused packfile");
+
+ if (read_pack > to_write)
+ read_pack = to_write;
+
+ sha1write(f, buffer, read_pack);
+ to_write -= read_pack;
+
+ /*
+ * We don't know the actual number of objects written,
+ * only how many bytes written, how many bytes total, and
+ * how many objects total. So we can fake it by pretending all
+ * objects we are writing are the same size. This gives us a
+ * smooth progress meter, and at the end it matches the true
+ * answer.
+ */
+ written = reuse_packfile_objects *
+ (((double)(total - to_write)) / total);
+ display_progress(progress_state, written);
+ }
+
+ close(fd);
+ written = reuse_packfile_objects;
+ display_progress(progress_state, written);
+ return reuse_packfile_offset - sizeof(struct pack_header);
+}
+
static void write_pack_file(void)
{
uint32_t i = 0, j;
@@ -723,8 +768,8 @@ static void write_pack_file(void)
struct object_entry **write_order;
if (progress > pack_to_stdout)
- progress_state = start_progress("Writing objects", nr_result);
- written_list = xmalloc(nr_objects * sizeof(*written_list));
+ progress_state = start_progress(_("Writing objects"), nr_result);
+ written_list = xmalloc(to_pack.nr_objects * sizeof(*written_list));
write_order = compute_write_order();
do {
@@ -737,10 +782,17 @@ static void write_pack_file(void)
f = create_tmp_packfile(&pack_tmp_name);
offset = write_pack_header(f, nr_remaining);
- if (!offset)
- die_errno("unable to write pack header");
+
+ if (reuse_packfile) {
+ off_t packfile_size;
+ assert(pack_to_stdout);
+
+ packfile_size = write_reused_pack(f);
+ offset += packfile_size;
+ }
+
nr_written = 0;
- for (; i < nr_objects; i++) {
+ for (; i < to_pack.nr_objects; i++) {
struct object_entry *e = write_order[i];
if (write_one(f, e, &offset) == WRITE_ONE_BREAK)
break;
@@ -764,7 +816,7 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
struct stat st;
- char tmpname[PATH_MAX];
+ struct strbuf tmpname = STRBUF_INIT;
/*
* Packs are runtime accessed in their mtime
@@ -784,16 +836,35 @@ static void write_pack_file(void)
utb.modtime = --last_mtime;
if (utime(pack_tmp_name, &utb) < 0)
warning("failed utime() on %s: %s",
- tmpname, strerror(errno));
+ pack_tmp_name, strerror(errno));
}
- /* Enough space for "-<sha-1>.pack"? */
- if (sizeof(tmpname) <= strlen(base_name) + 50)
- die("pack base name '%s' too long", base_name);
- snprintf(tmpname, sizeof(tmpname), "%s-", base_name);
- finish_tmp_packfile(tmpname, pack_tmp_name,
+ strbuf_addf(&tmpname, "%s-", base_name);
+
+ if (write_bitmap_index) {
+ bitmap_writer_set_checksum(sha1);
+ bitmap_writer_build_type_index(written_list, nr_written);
+ }
+
+ finish_tmp_packfile(&tmpname, pack_tmp_name,
written_list, nr_written,
&pack_idx_opts, sha1);
+
+ if (write_bitmap_index) {
+ strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1));
+
+ stop_progress(&progress_state);
+
+ bitmap_writer_show_progress(progress);
+ bitmap_writer_reuse_bitmaps(&to_pack);
+ bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
+ bitmap_writer_build(&to_pack);
+ bitmap_writer_finish(written_list, nr_written,
+ tmpname.buf, write_bitmap_options);
+ write_bitmap_index = 0;
+ }
+
+ strbuf_release(&tmpname);
free(pack_tmp_name);
puts(sha1_to_hex(sha1));
}
@@ -803,7 +874,7 @@ static void write_pack_file(void)
written_list[j]->offset = (off_t)-1;
}
nr_remaining -= nr_written;
- } while (nr_remaining && i < nr_objects);
+ } while (nr_remaining && i < to_pack.nr_objects);
free(written_list);
free(write_order);
@@ -813,73 +884,6 @@ static void write_pack_file(void)
written, nr_result);
}
-static int locate_object_entry_hash(const unsigned char *sha1)
-{
- int i;
- unsigned int ui;
- memcpy(&ui, sha1, sizeof(unsigned int));
- i = ui % object_ix_hashsz;
- while (0 < object_ix[i]) {
- if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))
- return i;
- if (++i == object_ix_hashsz)
- i = 0;
- }
- return -1 - i;
-}
-
-static struct object_entry *locate_object_entry(const unsigned char *sha1)
-{
- int i;
-
- if (!object_ix_hashsz)
- return NULL;
-
- i = locate_object_entry_hash(sha1);
- if (0 <= i)
- return &objects[object_ix[i]-1];
- return NULL;
-}
-
-static void rehash_objects(void)
-{
- uint32_t i;
- struct object_entry *oe;
-
- object_ix_hashsz = nr_objects * 3;
- if (object_ix_hashsz < 1024)
- object_ix_hashsz = 1024;
- object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
- memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
- for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
- int ix = locate_object_entry_hash(oe->idx.sha1);
- if (0 <= ix)
- continue;
- ix = -1 - ix;
- object_ix[ix] = i + 1;
- }
-}
-
-static uint32_t name_hash(const char *name)
-{
- uint32_t c, hash = 0;
-
- if (!name)
- return 0;
-
- /*
- * This effectively just creates a sortable number from the
- * last sixteen non-whitespace characters. Last characters
- * count "most", so things that end in ".c" sort together.
- */
- while ((c = *name++) != 0) {
- if (isspace(c))
- continue;
- hash = (hash >> 2) + (c << 24);
- }
- return hash;
-}
-
static void setup_delta_attr_check(struct git_attr_check *check)
{
static struct git_attr *attr_delta;
@@ -902,42 +906,69 @@ static int no_try_delta(const char *path)
return 0;
}
-static int add_object_entry(const unsigned char *sha1, enum object_type type,
- const char *name, int exclude)
+/*
+ * When adding an object, check whether we have already added it
+ * to our packing list. If so, we can skip. However, if we are
+ * being asked to excludei t, but the previous mention was to include
+ * it, make sure to adjust its flags and tweak our numbers accordingly.
+ *
+ * As an optimization, we pass out the index position where we would have
+ * found the item, since that saves us from having to look it up again a
+ * few lines later when we want to add the new entry.
+ */
+static int have_duplicate_entry(const unsigned char *sha1,
+ int exclude,
+ uint32_t *index_pos)
{
struct object_entry *entry;
- struct packed_git *p, *found_pack = NULL;
- off_t found_offset = 0;
- int ix;
- uint32_t hash = name_hash(name);
-
- ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
- if (ix >= 0) {
- if (exclude) {
- entry = objects + object_ix[ix] - 1;
- if (!entry->preferred_base)
- nr_result--;
- entry->preferred_base = 1;
- }
+
+ entry = packlist_find(&to_pack, sha1, index_pos);
+ if (!entry)
return 0;
+
+ if (exclude) {
+ if (!entry->preferred_base)
+ nr_result--;
+ entry->preferred_base = 1;
}
+ return 1;
+}
+
+/*
+ * Check whether we want the object in the pack (e.g., we do not want
+ * objects found in non-local stores if the "--local" option was used).
+ *
+ * As a side effect of this check, we will find the packed version of this
+ * object, if any. We therefore pass out the pack information to avoid having
+ * to look it up again later.
+ */
+static int want_object_in_pack(const unsigned char *sha1,
+ int exclude,
+ struct packed_git **found_pack,
+ off_t *found_offset)
+{
+ struct packed_git *p;
+
if (!exclude && local && has_loose_object_nonlocal(sha1))
return 0;
+ *found_pack = NULL;
+ *found_offset = 0;
+
for (p = packed_git; p; p = p->next) {
off_t offset = find_pack_entry_one(sha1, p);
if (offset) {
- if (!found_pack) {
+ if (!*found_pack) {
if (!is_pack_valid(p)) {
warning("packfile %s cannot be accessed", p->pack_name);
continue;
}
- found_offset = offset;
- found_pack = p;
+ *found_offset = offset;
+ *found_pack = p;
}
if (exclude)
- break;
+ return 1;
if (incremental)
return 0;
if (local && !p->pack_local)
@@ -947,14 +978,21 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
}
}
- if (nr_objects >= nr_alloc) {
- nr_alloc = (nr_alloc + 1024) * 3 / 2;
- objects = xrealloc(objects, nr_alloc * sizeof(*entry));
- }
+ return 1;
+}
- entry = objects + nr_objects++;
- memset(entry, 0, sizeof(*entry));
- hashcpy(entry->idx.sha1, sha1);
+static void create_object_entry(const unsigned char *sha1,
+ enum object_type type,
+ uint32_t hash,
+ int exclude,
+ int no_try_delta,
+ uint32_t index_pos,
+ struct packed_git *found_pack,
+ off_t found_offset)
+{
+ struct object_entry *entry;
+
+ entry = packlist_alloc(&to_pack, sha1, index_pos);
entry->hash = hash;
if (type)
entry->type = type;
@@ -967,16 +1005,53 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
entry->in_pack_offset = found_offset;
}
- if (object_ix_hashsz * 3 <= nr_objects * 4)
- rehash_objects();
- else
- object_ix[-1 - ix] = nr_objects;
+ entry->no_try_delta = no_try_delta;
+}
+
+static const char no_closure_warning[] = N_(
+"disabling bitmap writing, as some objects are not being packed"
+);
- display_progress(progress_state, nr_objects);
+static int add_object_entry(const unsigned char *sha1, enum object_type type,
+ const char *name, int exclude)
+{
+ struct packed_git *found_pack;
+ off_t found_offset;
+ uint32_t index_pos;
+
+ if (have_duplicate_entry(sha1, exclude, &index_pos))
+ return 0;
+
+ if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) {
+ /* The pack is missing an object, so it will not have closure */
+ if (write_bitmap_index) {
+ warning(_(no_closure_warning));
+ write_bitmap_index = 0;
+ }
+ return 0;
+ }
- if (name && no_try_delta(name))
- entry->no_try_delta = 1;
+ create_object_entry(sha1, type, pack_name_hash(name),
+ exclude, name && no_try_delta(name),
+ index_pos, found_pack, found_offset);
+ display_progress(progress_state, nr_result);
+ return 1;
+}
+
+static int add_object_entry_from_bitmap(const unsigned char *sha1,
+ enum object_type type,
+ int flags, uint32_t name_hash,
+ struct packed_git *pack, off_t offset)
+{
+ uint32_t index_pos;
+
+ if (have_duplicate_entry(sha1, 0, &index_pos))
+ return 0;
+
+ create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
+
+ display_progress(progress_state, nr_result);
return 1;
}
@@ -1001,7 +1076,7 @@ static int pbase_tree_cache_ix_incr(int ix)
static struct pbase_tree {
struct pbase_tree *next;
/* This is a phony "cache" entry; we are not
- * going to evict it nor find it through _get()
+ * going to evict it or find it through _get()
* mechanism -- this is for the toplevel node that
* would almost always change with any commit.
*/
@@ -1158,12 +1233,9 @@ static int check_pbase_path(unsigned hash)
if (0 <= pos)
return 1;
pos = -pos - 1;
- if (done_pbase_paths_alloc <= done_pbase_paths_num) {
- done_pbase_paths_alloc = alloc_nr(done_pbase_paths_alloc);
- done_pbase_paths = xrealloc(done_pbase_paths,
- done_pbase_paths_alloc *
- sizeof(unsigned));
- }
+ ALLOC_GROW(done_pbase_paths,
+ done_pbase_paths_num + 1,
+ done_pbase_paths_alloc);
done_pbase_paths_num++;
if (pos < done_pbase_paths_num)
memmove(done_pbase_paths + pos + 1,
@@ -1177,7 +1249,7 @@ static void add_preferred_base_object(const char *name)
{
struct pbase_tree *it;
int cmplen;
- unsigned hash = name_hash(name);
+ unsigned hash = pack_name_hash(name);
if (!num_preferred_base || check_pbase_path(hash))
return;
@@ -1329,7 +1401,7 @@ static void check_object(struct object_entry *entry)
break;
}
- if (base_ref && (base_entry = locate_object_entry(base_ref))) {
+ if (base_ref && (base_entry = packlist_find(&to_pack, base_ref, NULL))) {
/*
* If base_ref was set above that means we wish to
* reuse delta data, and we even found that base
@@ -1403,12 +1475,12 @@ static void get_object_details(void)
uint32_t i;
struct object_entry **sorted_by_offset;
- sorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *));
- for (i = 0; i < nr_objects; i++)
- sorted_by_offset[i] = objects + i;
- qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
+ sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *));
+ for (i = 0; i < to_pack.nr_objects; i++)
+ sorted_by_offset[i] = to_pack.objects + i;
+ qsort(sorted_by_offset, to_pack.nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
- for (i = 0; i < nr_objects; i++) {
+ for (i = 0; i < to_pack.nr_objects; i++) {
struct object_entry *entry = sorted_by_offset[i];
check_object(entry);
if (big_file_threshold < entry->size)
@@ -2032,9 +2104,9 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo
{
unsigned char peeled[20];
- if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+ if (starts_with(path, "refs/tags/") && /* is a tag? */
!peel_ref(path, peeled) && /* peelable? */
- locate_object_entry(peeled)) /* object packed? */
+ packlist_find(&to_pack, peeled, NULL)) /* object packed? */
add_object_entry(sha1, OBJ_TAG, NULL, 0);
return 0;
}
@@ -2057,14 +2129,14 @@ static void prepare_pack(int window, int depth)
if (!pack_to_stdout)
do_check_packed_object_crc = 1;
- if (!nr_objects || !window || !depth)
+ if (!to_pack.nr_objects || !window || !depth)
return;
- delta_list = xmalloc(nr_objects * sizeof(*delta_list));
+ delta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list));
nr_deltas = n = 0;
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *entry = objects + i;
+ for (i = 0; i < to_pack.nr_objects; i++) {
+ struct object_entry *entry = to_pack.objects + i;
if (entry->delta)
/* This happens if we decided to reuse existing
@@ -2099,7 +2171,7 @@ static void prepare_pack(int window, int depth)
if (nr_deltas && n > 1) {
unsigned nr_done = 0;
if (progress)
- progress_state = start_progress("Compressing objects",
+ progress_state = start_progress(_("Compressing objects"),
nr_deltas);
qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
@@ -2142,6 +2214,20 @@ static int git_pack_config(const char *k, const char *v, void *cb)
cache_max_small_delta_size = git_config_int(k, v);
return 0;
}
+ if (!strcmp(k, "pack.writebitmaps")) {
+ write_bitmap_index = git_config_bool(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "pack.writebitmaphashcache")) {
+ if (git_config_bool(k, v))
+ write_bitmap_options |= BITMAP_OPT_HASH_CACHE;
+ else
+ write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE;
+ }
+ if (!strcmp(k, "pack.usebitmaps")) {
+ use_bitmap_index = git_config_bool(k, v);
+ return 0;
+ }
if (!strcmp(k, "pack.threads")) {
delta_search_threads = git_config_int(k, v);
if (delta_search_threads < 0)
@@ -2200,6 +2286,9 @@ static void show_commit(struct commit *commit, void *data)
{
add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
commit->object.flags |= OBJECT_ADDED;
+
+ if (write_bitmap_index)
+ index_commit_for_bitmap(commit);
}
static void show_object(struct object *obj,
@@ -2342,7 +2431,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
for (i = 0; i < p->num_objects; i++) {
sha1 = nth_packed_object_sha1(p, i);
- if (!locate_object_entry(sha1) &&
+ if (!packlist_find(&to_pack, sha1, NULL) &&
!has_sha1_pack_kept_or_nonlocal(sha1))
if (force_object_loose(sha1, p->mtime))
die("unable to force loose object");
@@ -2350,6 +2439,35 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
}
}
+/*
+ * This tracks any options which a reader of the pack might
+ * not understand, and which would therefore prevent blind reuse
+ * of what we have on disk.
+ */
+static int pack_options_allow_reuse(void)
+{
+ return allow_ofs_delta;
+}
+
+static int get_object_list_from_bitmap(struct rev_info *revs)
+{
+ if (prepare_bitmap_walk(revs) < 0)
+ return -1;
+
+ if (pack_options_allow_reuse() &&
+ !reuse_partial_packfile_from_bitmap(
+ &reuse_packfile,
+ &reuse_packfile_objects,
+ &reuse_packfile_offset)) {
+ assert(reuse_packfile_objects);
+ nr_result += reuse_packfile_objects;
+ display_progress(progress_state, nr_result);
+ }
+
+ traverse_bitmap_commit_list(&add_object_entry_from_bitmap);
+ return 0;
+}
+
static void get_object_list(int ac, const char **av)
{
struct rev_info revs;
@@ -2360,6 +2478,9 @@ static void get_object_list(int ac, const char **av)
save_commit_buffer = 0;
setup_revisions(ac, av, &revs, NULL);
+ /* make sure shallows are read */
+ is_repository_shallow();
+
while (fgets(line, sizeof(line), stdin) != NULL) {
int len = strlen(line);
if (len && line[len - 1] == '\n')
@@ -2369,6 +2490,14 @@ static void get_object_list(int ac, const char **av)
if (*line == '-') {
if (!strcmp(line, "--not")) {
flags ^= UNINTERESTING;
+ write_bitmap_index = 0;
+ continue;
+ }
+ if (starts_with(line, "--shallow ")) {
+ unsigned char sha1[20];
+ if (get_sha1_hex(line + 10, sha1))
+ die("not an SHA-1 '%s'", line + 10);
+ register_shallow(sha1);
continue;
}
die("not a rev '%s'", line);
@@ -2377,6 +2506,9 @@ static void get_object_list(int ac, const char **av)
die("bad revision '%s'", line);
}
+ if (use_bitmap_index && !get_object_list_from_bitmap(&revs))
+ return;
+
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(&revs, show_edge);
@@ -2506,10 +2638,14 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
N_("pack compression level")),
OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
N_("do not hide commits by grafts"), 0),
+ OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index,
+ N_("use a bitmap index if available to speed up counting objects")),
+ OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
+ N_("write a bitmap index together with the pack index")),
OPT_END(),
};
- read_replace_refs = 0;
+ check_replace_refs = 0;
reset_pack_idx_option(&pack_idx_opts);
git_config(git_pack_config, NULL);
@@ -2572,13 +2708,19 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (keep_unreachable && unpack_unreachable)
die("--keep-unreachable and --unpack-unreachable are incompatible.");
+ if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow())
+ use_bitmap_index = 0;
+
+ if (pack_to_stdout || !rev_list_all)
+ write_bitmap_index = 0;
+
if (progress && all_progress_implied)
progress = 2;
prepare_packed_git();
if (progress)
- progress_state = start_progress("Counting objects", 0);
+ progress_state = start_progress(_("Counting objects"), 0);
if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index fa6ce42..6879468 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -10,58 +10,62 @@ static const char * const prune_packed_usage[] = {
static struct progress *progress;
-static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
+static void prune_dir(int i, DIR *dir, struct strbuf *pathname, int opts)
{
struct dirent *de;
char hex[40];
+ int top_len = pathname->len;
sprintf(hex, "%02x", i);
while ((de = readdir(dir)) != NULL) {
unsigned char sha1[20];
if (strlen(de->d_name) != 38)
continue;
- memcpy(hex+2, de->d_name, 38);
+ memcpy(hex + 2, de->d_name, 38);
if (get_sha1_hex(hex, sha1))
continue;
if (!has_sha1_pack(sha1))
continue;
- memcpy(pathname + len, de->d_name, 38);
+
+ strbuf_add(pathname, de->d_name, 38);
if (opts & PRUNE_PACKED_DRY_RUN)
- printf("rm -f %s\n", pathname);
+ printf("rm -f %s\n", pathname->buf);
else
- unlink_or_warn(pathname);
+ unlink_or_warn(pathname->buf);
display_progress(progress, i + 1);
+ strbuf_setlen(pathname, top_len);
}
}
void prune_packed_objects(int opts)
{
int i;
- static char pathname[PATH_MAX];
const char *dir = get_object_directory();
- int len = strlen(dir);
+ struct strbuf pathname = STRBUF_INIT;
+ int top_len;
+ strbuf_addstr(&pathname, dir);
if (opts & PRUNE_PACKED_VERBOSE)
- progress = start_progress_delay("Removing duplicate objects",
+ progress = start_progress_delay(_("Removing duplicate objects"),
256, 95, 2);
- if (len > PATH_MAX - 42)
- die("impossible object directory");
- memcpy(pathname, dir, len);
- if (len && pathname[len-1] != '/')
- pathname[len++] = '/';
+ if (pathname.len && pathname.buf[pathname.len - 1] != '/')
+ strbuf_addch(&pathname, '/');
+
+ top_len = pathname.len;
for (i = 0; i < 256; i++) {
DIR *d;
display_progress(progress, i + 1);
- sprintf(pathname + len, "%02x/", i);
- d = opendir(pathname);
+ strbuf_setlen(&pathname, top_len);
+ strbuf_addf(&pathname, "%02x/", i);
+ d = opendir(pathname.buf);
if (!d)
continue;
- prune_dir(i, d, pathname, len + 3, opts);
+ prune_dir(i, d, &pathname, opts);
closedir(d);
- pathname[len + 2] = '\0';
- rmdir(pathname);
+ strbuf_setlen(&pathname, top_len + 2);
+ rmdir(pathname.buf);
}
stop_progress(&progress);
}
diff --git a/builtin/prune.c b/builtin/prune.c
index 6366917..144a3bd 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -17,9 +17,8 @@ static int verbose;
static unsigned long expire;
static int show_progress = -1;
-static int prune_tmp_object(const char *path, const char *filename)
+static int prune_tmp_file(const char *fullpath)
{
- const char *fullpath = mkpath("%s/%s", path, filename);
struct stat st;
if (lstat(fullpath, &st))
return error("Could not stat '%s'", fullpath);
@@ -32,9 +31,8 @@ static int prune_tmp_object(const char *path, const char *filename)
return 0;
}
-static int prune_object(char *path, const char *filename, const unsigned char *sha1)
+static int prune_object(const char *fullpath, const unsigned char *sha1)
{
- const char *fullpath = mkpath("%s/%s", path, filename);
struct stat st;
if (lstat(fullpath, &st))
return error("Could not stat '%s'", fullpath);
@@ -50,9 +48,10 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
return 0;
}
-static int prune_dir(int i, char *path)
+static int prune_dir(int i, struct strbuf *path)
{
- DIR *dir = opendir(path);
+ size_t baselen = path->len;
+ DIR *dir = opendir(path->buf);
struct dirent *de;
if (!dir)
@@ -77,28 +76,39 @@ static int prune_dir(int i, char *path)
if (lookup_object(sha1))
continue;
- prune_object(path, de->d_name, sha1);
+ strbuf_addf(path, "/%s", de->d_name);
+ prune_object(path->buf, sha1);
+ strbuf_setlen(path, baselen);
continue;
}
- if (!prefixcmp(de->d_name, "tmp_obj_")) {
- prune_tmp_object(path, de->d_name);
+ if (starts_with(de->d_name, "tmp_obj_")) {
+ strbuf_addf(path, "/%s", de->d_name);
+ prune_tmp_file(path->buf);
+ strbuf_setlen(path, baselen);
continue;
}
- fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+ fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name);
}
closedir(dir);
if (!show_only)
- rmdir(path);
+ rmdir(path->buf);
return 0;
}
static void prune_object_dir(const char *path)
{
+ struct strbuf buf = STRBUF_INIT;
+ size_t baselen;
int i;
+
+ strbuf_addstr(&buf, path);
+ strbuf_addch(&buf, '/');
+ baselen = buf.len;
+
for (i = 0; i < 256; i++) {
- static char dir[4096];
- sprintf(dir, "%s/%02x", path, i);
- prune_dir(i, dir);
+ strbuf_addf(&buf, "%02x", i);
+ prune_dir(i, &buf);
+ strbuf_setlen(&buf, baselen);
}
}
@@ -119,8 +129,8 @@ static void remove_temporary_files(const char *path)
return;
}
while ((de = readdir(dir)) != NULL)
- if (!prefixcmp(de->d_name, "tmp_"))
- prune_tmp_object(path, de->d_name);
+ if (starts_with(de->d_name, "tmp_"))
+ prune_tmp_file(mkpath("%s/%s", path, de->d_name));
closedir(dir);
}
@@ -140,7 +150,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
expire = ULONG_MAX;
save_commit_buffer = 0;
- read_replace_refs = 0;
+ check_replace_refs = 0;
init_revisions(&revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
@@ -159,7 +169,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
if (show_progress == -1)
show_progress = isatty(2);
if (show_progress)
- progress = start_progress_delay("Checking connectivity", 0, 0, 2);
+ progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
mark_reachable_objects(&revs, 1, progress);
stop_progress(&progress);
@@ -170,5 +180,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
s = mkpathdup("%s/pack", get_object_directory());
remove_temporary_files(s);
free(s);
+
+ if (is_repository_shallow())
+ prune_shallow(show_only);
+
return 0;
}
diff --git a/builtin/push.c b/builtin/push.c
index 7b1b66c..f8dfea4 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -26,7 +26,6 @@ static struct push_cas_option cas;
static const char **refspec;
static int refspec_nr;
static int refspec_alloc;
-static int default_matching_used;
static void add_refspec(const char *ref)
{
@@ -35,35 +34,75 @@ static void add_refspec(const char *ref)
refspec[refspec_nr-1] = ref;
}
-static void set_refspecs(const char **refs, int nr)
+static const char *map_refspec(const char *ref,
+ struct remote *remote, struct ref *local_refs)
{
+ struct ref *matched = NULL;
+
+ /* Does "ref" uniquely name our ref? */
+ if (count_refspec_match(ref, local_refs, &matched) != 1)
+ return ref;
+
+ if (remote->push) {
+ struct refspec query;
+ memset(&query, 0, sizeof(struct refspec));
+ query.src = matched->name;
+ if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&
+ query.dst) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "%s%s:%s",
+ query.force ? "+" : "",
+ query.src, query.dst);
+ return strbuf_detach(&buf, NULL);
+ }
+ }
+
+ if (push_default == PUSH_DEFAULT_UPSTREAM &&
+ starts_with(matched->name, "refs/heads/")) {
+ struct branch *branch = branch_get(matched->name + 11);
+ if (branch->merge_nr == 1 && branch->merge[0]->src) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "%s:%s",
+ ref, branch->merge[0]->src);
+ return strbuf_detach(&buf, NULL);
+ }
+ }
+
+ return ref;
+}
+
+static void set_refspecs(const char **refs, int nr, const char *repo)
+{
+ struct remote *remote = NULL;
+ struct ref *local_refs = NULL;
int i;
+
for (i = 0; i < nr; i++) {
const char *ref = refs[i];
if (!strcmp("tag", ref)) {
- char *tag;
- int len;
+ struct strbuf tagref = STRBUF_INIT;
if (nr <= ++i)
die(_("tag shorthand without <tag>"));
- len = strlen(refs[i]) + 11;
- if (deleterefs) {
- tag = xmalloc(len+1);
- strcpy(tag, ":refs/tags/");
- } else {
- tag = xmalloc(len);
- strcpy(tag, "refs/tags/");
+ ref = refs[i];
+ if (deleterefs)
+ strbuf_addf(&tagref, ":refs/tags/%s", ref);
+ else
+ strbuf_addf(&tagref, "refs/tags/%s", ref);
+ ref = strbuf_detach(&tagref, NULL);
+ } else if (deleterefs) {
+ struct strbuf delref = STRBUF_INIT;
+ if (strchr(ref, ':'))
+ die(_("--delete only accepts plain target ref names"));
+ strbuf_addf(&delref, ":%s", ref);
+ ref = strbuf_detach(&delref, NULL);
+ } else if (!strchr(ref, ':')) {
+ if (!remote) {
+ /* lazily grab remote and local_refs */
+ remote = remote_get(repo);
+ local_refs = get_local_heads();
}
- strcat(tag, refs[i]);
- ref = tag;
- } else if (deleterefs && !strchr(ref, ':')) {
- char *delref;
- int len = strlen(ref)+1;
- delref = xmalloc(len+1);
- strcpy(delref, ":");
- strcat(delref, ref);
- ref = delref;
- } else if (deleterefs)
- die(_("--delete only accepts plain target ref names"));
+ ref = map_refspec(ref, remote, local_refs);
+ }
add_refspec(ref);
}
}
@@ -164,9 +203,9 @@ static void setup_push_current(struct remote *remote, struct branch *branch)
}
static char warn_unspecified_push_default_msg[] =
-N_("push.default is unset; its implicit value is changing in\n"
+N_("push.default is unset; its implicit value has changed in\n"
"Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
- "and maintain the current behavior after the default changes, use:\n"
+ "and maintain the traditional behavior, use:\n"
"\n"
" git config --global push.default matching\n"
"\n"
@@ -174,6 +213,13 @@ N_("push.default is unset; its implicit value is changing in\n"
"\n"
" git config --global push.default simple\n"
"\n"
+ "When push.default is set to 'matching', git will push local branches\n"
+ "to the remote branches that already exist with the same name.\n"
+ "\n"
+ "Since Git 2.0, Git defaults to the more conservative 'simple'\n"
+ "behavior, which only pushes the current branch to the corresponding\n"
+ "remote branch that 'git pull' uses to update the current branch.\n"
+ "\n"
"See 'git help config' and search for 'push.default' for further information.\n"
"(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode\n"
"'current' instead of 'simple' if you sometimes use older versions of Git)");
@@ -200,14 +246,14 @@ static void setup_default_push_refspecs(struct remote *remote)
switch (push_default) {
default:
- case PUSH_DEFAULT_UNSPECIFIED:
- default_matching_used = 1;
- warn_unspecified_push_default_configuration();
- /* fallthru */
case PUSH_DEFAULT_MATCHING:
add_refspec(":");
break;
+ case PUSH_DEFAULT_UNSPECIFIED:
+ warn_unspecified_push_default_configuration();
+ /* fallthru */
+
case PUSH_DEFAULT_SIMPLE:
if (triangular)
setup_push_current(remote, branch);
@@ -236,12 +282,6 @@ static const char message_advice_pull_before_push[] =
"'git pull ...') before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
-static const char message_advice_use_upstream[] =
- N_("Updates were rejected because a pushed branch tip is behind its remote\n"
- "counterpart. If you did not intend to push that branch, you may want to\n"
- "specify branches to push or set the 'push.default' configuration variable\n"
- "to 'simple', 'current' or 'upstream' to push only the current branch.");
-
static const char message_advice_checkout_pull_push[] =
N_("Updates were rejected because a pushed branch tip is behind its remote\n"
"counterpart. Check out this branch and integrate the remote changes\n"
@@ -270,13 +310,6 @@ static void advise_pull_before_push(void)
advise(_(message_advice_pull_before_push));
}
-static void advise_use_upstream(void)
-{
- if (!advice_push_non_ff_default || !advice_push_update_rejected)
- return;
- advise(_(message_advice_use_upstream));
-}
-
static void advise_checkout_pull_push(void)
{
if (!advice_push_non_ff_matching || !advice_push_update_rejected)
@@ -338,10 +371,7 @@ static int push_with_options(struct transport *transport, int flags)
if (reject_reasons & REJECT_NON_FF_HEAD) {
advise_pull_before_push();
} else if (reject_reasons & REJECT_NON_FF_OTHER) {
- if (default_matching_used)
- advise_use_upstream();
- else
- advise_checkout_pull_push();
+ advise_checkout_pull_push();
} else if (reject_reasons & REJECT_ALREADY_EXISTS) {
advise_ref_already_exists();
} else if (reject_reasons & REJECT_FETCH_FIRST) {
@@ -494,7 +524,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
if (argc > 0) {
repo = argv[0];
- set_refspecs(argv + 1, argc - 1);
+ set_refspecs(argv + 1, argc - 1, repo);
}
rc = do_push(repo, flags);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 67ce1ef..c323081 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -13,6 +13,7 @@
#include "string-list.h"
#include "sha1-array.h"
#include "connected.h"
+#include "argv-array.h"
#include "version.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -43,6 +44,8 @@ static int fix_thin = 1;
static const char *head_name;
static void *head_name_to_free;
static int sent_capabilities;
+static int shallow_update;
+static const char *alt_shallow_file;
static enum deny_action parse_deny_action(const char *var, const char *value)
{
@@ -121,6 +124,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "receive.shallowupdate") == 0) {
+ shallow_update = git_config_bool(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -178,6 +186,8 @@ static void write_head_info(void)
if (!sent_capabilities)
show_ref("capabilities^{}", null_sha1);
+ advertise_shallow_grafts(1);
+
/* EOF */
packet_flush(1);
}
@@ -187,6 +197,7 @@ struct command {
const char *error_string;
unsigned int skip_update:1,
did_not_exist:1;
+ int index;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char ref_name[FLEX_ARRAY]; /* more */
@@ -418,7 +429,46 @@ static void refuse_unconfigured_deny_delete_current(void)
rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
}
-static const char *update(struct command *cmd)
+static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
+static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
+{
+ static struct lock_file shallow_lock;
+ struct sha1_array extra = SHA1_ARRAY_INIT;
+ const char *alt_file;
+ uint32_t mask = 1 << (cmd->index % 32);
+ int i;
+
+ trace_printf_key("GIT_TRACE_SHALLOW",
+ "shallow: update_shallow_ref %s\n", cmd->ref_name);
+ for (i = 0; i < si->shallow->nr; i++)
+ if (si->used_shallow[i] &&
+ (si->used_shallow[i][cmd->index / 32] & mask) &&
+ !delayed_reachability_test(si, i))
+ sha1_array_append(&extra, si->shallow->sha1[i]);
+
+ setup_alternate_shallow(&shallow_lock, &alt_file, &extra);
+ if (check_shallow_connected(command_singleton_iterator,
+ 0, cmd, alt_file)) {
+ rollback_lock_file(&shallow_lock);
+ sha1_array_clear(&extra);
+ return -1;
+ }
+
+ commit_lock_file(&shallow_lock);
+
+ /*
+ * Make sure setup_alternate_shallow() for the next ref does
+ * not lose these new roots..
+ */
+ for (i = 0; i < extra.nr; i++)
+ register_shallow(extra.sha1[i]);
+
+ si->shallow_ref[cmd->index] = 0;
+ sha1_array_clear(&extra);
+ return 0;
+}
+
+static const char *update(struct command *cmd, struct shallow_info *si)
{
const char *name = cmd->ref_name;
struct strbuf namespaced_name_buf = STRBUF_INIT;
@@ -428,7 +478,7 @@ static const char *update(struct command *cmd)
struct ref_lock *lock;
/* only refs/... are allowed */
- if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
+ if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
rp_error("refusing to create funny ref '%s' remotely", name);
return "funny refname";
}
@@ -459,7 +509,7 @@ static const char *update(struct command *cmd)
}
if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
- if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
+ if (deny_deletes && starts_with(name, "refs/heads/")) {
rp_error("denying ref deletion for %s", name);
return "deletion prohibited";
}
@@ -483,7 +533,7 @@ static const char *update(struct command *cmd)
if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
!is_null_sha1(old_sha1) &&
- !prefixcmp(name, "refs/heads/")) {
+ starts_with(name, "refs/heads/")) {
struct object *old_object, *new_object;
struct commit *old_commit, *new_commit;
@@ -526,6 +576,10 @@ static const char *update(struct command *cmd)
return NULL; /* good */
}
else {
+ if (shallow_update && si->shallow_ref[cmd->index] &&
+ update_shallow_ref(cmd, si))
+ return "shallow error";
+
lock = lock_any_ref_for_update(namespaced_name, old_sha1,
0, NULL);
if (!lock) {
@@ -666,12 +720,16 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
return 0;
}
-static void set_connectivity_errors(struct command *commands)
+static void set_connectivity_errors(struct command *commands,
+ struct shallow_info *si)
{
struct command *cmd;
for (cmd = commands; cmd; cmd = cmd->next) {
struct command *singleton = cmd;
+ if (shallow_update && si->shallow_ref[cmd->index])
+ /* to be checked in update_shallow_ref() */
+ continue;
if (!check_everything_connected(command_singleton_iterator,
0, &singleton))
continue;
@@ -679,18 +737,26 @@ static void set_connectivity_errors(struct command *commands)
}
}
+struct iterate_data {
+ struct command *cmds;
+ struct shallow_info *si;
+};
+
static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
{
- struct command **cmd_list = cb_data;
+ struct iterate_data *data = cb_data;
+ struct command **cmd_list = &data->cmds;
struct command *cmd = *cmd_list;
- while (cmd) {
- if (!is_null_sha1(cmd->new_sha1)) {
+ for (; cmd; cmd = cmd->next) {
+ if (shallow_update && data->si->shallow_ref[cmd->index])
+ /* to be checked in update_shallow_ref() */
+ continue;
+ if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
hashcpy(sha1, cmd->new_sha1);
*cmd_list = cmd->next;
return 0;
}
- cmd = cmd->next;
}
*cmd_list = NULL;
return -1; /* end of list */
@@ -710,10 +776,14 @@ static void reject_updates_to_hidden(struct command *commands)
}
}
-static void execute_commands(struct command *commands, const char *unpacker_error)
+static void execute_commands(struct command *commands,
+ const char *unpacker_error,
+ struct shallow_info *si)
{
+ int checked_connectivity;
struct command *cmd;
unsigned char sha1[20];
+ struct iterate_data data;
if (unpacker_error) {
for (cmd = commands; cmd; cmd = cmd->next)
@@ -721,10 +791,10 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
return;
}
- cmd = commands;
- if (check_everything_connected(iterate_receive_command_list,
- 0, &cmd))
- set_connectivity_errors(commands);
+ data.cmds = commands;
+ data.si = si;
+ if (check_everything_connected(iterate_receive_command_list, 0, &data))
+ set_connectivity_errors(commands, si);
reject_updates_to_hidden(commands);
@@ -741,6 +811,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
free(head_name_to_free);
head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
+ checked_connectivity = 1;
for (cmd = commands; cmd; cmd = cmd->next) {
if (cmd->error_string)
continue;
@@ -748,11 +819,22 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
if (cmd->skip_update)
continue;
- cmd->error_string = update(cmd);
+ cmd->error_string = update(cmd, si);
+ if (shallow_update && !cmd->error_string &&
+ si->shallow_ref[cmd->index]) {
+ error("BUG: connectivity check has not been run on ref %s",
+ cmd->ref_name);
+ checked_connectivity = 0;
+ }
}
+
+ if (shallow_update && !checked_connectivity)
+ error("BUG: run 'git fsck' for safety.\n"
+ "If there are errors, try to remove "
+ "the reported refs above");
}
-static struct command *read_head_info(void)
+static struct command *read_head_info(struct sha1_array *shallow)
{
struct command *commands = NULL;
struct command **p = &commands;
@@ -766,6 +848,14 @@ static struct command *read_head_info(void)
line = packet_read_line(0, &len);
if (!line)
break;
+
+ if (len == 48 && starts_with(line, "shallow ")) {
+ if (get_sha1_hex(line + 8, old_sha1))
+ die("protocol error: expected shallow sha, got '%s'", line + 8);
+ sha1_array_append(shallow, old_sha1);
+ continue;
+ }
+
if (len < 83 ||
line[40] != ' ' ||
line[81] != ' ' ||
@@ -817,11 +907,14 @@ static const char *parse_pack_header(struct pack_header *hdr)
static const char *pack_lockfile;
-static const char *unpack(int err_fd)
+static const char *unpack(int err_fd, struct shallow_info *si)
{
struct pack_header hdr;
+ struct argv_array av = ARGV_ARRAY_INIT;
const char *hdr_err;
+ int status;
char hdr_arg[38];
+ struct child_process child;
int fsck_objects = (receive_fsck_objects >= 0
? receive_fsck_objects
: transfer_fsck_objects >= 0
@@ -838,72 +931,63 @@ static const char *unpack(int err_fd)
"--pack_header=%"PRIu32",%"PRIu32,
ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
+ if (si->nr_ours || si->nr_theirs) {
+ alt_shallow_file = setup_temporary_shallow(si->shallow);
+ argv_array_pushl(&av, "--shallow-file", alt_shallow_file, NULL);
+ }
+
+ memset(&child, 0, sizeof(child));
if (ntohl(hdr.hdr_entries) < unpack_limit) {
- int code, i = 0;
- struct child_process child;
- const char *unpacker[5];
- unpacker[i++] = "unpack-objects";
+ argv_array_pushl(&av, "unpack-objects", hdr_arg, NULL);
if (quiet)
- unpacker[i++] = "-q";
+ argv_array_push(&av, "-q");
if (fsck_objects)
- unpacker[i++] = "--strict";
- unpacker[i++] = hdr_arg;
- unpacker[i++] = NULL;
- memset(&child, 0, sizeof(child));
- child.argv = unpacker;
+ argv_array_push(&av, "--strict");
+ child.argv = av.argv;
child.no_stdout = 1;
child.err = err_fd;
child.git_cmd = 1;
- code = run_command(&child);
- if (!code)
- return NULL;
- return "unpack-objects abnormal exit";
+ status = run_command(&child);
+ if (status)
+ return "unpack-objects abnormal exit";
} else {
- const char *keeper[7];
- int s, status, i = 0;
+ int s;
char keep_arg[256];
- struct child_process ip;
s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid());
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
- keeper[i++] = "index-pack";
- keeper[i++] = "--stdin";
+ argv_array_pushl(&av, "index-pack",
+ "--stdin", hdr_arg, keep_arg, NULL);
if (fsck_objects)
- keeper[i++] = "--strict";
+ argv_array_push(&av, "--strict");
if (fix_thin)
- keeper[i++] = "--fix-thin";
- keeper[i++] = hdr_arg;
- keeper[i++] = keep_arg;
- keeper[i++] = NULL;
- memset(&ip, 0, sizeof(ip));
- ip.argv = keeper;
- ip.out = -1;
- ip.err = err_fd;
- ip.git_cmd = 1;
- status = start_command(&ip);
- if (status) {
+ argv_array_push(&av, "--fix-thin");
+ child.argv = av.argv;
+ child.out = -1;
+ child.err = err_fd;
+ child.git_cmd = 1;
+ status = start_command(&child);
+ if (status)
return "index-pack fork failed";
- }
- pack_lockfile = index_pack_lockfile(ip.out);
- close(ip.out);
- status = finish_command(&ip);
- if (!status) {
- reprepare_packed_git();
- return NULL;
- }
- return "index-pack abnormal exit";
+ pack_lockfile = index_pack_lockfile(child.out);
+ close(child.out);
+ status = finish_command(&child);
+ if (status)
+ return "index-pack abnormal exit";
+ reprepare_packed_git();
}
+ return NULL;
}
-static const char *unpack_with_sideband(void)
+static const char *unpack_with_sideband(struct shallow_info *si)
{
struct async muxer;
const char *ret;
if (!use_sideband)
- return unpack(0);
+ return unpack(0, si);
memset(&muxer, 0, sizeof(muxer));
muxer.proc = copy_to_sideband;
@@ -911,12 +995,97 @@ static const char *unpack_with_sideband(void)
if (start_async(&muxer))
return NULL;
- ret = unpack(muxer.in);
+ ret = unpack(muxer.in, si);
finish_async(&muxer);
return ret;
}
+static void prepare_shallow_update(struct command *commands,
+ struct shallow_info *si)
+{
+ int i, j, k, bitmap_size = (si->ref->nr + 31) / 32;
+
+ si->used_shallow = xmalloc(sizeof(*si->used_shallow) *
+ si->shallow->nr);
+ assign_shallow_commits_to_refs(si, si->used_shallow, NULL);
+
+ si->need_reachability_test =
+ xcalloc(si->shallow->nr, sizeof(*si->need_reachability_test));
+ si->reachable =
+ xcalloc(si->shallow->nr, sizeof(*si->reachable));
+ si->shallow_ref = xcalloc(si->ref->nr, sizeof(*si->shallow_ref));
+
+ for (i = 0; i < si->nr_ours; i++)
+ si->need_reachability_test[si->ours[i]] = 1;
+
+ for (i = 0; i < si->shallow->nr; i++) {
+ if (!si->used_shallow[i])
+ continue;
+ for (j = 0; j < bitmap_size; j++) {
+ if (!si->used_shallow[i][j])
+ continue;
+ si->need_reachability_test[i]++;
+ for (k = 0; k < 32; k++)
+ if (si->used_shallow[i][j] & (1 << k))
+ si->shallow_ref[j * 32 + k]++;
+ }
+
+ /*
+ * true for those associated with some refs and belong
+ * in "ours" list aka "step 7 not done yet"
+ */
+ si->need_reachability_test[i] =
+ si->need_reachability_test[i] > 1;
+ }
+
+ /*
+ * keep hooks happy by forcing a temporary shallow file via
+ * env variable because we can't add --shallow-file to every
+ * command. check_everything_connected() will be done with
+ * true .git/shallow though.
+ */
+ setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1);
+}
+
+static void update_shallow_info(struct command *commands,
+ struct shallow_info *si,
+ struct sha1_array *ref)
+{
+ struct command *cmd;
+ int *ref_status;
+ remove_nonexistent_theirs_shallow(si);
+ if (!si->nr_ours && !si->nr_theirs) {
+ shallow_update = 0;
+ return;
+ }
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (is_null_sha1(cmd->new_sha1))
+ continue;
+ sha1_array_append(ref, cmd->new_sha1);
+ cmd->index = ref->nr - 1;
+ }
+ si->ref = ref;
+
+ if (shallow_update) {
+ prepare_shallow_update(commands, si);
+ return;
+ }
+
+ ref_status = xmalloc(sizeof(*ref_status) * ref->nr);
+ assign_shallow_commits_to_refs(si, NULL, ref_status);
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (is_null_sha1(cmd->new_sha1))
+ continue;
+ if (ref_status[cmd->index]) {
+ cmd->error_string = "shallow update not allowed";
+ cmd->skip_update = 1;
+ }
+ }
+ free(ref_status);
+}
+
static void report(struct command *commands, const char *unpack_status)
{
struct command *cmd;
@@ -958,6 +1127,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
int i;
char *dir = NULL;
struct command *commands;
+ struct sha1_array shallow = SHA1_ARRAY_INIT;
+ struct sha1_array ref = SHA1_ARRAY_INIT;
+ struct shallow_info si;
packet_trace_identity("receive-pack");
@@ -998,9 +1170,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (!enter_repo(dir, 0))
die("'%s' does not appear to be a git repository", dir);
- if (is_repository_shallow())
- die("attempt to push into a shallow repository");
-
git_config(receive_pack_config, NULL);
if (0 <= transfer_unpack_limit)
@@ -1014,12 +1183,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (advertise_refs)
return 0;
- if ((commands = read_head_info()) != NULL) {
+ if ((commands = read_head_info(&shallow)) != NULL) {
const char *unpack_status = NULL;
- if (!delete_only(commands))
- unpack_status = unpack_with_sideband();
- execute_commands(commands, unpack_status);
+ prepare_shallow_info(&si, &shallow);
+ if (!si.nr_ours && !si.nr_theirs)
+ shallow_update = 0;
+ if (!delete_only(commands)) {
+ unpack_status = unpack_with_sideband(&si);
+ update_shallow_info(commands, &si, &ref);
+ }
+ execute_commands(commands, unpack_status, &si);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
if (report_status)
@@ -1035,8 +1209,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
}
if (auto_update_server_info)
update_server_info(0);
+ clear_shallow_info(&si);
}
if (use_sideband)
packet_flush(1);
+ sha1_array_clear(&shallow);
+ sha1_array_clear(&ref);
return 0;
}
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 6eb24c8..c12a9784 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -561,7 +561,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
return; /* both given explicitly -- nothing to tweak */
for (ent = reflog_expire_cfg; ent; ent = ent->next) {
- if (!fnmatch(ent->pattern, ref, 0)) {
+ if (!wildmatch(ent->pattern, ref, 0, NULL)) {
if (!(slot & EXPIRE_TOTAL))
cb->expire_total = ent->expire_total;
if (!(slot & EXPIRE_UNREACH))
@@ -610,12 +610,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
const char *arg = argv[i];
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
cb.dry_run = 1;
- else if (!prefixcmp(arg, "--expire=")) {
+ else if (starts_with(arg, "--expire=")) {
if (parse_expiry_date(arg + 9, &cb.expire_total))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_TOTAL;
}
- else if (!prefixcmp(arg, "--expire-unreachable=")) {
+ else if (starts_with(arg, "--expire-unreachable=")) {
if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_UNREACH;
diff --git a/builtin/remote.c b/builtin/remote.c
index 4e14891..9b3e368 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -6,6 +6,7 @@
#include "strbuf.h"
#include "run-command.h"
#include "refs.h"
+#include "argv-array.h"
static const char * const builtin_remote_usage[] = {
N_("git remote [-v | --verbose]"),
@@ -77,17 +78,6 @@ static const char * const builtin_remote_seturl_usage[] = {
static int verbose;
-static int show_all(void);
-static int prune_remote(const char *remote, int dry_run);
-
-static inline int postfixcmp(const char *string, const char *postfix)
-{
- int len1 = strlen(string), len2 = strlen(postfix);
- if (len1 < len2)
- return 1;
- return strcmp(string + len1 - len2, postfix);
-}
-
static int fetch_remote(const char *name)
{
const char *argv[] = { "fetch", name, NULL, NULL };
@@ -269,7 +259,7 @@ static const char *abbrev_ref(const char *name, const char *prefix)
static int config_read_branches(const char *key, const char *value, void *cb)
{
- if (!prefixcmp(key, "branch.")) {
+ if (starts_with(key, "branch.")) {
const char *orig_key = key;
char *name;
struct string_list_item *item;
@@ -277,13 +267,13 @@ static int config_read_branches(const char *key, const char *value, void *cb)
enum { REMOTE, MERGE, REBASE } type;
key += 7;
- if (!postfixcmp(key, ".remote")) {
+ if (ends_with(key, ".remote")) {
name = xstrndup(key, strlen(key) - 7);
type = REMOTE;
- } else if (!postfixcmp(key, ".merge")) {
+ } else if (ends_with(key, ".merge")) {
name = xstrndup(key, strlen(key) - 6);
type = MERGE;
- } else if (!postfixcmp(key, ".rebase")) {
+ } else if (ends_with(key, ".rebase")) {
name = xstrndup(key, strlen(key) - 7);
type = REBASE;
} else
@@ -309,8 +299,13 @@ static int config_read_branches(const char *key, const char *value, void *cb)
space = strchr(value, ' ');
}
string_list_append(&info->merge, xstrdup(value));
- } else
- info->rebase = git_config_bool(orig_key, value);
+ } else {
+ int v = git_config_maybe_bool(orig_key, value);
+ if (v >= 0)
+ info->rebase = v;
+ else if (!strcmp(value, "preserve"))
+ info->rebase = 1;
+ }
}
return 0;
}
@@ -534,9 +529,9 @@ static int add_branch_for_removal(const char *refname,
}
/* don't delete non-remote-tracking refs */
- if (prefixcmp(refname, "refs/remotes/")) {
+ if (!starts_with(refname, "refs/remotes/")) {
/* advise user how to delete local branches */
- if (!prefixcmp(refname, "refs/heads/"))
+ if (starts_with(refname, "refs/heads/"))
string_list_append(branches->skipped,
abbrev_branch(refname));
/* silently skip over other non-remote refs */
@@ -571,7 +566,7 @@ static int read_remote_branches(const char *refname,
const char *symref;
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
- if (!prefixcmp(refname, buf.buf)) {
+ if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
@@ -754,15 +749,23 @@ static int mv(int argc, const char **argv)
static int remove_branches(struct string_list *branches)
{
+ const char **branch_names;
int i, result = 0;
+
+ branch_names = xmalloc(branches->nr * sizeof(*branch_names));
+ for (i = 0; i < branches->nr; i++)
+ branch_names[i] = branches->items[i].string;
+ result |= repack_without_refs(branch_names, branches->nr);
+ free(branch_names);
+
for (i = 0; i < branches->nr; i++) {
struct string_list_item *item = branches->items + i;
const char *refname = item->string;
- unsigned char *sha1 = item->util;
- if (delete_ref(refname, sha1, 0))
+ if (delete_ref(refname, NULL, 0))
result |= error(_("Could not remove branch %s"), refname);
}
+
return result;
}
@@ -794,10 +797,6 @@ static int rm(int argc, const char **argv)
known_remotes.to_delete = remote;
for_each_remote(add_known_remote, &known_remotes);
- strbuf_addf(&buf, "remote.%s", remote->name);
- if (git_config_rename_section(buf.buf, NULL) < 1)
- return error(_("Could not remove config section '%s'"), buf.buf);
-
read_branches();
for (i = 0; i < branch_list.nr; i++) {
struct string_list_item *item = branch_list.items + i;
@@ -842,6 +841,12 @@ static int rm(int argc, const char **argv)
}
string_list_clear(&skipped, 0);
+ if (!result) {
+ strbuf_addf(&buf, "remote.%s", remote->name);
+ if (git_config_rename_section(buf.buf, NULL) < 1)
+ return error(_("Could not remove config section '%s'"), buf.buf);
+ }
+
return result;
}
@@ -1084,6 +1089,64 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data)
return 0;
}
+static int get_one_entry(struct remote *remote, void *priv)
+{
+ struct string_list *list = priv;
+ struct strbuf url_buf = STRBUF_INIT;
+ const char **url;
+ int i, url_nr;
+
+ if (remote->url_nr > 0) {
+ strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
+ string_list_append(list, remote->name)->util =
+ strbuf_detach(&url_buf, NULL);
+ } else
+ string_list_append(list, remote->name)->util = NULL;
+ if (remote->pushurl_nr) {
+ url = remote->pushurl;
+ url_nr = remote->pushurl_nr;
+ } else {
+ url = remote->url;
+ url_nr = remote->url_nr;
+ }
+ for (i = 0; i < url_nr; i++)
+ {
+ strbuf_addf(&url_buf, "%s (push)", url[i]);
+ string_list_append(list, remote->name)->util =
+ strbuf_detach(&url_buf, NULL);
+ }
+
+ return 0;
+}
+
+static int show_all(void)
+{
+ struct string_list list = STRING_LIST_INIT_NODUP;
+ int result;
+
+ list.strdup_strings = 1;
+ result = for_each_remote(get_one_entry, &list);
+
+ if (!result) {
+ int i;
+
+ sort_string_list(&list);
+ for (i = 0; i < list.nr; i++) {
+ struct string_list_item *item = list.items + i;
+ if (verbose)
+ printf("%s\t%s\n", item->string,
+ item->util ? (const char *)item->util : "");
+ else {
+ if (i && !strcmp((item - 1)->string, item->string))
+ continue;
+ printf("%s\n", item->string);
+ }
+ }
+ }
+ string_list_clear(&list, 1);
+ return result;
+}
+
static int show(int argc, const char **argv)
{
int no_query = 0, result = 0, query_flag = 0;
@@ -1246,30 +1309,12 @@ static int set_head(int argc, const char **argv)
return result;
}
-static int prune(int argc, const char **argv)
-{
- int dry_run = 0, result = 0;
- struct option options[] = {
- OPT__DRY_RUN(&dry_run, N_("dry run")),
- OPT_END()
- };
-
- argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
- 0);
-
- if (argc < 1)
- usage_with_options(builtin_remote_prune_usage, options);
-
- for (; argc; argc--, argv++)
- result |= prune_remote(*argv, dry_run);
-
- return result;
-}
-
static int prune_remote(const char *remote, int dry_run)
{
int result = 0, i;
struct ref_states states;
+ struct string_list delete_refs_list = STRING_LIST_INIT_NODUP;
+ const char **delete_refs;
const char *dangling_msg = dry_run
? _(" %s will become dangling!")
: _(" %s has become dangling!");
@@ -1283,11 +1328,20 @@ static int prune_remote(const char *remote, int dry_run)
states.remote->url_nr
? states.remote->url[0]
: _("(no URL)"));
+
+ delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
+ for (i = 0; i < states.stale.nr; i++)
+ delete_refs[i] = states.stale.items[i].util;
+ if (!dry_run)
+ result |= repack_without_refs(delete_refs, states.stale.nr);
+ free(delete_refs);
}
for (i = 0; i < states.stale.nr; i++) {
const char *refname = states.stale.items[i].util;
+ string_list_insert(&delete_refs_list, refname);
+
if (!dry_run)
result |= delete_ref(refname, NULL, 0);
@@ -1297,13 +1351,35 @@ static int prune_remote(const char *remote, int dry_run)
else
printf_ln(_(" * [pruned] %s"),
abbrev_ref(refname, "refs/remotes/"));
- warn_dangling_symref(stdout, dangling_msg, refname);
}
+ warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list);
+ string_list_clear(&delete_refs_list, 0);
+
free_remote_ref_states(&states);
return result;
}
+static int prune(int argc, const char **argv)
+{
+ int dry_run = 0, result = 0;
+ struct option options[] = {
+ OPT__DRY_RUN(&dry_run, N_("dry run")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
+ 0);
+
+ if (argc < 1)
+ usage_with_options(builtin_remote_prune_usage, options);
+
+ for (; argc; argc--, argv++)
+ result |= prune_remote(*argv, dry_run);
+
+ return result;
+}
+
static int get_remote_default(const char *key, const char *value, void *priv)
{
if (strcmp(key, "remotes.default") == 0) {
@@ -1315,42 +1391,42 @@ static int get_remote_default(const char *key, const char *value, void *priv)
static int update(int argc, const char **argv)
{
- int i, prune = 0;
+ int i, prune = -1;
struct option options[] = {
OPT_BOOL('p', "prune", &prune,
N_("prune remotes after fetching")),
OPT_END()
};
- const char **fetch_argv;
- int fetch_argc = 0;
+ struct argv_array fetch_argv = ARGV_ARRAY_INIT;
int default_defined = 0;
-
- fetch_argv = xmalloc(sizeof(char *) * (argc+5));
+ int retval;
argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
PARSE_OPT_KEEP_ARGV0);
- fetch_argv[fetch_argc++] = "fetch";
+ argv_array_push(&fetch_argv, "fetch");
- if (prune)
- fetch_argv[fetch_argc++] = "--prune";
+ if (prune != -1)
+ argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune");
if (verbose)
- fetch_argv[fetch_argc++] = "-v";
- fetch_argv[fetch_argc++] = "--multiple";
+ argv_array_push(&fetch_argv, "-v");
+ argv_array_push(&fetch_argv, "--multiple");
if (argc < 2)
- fetch_argv[fetch_argc++] = "default";
+ argv_array_push(&fetch_argv, "default");
for (i = 1; i < argc; i++)
- fetch_argv[fetch_argc++] = argv[i];
+ argv_array_push(&fetch_argv, argv[i]);
- if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
+ if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) {
git_config(get_remote_default, &default_defined);
- if (!default_defined)
- fetch_argv[fetch_argc-1] = "--all";
+ if (!default_defined) {
+ argv_array_pop(&fetch_argv);
+ argv_array_push(&fetch_argv, "--all");
+ }
}
- fetch_argv[fetch_argc] = NULL;
-
- return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
+ retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD);
+ argv_array_clear(&fetch_argv);
+ return retval;
}
static int remove_all_fetch_refspecs(const char *remote, const char *key)
@@ -1505,64 +1581,6 @@ static int set_url(int argc, const char **argv)
return 0;
}
-static int get_one_entry(struct remote *remote, void *priv)
-{
- struct string_list *list = priv;
- struct strbuf url_buf = STRBUF_INIT;
- const char **url;
- int i, url_nr;
-
- if (remote->url_nr > 0) {
- strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
- string_list_append(list, remote->name)->util =
- strbuf_detach(&url_buf, NULL);
- } else
- string_list_append(list, remote->name)->util = NULL;
- if (remote->pushurl_nr) {
- url = remote->pushurl;
- url_nr = remote->pushurl_nr;
- } else {
- url = remote->url;
- url_nr = remote->url_nr;
- }
- for (i = 0; i < url_nr; i++)
- {
- strbuf_addf(&url_buf, "%s (push)", url[i]);
- string_list_append(list, remote->name)->util =
- strbuf_detach(&url_buf, NULL);
- }
-
- return 0;
-}
-
-static int show_all(void)
-{
- struct string_list list = STRING_LIST_INIT_NODUP;
- int result;
-
- list.strdup_strings = 1;
- result = for_each_remote(get_one_entry, &list);
-
- if (!result) {
- int i;
-
- sort_string_list(&list);
- for (i = 0; i < list.nr; i++) {
- struct string_list_item *item = list.items + i;
- if (verbose)
- printf("%s\t%s\n", item->string,
- item->util ? (const char *)item->util : "");
- else {
- if (i && !strcmp((item - 1)->string, item->string))
- continue;
- printf("%s\n", item->string);
- }
- }
- }
- string_list_clear(&list, 1);
- return result;
-}
-
int cmd_remote(int argc, const char **argv, const char *prefix)
{
struct option options[] = {
diff --git a/builtin/repack.c b/builtin/repack.c
index a2b9dba..6b0b62d 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -9,6 +9,7 @@
#include "argv-array.h"
static int delta_base_offset = 1;
+static int pack_kept_objects = -1;
static char *packdir, *packtmp;
static const char *const git_repack_usage[] = {
@@ -22,6 +23,10 @@ static int repack_config(const char *var, const char *value, void *cb)
delta_base_offset = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "repack.packkeptobjects")) {
+ pack_kept_objects = git_config_bool(var, value);
+ return 0;
+ }
return git_default_config(var, value, cb);
}
@@ -78,7 +83,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
return;
while ((e = readdir(dir)) != NULL) {
- if (suffixcmp(e->d_name, ".pack"))
+ if (!ends_with(e->d_name, ".pack"))
continue;
len = strlen(e->d_name) - strlen(".pack");
@@ -94,7 +99,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
static void remove_redundant_pack(const char *dir_name, const char *base_name)
{
- const char *exts[] = {".pack", ".idx", ".keep"};
+ const char *exts[] = {".pack", ".idx", ".keep", ".bitmap"};
int i;
struct strbuf buf = STRBUF_INIT;
size_t plen;
@@ -115,7 +120,14 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)
int cmd_repack(int argc, const char **argv, const char *prefix)
{
- const char *exts[2] = {".pack", ".idx"};
+ struct {
+ const char *name;
+ unsigned optional:1;
+ } exts[] = {
+ {".pack"},
+ {".idx"},
+ {".bitmap", 1},
+ };
struct child_process cmd;
struct string_list_item *item;
struct argv_array cmd_args = ARGV_ARRAY_INIT;
@@ -137,6 +149,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int no_update_server_info = 0;
int quiet = 0;
int local = 0;
+ int write_bitmap = -1;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -155,6 +168,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, N_("be quiet")),
OPT_BOOL('l', "local", &local,
N_("pass --local to git-pack-objects")),
+ OPT_BOOL('b', "write-bitmap-index", &write_bitmap,
+ N_("write bitmap index")),
OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
N_("with -A, do not loosen objects older than this")),
OPT_STRING(0, "window", &window, N_("n"),
@@ -165,6 +180,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("limits the maximum delta depth")),
OPT_STRING(0, "max-pack-size", &max_pack_size, N_("bytes"),
N_("maximum size of each packfile")),
+ OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
+ N_("repack objects in packs marked with .keep")),
OPT_END()
};
@@ -173,6 +190,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, builtin_repack_options,
git_repack_usage, 0);
+ if (pack_kept_objects < 0)
+ pack_kept_objects = write_bitmap;
+
packdir = mkpathdup("%s/pack", get_object_directory());
packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
@@ -180,7 +200,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd_args, "pack-objects");
argv_array_push(&cmd_args, "--keep-true-parents");
- argv_array_push(&cmd_args, "--honor-pack-keep");
+ if (!pack_kept_objects)
+ argv_array_push(&cmd_args, "--honor-pack-keep");
argv_array_push(&cmd_args, "--non-empty");
argv_array_push(&cmd_args, "--all");
argv_array_push(&cmd_args, "--reflog");
@@ -196,6 +217,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_pushf(&cmd_args, "--no-reuse-delta");
if (no_reuse_object)
argv_array_pushf(&cmd_args, "--no-reuse-object");
+ if (write_bitmap >= 0)
+ argv_array_pushf(&cmd_args, "--%swrite-bitmap-index",
+ write_bitmap ? "" : "no-");
if (pack_everything & ALL_INTO_ONE) {
get_non_kept_pack_filenames(&existing_packs);
@@ -256,17 +280,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
*/
failed = 0;
for_each_string_list_item(item, &names) {
- for (ext = 0; ext < 2; ext++) {
+ for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
char *fname, *fname_old;
- fname = mkpathdup("%s/%s%s", packdir,
- item->string, exts[ext]);
+ fname = mkpathdup("%s/pack-%s%s", packdir,
+ item->string, exts[ext].name);
if (!file_exists(fname)) {
free(fname);
continue;
}
fname_old = mkpath("%s/old-%s%s", packdir,
- item->string, exts[ext]);
+ item->string, exts[ext].name);
if (file_exists(fname_old))
if (unlink(fname_old))
failed = 1;
@@ -313,19 +337,23 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
/* Now the ones with the same name are out of the way... */
for_each_string_list_item(item, &names) {
- for (ext = 0; ext < 2; ext++) {
+ for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
char *fname, *fname_old;
struct stat statbuffer;
+ int exists = 0;
fname = mkpathdup("%s/pack-%s%s",
- packdir, item->string, exts[ext]);
+ packdir, item->string, exts[ext].name);
fname_old = mkpathdup("%s-%s%s",
- packtmp, item->string, exts[ext]);
+ packtmp, item->string, exts[ext].name);
if (!stat(fname_old, &statbuffer)) {
statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
chmod(fname_old, statbuffer.st_mode);
+ exists = 1;
+ }
+ if (exists || !exts[ext].optional) {
+ if (rename(fname_old, fname))
+ die_errno(_("renaming '%s' failed"), fname_old);
}
- if (rename(fname_old, fname))
- die_errno(_("renaming '%s' failed"), fname_old);
free(fname);
free(fname_old);
}
@@ -333,12 +361,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
/* Remove the "old-" files */
for_each_string_list_item(item, &names) {
- for (ext = 0; ext < 2; ext++) {
+ for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
char *fname;
- fname = mkpath("%s/old-pack-%s%s",
+ fname = mkpath("%s/old-%s%s",
packdir,
item->string,
- exts[ext]);
+ exts[ext].name);
if (remove_path(fname))
warning(_("removing '%s' failed"), fname);
}
diff --git a/builtin/replace.c b/builtin/replace.c
index b1bd3ef..b62420a 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -16,27 +16,69 @@
static const char * const git_replace_usage[] = {
N_("git replace [-f] <object> <replacement>"),
N_("git replace -d <object>..."),
- N_("git replace -l [<pattern>]"),
+ N_("git replace [--format=<format>] [-l [<pattern>]]"),
NULL
};
+enum replace_format {
+ REPLACE_FORMAT_SHORT,
+ REPLACE_FORMAT_MEDIUM,
+ REPLACE_FORMAT_LONG
+};
+
+struct show_data {
+ const char *pattern;
+ enum replace_format format;
+};
+
static int show_reference(const char *refname, const unsigned char *sha1,
int flag, void *cb_data)
{
- const char *pattern = cb_data;
+ struct show_data *data = cb_data;
- if (!fnmatch(pattern, refname, 0))
- printf("%s\n", refname);
+ if (!wildmatch(data->pattern, refname, 0, NULL)) {
+ if (data->format == REPLACE_FORMAT_SHORT)
+ printf("%s\n", refname);
+ else if (data->format == REPLACE_FORMAT_MEDIUM)
+ printf("%s -> %s\n", refname, sha1_to_hex(sha1));
+ else { /* data->format == REPLACE_FORMAT_LONG */
+ unsigned char object[20];
+ enum object_type obj_type, repl_type;
+
+ if (get_sha1(refname, object))
+ return error("Failed to resolve '%s' as a valid ref.", refname);
+
+ obj_type = sha1_object_info(object, NULL);
+ repl_type = sha1_object_info(sha1, NULL);
+
+ printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
+ sha1_to_hex(sha1), typename(repl_type));
+ }
+ }
return 0;
}
-static int list_replace_refs(const char *pattern)
+static int list_replace_refs(const char *pattern, const char *format)
{
+ struct show_data data;
+
if (pattern == NULL)
pattern = "*";
+ data.pattern = pattern;
- for_each_replace_ref(show_reference, (void *) pattern);
+ if (format == NULL || *format == '\0' || !strcmp(format, "short"))
+ data.format = REPLACE_FORMAT_SHORT;
+ else if (!strcmp(format, "medium"))
+ data.format = REPLACE_FORMAT_MEDIUM;
+ else if (!strcmp(format, "long"))
+ data.format = REPLACE_FORMAT_LONG;
+ else
+ die("invalid replace format '%s'\n"
+ "valid formats are 'short', 'medium' and 'long'\n",
+ format);
+
+ for_each_replace_ref(show_reference, (void *) &data);
return 0;
}
@@ -127,19 +169,27 @@ static int replace_object(const char *object_ref, const char *replace_ref,
int cmd_replace(int argc, const char **argv, const char *prefix)
{
int list = 0, delete = 0, force = 0;
+ const char *format = NULL;
struct option options[] = {
OPT_BOOL('l', "list", &list, N_("list replace refs")),
OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+ OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
OPT_END()
};
+ check_replace_refs = 0;
+
argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
if (list && delete)
usage_msg_opt("-l and -d cannot be used together",
git_replace_usage, options);
+ if (format && delete)
+ usage_msg_opt("--format and -d cannot be used together",
+ git_replace_usage, options);
+
if (force && (list || delete))
usage_msg_opt("-f cannot be used with -d or -l",
git_replace_usage, options);
@@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
if (argc != 2)
usage_msg_opt("bad number of arguments",
git_replace_usage, options);
+ if (format)
+ usage_msg_opt("--format cannot be used when not listing",
+ git_replace_usage, options);
return replace_object(argv[0], argv[1], force);
}
@@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
usage_msg_opt("-f needs some arguments",
git_replace_usage, options);
- return list_replace_refs(argv[0]);
+ return list_replace_refs(argv[0], format);
}
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 4e51add..98eb8c5 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -60,6 +60,8 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, rerere_usage, 0);
+ git_config(git_xmerge_config, NULL);
+
if (autoupdate == 1)
flags = RERERE_AUTOUPDATE;
if (autoupdate == 0)
diff --git a/builtin/reset.c b/builtin/reset.c
index 6004803..f4e0875 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -116,25 +116,34 @@ static void update_index_from_diff(struct diff_queue_struct *q,
struct diff_options *opt, void *data)
{
int i;
+ int intent_to_add = *(int *)data;
for (i = 0; i < q->nr; i++) {
struct diff_filespec *one = q->queue[i]->one;
- if (one->mode && !is_null_sha1(one->sha1)) {
- struct cache_entry *ce;
- ce = make_cache_entry(one->mode, one->sha1, one->path,
- 0, 0);
- if (!ce)
- die(_("make_cache_entry failed for path '%s'"),
- one->path);
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
- ADD_CACHE_OK_TO_REPLACE);
- } else
+ int is_missing = !(one->mode && !is_null_sha1(one->sha1));
+ struct cache_entry *ce;
+
+ if (is_missing && !intent_to_add) {
remove_file_from_cache(one->path);
+ continue;
+ }
+
+ ce = make_cache_entry(one->mode, one->sha1, one->path,
+ 0, 0);
+ if (!ce)
+ die(_("make_cache_entry failed for path '%s'"),
+ one->path);
+ if (is_missing) {
+ ce->ce_flags |= CE_INTENT_TO_ADD;
+ set_object_name_for_intent_to_add_entry(ce);
+ }
+ add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
}
}
static int read_from_tree(const struct pathspec *pathspec,
- unsigned char *tree_sha1)
+ unsigned char *tree_sha1,
+ int intent_to_add)
{
struct diff_options opt;
@@ -142,6 +151,7 @@ static int read_from_tree(const struct pathspec *pathspec,
copy_pathspec(&opt.pathspec, pathspec);
opt.output_format = DIFF_FORMAT_CALLBACK;
opt.format_callback = update_index_from_diff;
+ opt.format_callback_data = &intent_to_add;
if (do_diff_cache(tree_sha1, &opt))
return 1;
@@ -258,6 +268,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
const char *rev;
unsigned char sha1[20];
struct pathspec pathspec;
+ int intent_to_add = 0;
const struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_SET_INT(0, "mixed", &reset_type,
@@ -270,6 +281,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
OPT_SET_INT(0, "keep", &reset_type,
N_("reset HEAD but keep local changes"), KEEP),
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
+ OPT_BOOL('N', "intent-to-add", &intent_to_add,
+ N_("record only the fact that removed paths will be added later")),
OPT_END()
};
@@ -320,13 +333,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (reset_type == NONE)
reset_type = MIXED; /* by default */
- if (reset_type != SOFT && reset_type != MIXED)
+ if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree()))
setup_work_tree();
if (reset_type == MIXED && is_bare_repository())
die(_("%s reset is not allowed in a bare repository"),
_(reset_type_names[reset_type]));
+ if (intent_to_add && reset_type != MIXED)
+ die(_("-N can only be used with --mixed"));
+
/* Soft reset does not touch the index file nor the working tree
* at all, but requires them in a good order. Other resets reset
* the index file to the tree object we are switching to. */
@@ -338,10 +354,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
int newfd = hold_locked_index(lock, 1);
if (reset_type == MIXED) {
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
- if (read_from_tree(&pathspec, sha1))
+ if (read_from_tree(&pathspec, sha1, intent_to_add))
return 1;
- refresh_index(&the_index, flags, NULL, NULL,
- _("Unstaged changes after reset:"));
+ if (get_git_work_tree())
+ refresh_index(&the_index, flags, NULL, NULL,
+ _("Unstaged changes after reset:"));
} else {
int err = reset_index(sha1, reset_type, quiet);
if (reset_type == KEEP && !err)
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 0745e2d..9f92905 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -3,6 +3,8 @@
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
+#include "pack.h"
+#include "pack-bitmap.h"
#include "builtin.h"
#include "log-tree.h"
#include "graph.h"
@@ -257,6 +259,18 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
return 0;
}
+static int show_object_fast(
+ const unsigned char *sha1,
+ enum object_type type,
+ int exclude,
+ uint32_t name_hash,
+ struct packed_git *found_pack,
+ off_t found_offset)
+{
+ fprintf(stdout, "%s\n", sha1_to_hex(sha1));
+ return 1;
+}
+
int cmd_rev_list(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -265,6 +279,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
int bisect_list = 0;
int bisect_show_vars = 0;
int bisect_find_all = 0;
+ int use_bitmap_index = 0;
git_config(git_default_config, NULL);
init_revisions(&revs, prefix);
@@ -306,6 +321,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
bisect_show_vars = 1;
continue;
}
+ if (!strcmp(arg, "--use-bitmap-index")) {
+ use_bitmap_index = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--test-bitmap")) {
+ test_bitmap_walk(&revs);
+ return 0;
+ }
usage(rev_list_usage);
}
@@ -333,6 +356,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (bisect_list)
revs.limited = 1;
+ if (use_bitmap_index) {
+ if (revs.count && !revs.left_right && !revs.cherry_mark) {
+ uint32_t commit_count;
+ if (!prepare_bitmap_walk(&revs)) {
+ count_bitmap_commit_list(&commit_count, NULL, NULL, NULL);
+ printf("%d\n", commit_count);
+ return 0;
+ }
+ } else if (revs.tag_objects && revs.tree_objects && revs.blob_objects) {
+ if (!prepare_bitmap_walk(&revs)) {
+ traverse_bitmap_commit_list(&show_object_fast);
+ return 0;
+ }
+ }
+ }
+
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
if (revs.tree_objects)
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index c4b768f..1a6122d 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -9,6 +9,8 @@
#include "quote.h"
#include "builtin.h"
#include "parse-options.h"
+#include "diff.h"
+#include "revision.h"
#define DO_REVS 1
#define DO_NOREV 2
@@ -30,6 +32,9 @@ static int abbrev_ref;
static int abbrev_ref_strict;
static int output_sq;
+static int stuck_long;
+static struct string_list *ref_excludes;
+
/*
* Some arguments are relevant "revision" arguments,
* others are about output format or other details.
@@ -185,6 +190,8 @@ static int show_default(void)
static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
+ if (ref_excluded(ref_excludes, refname))
+ return 0;
show_rev(NORMAL, sha1, refname);
return 0;
}
@@ -324,12 +331,15 @@ static int parseopt_dump(const struct option *o, const char *arg, int unset)
struct strbuf *parsed = o->value;
if (unset)
strbuf_addf(parsed, " --no-%s", o->long_name);
- else if (o->short_name)
+ else if (o->short_name && (o->long_name == NULL || !stuck_long))
strbuf_addf(parsed, " -%c", o->short_name);
else
strbuf_addf(parsed, " --%s", o->long_name);
if (arg) {
- strbuf_addch(parsed, ' ');
+ if (!stuck_long)
+ strbuf_addch(parsed, ' ');
+ else if (o->long_name)
+ strbuf_addch(parsed, '=');
sq_quote_buf(parsed, arg);
}
return 0;
@@ -355,6 +365,8 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
N_("stop parsing after the "
"first non-option argument")),
+ OPT_BOOL(0, "stuck-long", &stuck_long,
+ N_("output in stuck long form")),
OPT_END(),
};
@@ -383,9 +395,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
usage[unb++] = strbuf_detach(&sb, NULL);
}
- /* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
+ /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
while (strbuf_getline(&sb, stdin, '\n') != EOF) {
const char *s;
+ const char *end;
struct option *o;
if (!sb.len)
@@ -407,6 +420,16 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
o->value = &parsed;
o->flags = PARSE_OPT_NOARG;
o->callback = &parseopt_dump;
+
+ /* Possible argument name hint */
+ end = s;
+ while (s > sb.buf && strchr("*=?!", s[-1]) == NULL)
+ --s;
+ if (s != sb.buf && s != end)
+ o->argh = xmemdupz(s, end - s);
+ if (s == sb.buf)
+ s = end;
+
while (s > sb.buf && strchr("*=?!", s[-1])) {
switch (*--s) {
case '=':
@@ -520,7 +543,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
continue;
}
- if (!prefixcmp(arg, "-n")) {
+ if (starts_with(arg, "-n")) {
if ((filter & DO_FLAGS) && (filter & DO_REVS))
show(arg);
continue;
@@ -535,15 +558,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--default")) {
- def = argv[i+1];
- i++;
+ def = argv[++i];
+ if (!def)
+ die("--default requires an argument");
continue;
}
if (!strcmp(arg, "--prefix")) {
- prefix = argv[i+1];
+ prefix = argv[++i];
+ if (!prefix)
+ die("--prefix requires an argument");
startup_info->prefix = prefix;
output_prefix = 1;
- i++;
continue;
}
if (!strcmp(arg, "--revs-only")) {
@@ -572,7 +597,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--short") ||
- !prefixcmp(arg, "--short=")) {
+ starts_with(arg, "--short=")) {
filter &= ~(DO_FLAGS|DO_NOREV);
verify = 1;
abbrev = DEFAULT_ABBREV;
@@ -600,7 +625,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
symbolic = SHOW_SYMBOLIC_FULL;
continue;
}
- if (!prefixcmp(arg, "--abbrev-ref") &&
+ if (starts_with(arg, "--abbrev-ref") &&
(!arg[12] || arg[12] == '=')) {
abbrev_ref = 1;
abbrev_ref_strict = warn_ambiguous_refs;
@@ -618,7 +643,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
for_each_ref(show_reference, NULL);
continue;
}
- if (!prefixcmp(arg, "--disambiguate=")) {
+ if (starts_with(arg, "--disambiguate=")) {
for_each_abbrev(arg + 15, show_abbrev, NULL);
continue;
}
@@ -627,35 +652,46 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
for_each_ref_in("refs/bisect/good", anti_reference, NULL);
continue;
}
- if (!prefixcmp(arg, "--branches=")) {
+ if (starts_with(arg, "--branches=")) {
for_each_glob_ref_in(show_reference, arg + 11,
"refs/heads/", NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
if (!strcmp(arg, "--branches")) {
for_each_branch_ref(show_reference, NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
- if (!prefixcmp(arg, "--tags=")) {
+ if (starts_with(arg, "--tags=")) {
for_each_glob_ref_in(show_reference, arg + 7,
"refs/tags/", NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
if (!strcmp(arg, "--tags")) {
for_each_tag_ref(show_reference, NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
- if (!prefixcmp(arg, "--glob=")) {
+ if (starts_with(arg, "--glob=")) {
for_each_glob_ref(show_reference, arg + 7, NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
- if (!prefixcmp(arg, "--remotes=")) {
+ if (starts_with(arg, "--remotes=")) {
for_each_glob_ref_in(show_reference, arg + 10,
"refs/remotes/", NULL);
+ clear_ref_exclusion(&ref_excludes);
continue;
}
if (!strcmp(arg, "--remotes")) {
for_each_remote_ref(show_reference, NULL);
+ clear_ref_exclusion(&ref_excludes);
+ continue;
+ }
+ if (starts_with(arg, "--exclude=")) {
+ add_ref_exclusion(&ref_excludes, arg + 10);
continue;
}
if (!strcmp(arg, "--local-env-vars")) {
@@ -715,9 +751,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--resolve-git-dir")) {
- const char *gitdir = resolve_gitdir(argv[i+1]);
+ const char *gitdir = argv[++i];
+ if (!gitdir)
+ die("--resolve-git-dir requires an argument");
+ gitdir = resolve_gitdir(gitdir);
if (!gitdir)
- die("not a gitdir '%s'", argv[i+1]);
+ die("not a gitdir '%s'", argv[i]);
puts(gitdir);
continue;
}
@@ -736,19 +775,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
: "false");
continue;
}
- if (!prefixcmp(arg, "--since=")) {
+ if (starts_with(arg, "--since=")) {
show_datestring("--max-age=", arg+8);
continue;
}
- if (!prefixcmp(arg, "--after=")) {
+ if (starts_with(arg, "--after=")) {
show_datestring("--max-age=", arg+8);
continue;
}
- if (!prefixcmp(arg, "--before=")) {
+ if (starts_with(arg, "--before=")) {
show_datestring("--min-age=", arg+9);
continue;
}
- if (!prefixcmp(arg, "--until=")) {
+ if (starts_with(arg, "--until=")) {
show_datestring("--min-age=", arg+8);
continue;
}
diff --git a/builtin/revert.c b/builtin/revert.c
index 87659c9..f9ed5bd 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -89,6 +89,8 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
N_("option for merge strategy"), option_parse_x),
+ { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
+ N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_END(),
OPT_END(),
OPT_END(),
diff --git a/builtin/rm.c b/builtin/rm.c
index 3a0e0ea..960634d 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -308,10 +308,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
- if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))
+ if (!ce_path_match(ce, &pathspec, seen))
continue;
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
- list.entry[list.nr].name = ce->name;
+ list.entry[list.nr].name = xstrdup(ce->name);
list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
if (list.entry[list.nr++].is_submodule &&
!is_staging_gitmodules_ok())
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 4482f16..f420b74 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -10,6 +10,7 @@
#include "quote.h"
#include "transport.h"
#include "version.h"
+#include "sha1-array.h"
static const char send_pack_usage[] =
"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -99,7 +100,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
const char *dest = NULL;
int fd[2];
struct child_process *conn;
- struct extra_have_objects extra_have;
+ struct sha1_array extra_have = SHA1_ARRAY_INIT;
+ struct sha1_array shallow = SHA1_ARRAY_INIT;
struct ref *remote_refs, *local_refs;
int ret;
int helper_status = 0;
@@ -115,15 +117,15 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
const char *arg = *argv;
if (*arg == '-') {
- if (!prefixcmp(arg, "--receive-pack=")) {
+ if (starts_with(arg, "--receive-pack=")) {
receivepack = arg + 15;
continue;
}
- if (!prefixcmp(arg, "--exec=")) {
+ if (starts_with(arg, "--exec=")) {
receivepack = arg + 7;
continue;
}
- if (!prefixcmp(arg, "--remote=")) {
+ if (starts_with(arg, "--remote=")) {
remote_name = arg + 9;
continue;
}
@@ -181,7 +183,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
exit(1);
continue;
}
- if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) {
+ if (starts_with(arg, "--" CAS_OPT_NAME "=")) {
if (parse_push_cas_option(&cas,
strchr(arg, '=') + 1, 0) < 0)
exit(1);
@@ -228,9 +230,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.verbose ? CONNECT_VERBOSE : 0);
}
- memset(&extra_have, 0, sizeof(extra_have));
-
- get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, &extra_have);
+ get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL,
+ &extra_have, &shallow);
transport_verify_remote_names(nr_refspecs, refspecs);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index c226f76..4b7e536 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -65,7 +65,7 @@ static void insert_one_record(struct shortlog *log,
eol = strchr(oneline, '\n');
if (!eol)
eol = oneline + strlen(oneline);
- if (!prefixcmp(oneline, "[PATCH")) {
+ if (starts_with(oneline, "[PATCH")) {
char *eob = strchr(oneline, ']');
if (eob && (!eol || eob < eol))
oneline = eob + 1;
@@ -95,7 +95,7 @@ static void read_from_stdin(struct shortlog *log)
while (fgets(author, sizeof(author), stdin) != NULL) {
if (!(author[0] == 'A' || author[0] == 'a') ||
- prefixcmp(author + 1, "uthor: "))
+ !starts_with(author + 1, "uthor: "))
continue;
while (fgets(oneline, sizeof(oneline), stdin) &&
oneline[0] != '\n')
@@ -123,7 +123,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
else
eol++;
- if (!prefixcmp(buffer, "author "))
+ if (starts_with(buffer, "author "))
author = buffer + 7;
buffer = eol;
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 001f29c..d873172 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -227,8 +227,7 @@ static void join_revs(struct commit_list **list_p,
parents = parents->next;
if ((this_flag & flags) == flags)
continue;
- if (!p->object.parsed)
- parse_commit(p);
+ parse_commit(p);
if (mark_seen(p, seen_p) && !still_interesting)
extra--;
p->object.flags |= flags;
@@ -285,7 +284,7 @@ static void show_one_commit(struct commit *commit, int no_name)
pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
pretty_str = pretty.buf;
}
- if (!prefixcmp(pretty_str, "[PATCH] "))
+ if (starts_with(pretty_str, "[PATCH] "))
pretty_str += 8;
if (!no_name) {
@@ -396,7 +395,7 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, int f
{
unsigned char tmp[20];
int ofs = 11;
- if (prefixcmp(refname, "refs/heads/"))
+ if (!starts_with(refname, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
@@ -410,7 +409,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int
{
unsigned char tmp[20];
int ofs = 13;
- if (prefixcmp(refname, "refs/remotes/"))
+ if (!starts_with(refname, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
@@ -422,7 +421,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int
static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
- if (prefixcmp(refname, "refs/tags/"))
+ if (!starts_with(refname, "refs/tags/"))
return 0;
return append_ref(refname + 5, sha1, 0);
}
@@ -451,11 +450,11 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i
slash--;
if (!*tail)
return 0;
- if (fnmatch(match_ref_pattern, tail, 0))
+ if (wildmatch(match_ref_pattern, tail, 0, NULL))
return 0;
- if (!prefixcmp(refname, "refs/heads/"))
+ if (starts_with(refname, "refs/heads/"))
return append_head_ref(refname, sha1, flag, cb_data);
- if (!prefixcmp(refname, "refs/tags/"))
+ if (starts_with(refname, "refs/tags/"))
return append_tag_ref(refname, sha1, flag, cb_data);
return append_ref(refname, sha1, 0);
}
@@ -480,11 +479,11 @@ static int rev_is_head(char *head, int headlen, char *name,
if ((!head[0]) ||
(head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
return 0;
- if (!prefixcmp(head, "refs/heads/"))
+ if (starts_with(head, "refs/heads/"))
head += 11;
- if (!prefixcmp(name, "refs/heads/"))
+ if (starts_with(name, "refs/heads/"))
name += 11;
- else if (!prefixcmp(name, "heads/"))
+ else if (starts_with(name, "heads/"))
name += 6;
return !strcmp(head, name);
}
@@ -813,7 +812,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
has_head++;
}
if (!has_head) {
- int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0;
+ int offset = starts_with(head, "refs/heads/") ? 11 : 0;
append_one_rev(head + offset);
}
}
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 9f3f5e3..5ba1f30 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -37,8 +37,8 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
if (tags_only || heads_only) {
int match;
- match = heads_only && !prefixcmp(refname, "refs/heads/");
- match |= tags_only && !prefixcmp(refname, "refs/tags/");
+ match = heads_only && starts_with(refname, "refs/heads/");
+ match |= tags_only && starts_with(refname, "refs/tags/");
if (!match)
return 0;
}
@@ -210,7 +210,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
while (*pattern) {
unsigned char sha1[20];
- if (!prefixcmp(*pattern, "refs/") &&
+ if (starts_with(*pattern, "refs/") &&
!read_ref(*pattern, sha1)) {
if (!quiet)
show_one(*pattern, sha1);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index 71286b4..b6a711d 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -65,7 +65,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
break;
case 2:
if (!strcmp(argv[0], "HEAD") &&
- prefixcmp(argv[1], "refs/"))
+ !starts_with(argv[1], "refs/"))
die("Refusing to point HEAD outside of refs/");
create_symref(argv[0], argv[1], msg);
break;
diff --git a/builtin/tag.c b/builtin/tag.c
index ea55f1d..6c7c6bd 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -27,9 +27,16 @@ static const char * const git_tag_usage[] = {
NULL
};
+#define STRCMP_SORT 0 /* must be zero */
+#define VERCMP_SORT 1
+#define SORT_MASK 0x7fff
+#define REVERSE_SORT 0x8000
+
struct tag_filter {
const char **patterns;
int lines;
+ int sort;
+ struct string_list tags;
struct commit_list *with_commit;
};
@@ -42,7 +49,7 @@ static int match_pattern(const char **patterns, const char *ref)
if (!*patterns)
return 1;
for (; *patterns; patterns++)
- if (!fnmatch(*patterns, ref, 0))
+ if (!wildmatch(*patterns, ref, 0, NULL))
return 1;
return 0;
}
@@ -166,7 +173,10 @@ static int show_reference(const char *refname, const unsigned char *sha1,
return 0;
if (!filter->lines) {
- printf("%s\n", refname);
+ if (filter->sort)
+ string_list_append(&filter->tags, refname);
+ else
+ printf("%s\n", refname);
return 0;
}
printf("%-15s ", refname);
@@ -177,17 +187,39 @@ static int show_reference(const char *refname, const unsigned char *sha1,
return 0;
}
+static int sort_by_version(const void *a_, const void *b_)
+{
+ const struct string_list_item *a = a_;
+ const struct string_list_item *b = b_;
+ return versioncmp(a->string, b->string);
+}
+
static int list_tags(const char **patterns, int lines,
- struct commit_list *with_commit)
+ struct commit_list *with_commit, int sort)
{
struct tag_filter filter;
filter.patterns = patterns;
filter.lines = lines;
+ filter.sort = sort;
filter.with_commit = with_commit;
+ memset(&filter.tags, 0, sizeof(filter.tags));
+ filter.tags.strdup_strings = 1;
for_each_tag_ref(show_reference, (void *) &filter);
-
+ if (sort) {
+ int i;
+ if ((sort & SORT_MASK) == VERCMP_SORT)
+ qsort(filter.tags.items, filter.tags.nr,
+ sizeof(struct string_list_item), sort_by_version);
+ if (sort & REVERSE_SORT)
+ for (i = filter.tags.nr - 1; i >= 0; i--)
+ printf("%s\n", filter.tags.items[i].string);
+ else
+ for (i = 0; i < filter.tags.nr; i++)
+ printf("%s\n", filter.tags.items[i].string);
+ string_list_clear(&filter.tags, 0);
+ }
return 0;
}
@@ -259,7 +291,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
int status = git_gpg_config(var, value, cb);
if (status)
return status;
- if (!prefixcmp(var, "column."))
+ if (starts_with(var, "column."))
return git_column_config(var, value, "tag", &colopts);
return git_default_config(var, value, cb);
}
@@ -427,6 +459,29 @@ static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
return 0;
}
+static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
+{
+ int *sort = opt->value;
+ int flags = 0;
+
+ if (*arg == '-') {
+ flags |= REVERSE_SORT;
+ arg++;
+ }
+ if (starts_with(arg, "version:")) {
+ *sort = VERCMP_SORT;
+ arg += 8;
+ } else if (starts_with(arg, "v:")) {
+ *sort = VERCMP_SORT;
+ arg += 2;
+ } else
+ *sort = STRCMP_SORT;
+ if (strcmp(arg, "refname"))
+ die(_("unsupported sort specification %s"), arg);
+ *sort |= flags;
+ return 0;
+}
+
int cmd_tag(int argc, const char **argv, const char *prefix)
{
struct strbuf buf = STRBUF_INIT;
@@ -437,7 +492,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct create_tag_options opt;
char *cleanup_arg = NULL;
int annotate = 0, force = 0, lines = -1;
- int cmdmode = 0;
+ int cmdmode = 0, sort = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
@@ -458,10 +513,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
N_("how to strip spaces and #comments from message")),
- OPT_STRING('u', "local-user", &keyid, N_("key id"),
+ OPT_STRING('u', "local-user", &keyid, N_("key-id"),
N_("use another key to sign the tag")),
OPT__FORCE(&force, N_("replace the tag if exists")),
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
+ {
+ OPTION_CALLBACK, 0, "sort", &sort, N_("type"), N_("sort tags"),
+ PARSE_OPT_NONEG, parse_opt_sort
+ },
OPT_GROUP(N_("Tag listing options")),
{
@@ -471,6 +530,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
parse_opt_with_commit, (intptr_t)"HEAD",
},
{
+ OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"),
+ N_("print only tags that contain the commit"),
+ PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
+ parse_opt_with_commit, (intptr_t)"HEAD",
+ },
+ {
OPTION_CALLBACK, 0, "points-at", NULL, N_("object"),
N_("print only tags of the object"), 0, parse_opt_points_at
},
@@ -509,7 +574,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
copts.padding = 2;
run_column_filter(colopts, &copts);
}
- ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
+ if (lines != -1 && sort)
+ die(_("--sort and -n are incompatible"));
+ ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, sort);
if (column_active(colopts))
stop_column_filter();
return ret;
diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c
deleted file mode 100644
index ba3ffe6..0000000
--- a/builtin/tar-tree.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2005, 2006 Rene Scharfe
- */
-#include "cache.h"
-#include "commit.h"
-#include "tar.h"
-#include "builtin.h"
-#include "quote.h"
-
-static const char tar_tree_usage[] =
-"git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
-"*** Note that this command is now deprecated; use \"git archive\" instead.";
-
-static const char builtin_get_tar_commit_id_usage[] =
-"git get-tar-commit-id < <tarfile>";
-
-int cmd_tar_tree(int argc, const char **argv, const char *prefix)
-{
- /*
- * "git tar-tree" is now a wrapper around "git archive --format=tar"
- *
- * $0 --remote=<repo> arg... ==>
- * git archive --format=tar --remote=<repo> arg...
- * $0 tree-ish ==>
- * git archive --format=tar tree-ish
- * $0 tree-ish basedir ==>
- * git archive --format-tar --prefix=basedir tree-ish
- */
- const char **nargv = xcalloc(sizeof(*nargv), argc + 3);
- struct strbuf sb = STRBUF_INIT;
- char *basedir_arg;
- int nargc = 0;
-
- nargv[nargc++] = "archive";
- nargv[nargc++] = "--format=tar";
-
- if (2 <= argc && !prefixcmp(argv[1], "--remote=")) {
- nargv[nargc++] = argv[1];
- argv++;
- argc--;
- }
-
- /*
- * Because it's just a compatibility wrapper, tar-tree supports only
- * the old behaviour of reading attributes from the work tree.
- */
- nargv[nargc++] = "--worktree-attributes";
-
- switch (argc) {
- default:
- usage(tar_tree_usage);
- break;
- case 3:
- /* base-path */
- basedir_arg = xmalloc(strlen(argv[2]) + 11);
- sprintf(basedir_arg, "--prefix=%s/", argv[2]);
- nargv[nargc++] = basedir_arg;
- /* fallthru */
- case 2:
- /* tree-ish */
- nargv[nargc++] = argv[1];
- }
- nargv[nargc] = NULL;
-
- fprintf(stderr,
- "*** \"git tar-tree\" is now deprecated.\n"
- "*** Running \"git archive\" instead.\n***");
- sq_quote_argv(&sb, nargv, 0);
- strbuf_addch(&sb, '\n');
- fputs(sb.buf, stderr);
- strbuf_release(&sb);
- return cmd_archive(nargc, nargv, prefix);
-}
-
-/* ustar header + extended global header content */
-#define RECORDSIZE (512)
-#define HEADERSIZE (2 * RECORDSIZE)
-
-int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
-{
- char buffer[HEADERSIZE];
- struct ustar_header *header = (struct ustar_header *)buffer;
- char *content = buffer + RECORDSIZE;
- ssize_t n;
-
- if (argc != 1)
- usage(builtin_get_tar_commit_id_usage);
-
- n = read_in_full(0, buffer, HEADERSIZE);
- if (n < HEADERSIZE)
- die("git get-tar-commit-id: read error");
- if (header->typeflag[0] != 'g')
- return 1;
- if (memcmp(content, "52 comment=", 11))
- return 1;
-
- n = write_in_full(1, content + 11, 41);
- if (n < 41)
- die_errno("git get-tar-commit-id: write error");
-
- return 0;
-}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 2217d7b..99cde45 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -480,7 +480,7 @@ static void unpack_all(void)
use(sizeof(struct pack_header));
if (!quiet)
- progress = start_progress("Unpacking objects", nr_objects);
+ progress = start_progress(_("Unpacking objects"), nr_objects);
obj_list = xcalloc(nr_objects, sizeof(*obj_list));
for (i = 0; i < nr_objects; i++) {
unpack_one(i);
@@ -497,7 +497,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
int i;
unsigned char sha1[20];
- read_replace_refs = 0;
+ check_replace_refs = 0;
git_config(git_default_config, NULL);
@@ -523,7 +523,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
strict = 1;
continue;
}
- if (!prefixcmp(arg, "--pack_header=")) {
+ if (starts_with(arg, "--pack_header=")) {
struct pack_header *hdr;
char *c;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index e3a10d7..ebea285 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -12,6 +12,7 @@
#include "resolve-undo.h"
#include "parse-options.h"
#include "pathspec.h"
+#include "dir.h"
/*
* Default to not allowing changes to the list of files. The
@@ -274,36 +275,32 @@ static void chmod_path(int flip, const char *path)
die("git update-index: cannot chmod %cx '%s'", flip, path);
}
-static void update_one(const char *path, const char *prefix, int prefix_length)
+static void update_one(const char *path)
{
- const char *p = prefix_path(prefix, prefix_length, path);
- if (!verify_path(p)) {
+ if (!verify_path(path)) {
fprintf(stderr, "Ignoring path %s\n", path);
- goto free_return;
+ return;
}
if (mark_valid_only) {
- if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))
+ if (mark_ce_flags(path, CE_VALID, mark_valid_only == MARK_FLAG))
die("Unable to mark file %s", path);
- goto free_return;
+ return;
}
if (mark_skip_worktree_only) {
- if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
+ if (mark_ce_flags(path, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
die("Unable to mark file %s", path);
- goto free_return;
+ return;
}
if (force_remove) {
- if (remove_file_from_cache(p))
+ if (remove_file_from_cache(path))
die("git update-index: unable to remove %s", path);
report("remove '%s'", path);
- goto free_return;
+ return;
}
- if (process_path(p))
+ if (process_path(path))
die("Unable to process path %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)
@@ -563,8 +560,9 @@ static int do_reupdate(int ac, const char **av,
const struct cache_entry *ce = active_cache[pos];
struct cache_entry *old = NULL;
int save_nr;
+ char *path;
- if (ce_stage(ce) || !ce_path_match(ce, &pathspec))
+ if (ce_stage(ce) || !ce_path_match(ce, &pathspec, NULL))
continue;
if (has_head)
old = read_one_ent(NULL, head_sha1,
@@ -579,7 +577,9 @@ static int do_reupdate(int ac, const char **av,
* or worse yet 'allow_replace', active_nr may decrease.
*/
save_nr = active_nr;
- update_one(ce->name + prefix_length, prefix, prefix_length);
+ path = xstrdup(ce->name);
+ update_one(path);
+ free(path);
if (save_nr != active_nr)
goto redo;
}
@@ -629,14 +629,45 @@ static int resolve_undo_clear_callback(const struct option *opt,
return 0;
}
+static int parse_new_style_cacheinfo(const char *arg,
+ unsigned int *mode,
+ unsigned char sha1[],
+ const char **path)
+{
+ unsigned long ul;
+ char *endp;
+
+ if (!arg)
+ return -1;
+
+ errno = 0;
+ ul = strtoul(arg, &endp, 8);
+ if (errno || endp == arg || *endp != ',' || (unsigned int) ul != ul)
+ return -1; /* not a new-style cacheinfo */
+ *mode = ul;
+ endp++;
+ if (get_sha1_hex(endp, sha1) || endp[40] != ',')
+ return -1;
+ *path = endp + 41;
+ return 0;
+}
+
static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
const struct option *opt, int unset)
{
unsigned char sha1[20];
unsigned int mode;
+ const char *path;
+ if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, sha1, &path)) {
+ if (add_cacheinfo(mode, sha1, path, 0))
+ die("git update-index: --cacheinfo cannot add %s", path);
+ ctx->argv++;
+ ctx->argc--;
+ return 0;
+ }
if (ctx->argc <= 3)
- return error("option 'cacheinfo' expects three arguments");
+ return error("option 'cacheinfo' expects <mode>,<sha1>,<path>");
if (strtoul_ui(*++ctx->argv, 8, &mode) ||
get_sha1_hex(*++ctx->argv, sha1) ||
add_cacheinfo(mode, sha1, *++ctx->argv, 0))
@@ -740,9 +771,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
really_refresh_callback},
{OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
- N_("<mode> <object> <path>"),
+ N_("<mode>,<object>,<path>"),
N_("add the specified entry to the index"),
- PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */
+ PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */
PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
(parse_opt_cb *) cacheinfo_callback},
{OPTION_CALLBACK, 0, "chmod", &set_executable_bit, N_("(+/-)x"),
@@ -836,11 +867,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
setup_work_tree();
p = prefix_path(prefix, prefix_length, path);
- update_one(p, NULL, 0);
+ update_one(p);
if (set_executable_bit)
chmod_path(set_executable_bit, p);
- if (p < path || p > path + strlen(path))
- free((char *)p);
+ free((char *)p);
ctx.argc--;
ctx.argv++;
break;
@@ -879,11 +909,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
strbuf_swap(&buf, &nbuf);
}
p = prefix_path(prefix, prefix_length, buf.buf);
- update_one(p, NULL, 0);
+ update_one(p);
if (set_executable_bit)
chmod_path(set_executable_bit, p);
- if (p < buf.buf || p > buf.buf + buf.len)
- free((char *)p);
+ free((char *)p);
}
strbuf_release(&nbuf);
strbuf_release(&buf);
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 702e90d..5c208bb 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -147,6 +147,7 @@ static void parse_cmd_create(const char *next)
struct ref_update *update;
update = update_alloc();
+ update->have_old = 1;
if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
update_store_ref_name(update, ref.buf);
@@ -229,15 +230,15 @@ static void update_refs_stdin(void)
die("empty command in input");
else if (isspace(*cmd.buf))
die("whitespace before command: %s", cmd.buf);
- else if (!prefixcmp(cmd.buf, "update "))
+ else if (starts_with(cmd.buf, "update "))
parse_cmd_update(cmd.buf + 7);
- else if (!prefixcmp(cmd.buf, "create "))
+ else if (starts_with(cmd.buf, "create "))
parse_cmd_create(cmd.buf + 7);
- else if (!prefixcmp(cmd.buf, "delete "))
+ else if (starts_with(cmd.buf, "delete "))
parse_cmd_delete(cmd.buf + 7);
- else if (!prefixcmp(cmd.buf, "verify "))
+ else if (starts_with(cmd.buf, "verify "))
parse_cmd_verify(cmd.buf + 7);
- else if (!prefixcmp(cmd.buf, "option "))
+ else if (starts_with(cmd.buf, "option "))
parse_cmd_option(cmd.buf + 7);
else
die("unknown command: %s", cmd.buf);
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index af2da35..32ab94c 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -37,7 +37,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
if (sent_argv.argc > MAX_ARGS)
die("Too many options (>%d)", MAX_ARGS - 1);
- if (prefixcmp(buf, arg_cmd))
+ if (!starts_with(buf, arg_cmd))
die("'argument' token or flush expected");
argv_array_push(&sent_argv, buf + strlen(arg_cmd));
}