summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c4
-rw-r--r--builtin/apply.c103
-rw-r--r--builtin/blame.c12
-rw-r--r--builtin/branch.c32
-rw-r--r--builtin/cat-file.c4
-rw-r--r--builtin/checkout.c43
-rw-r--r--builtin/clone.c19
-rw-r--r--builtin/commit-tree.c70
-rw-r--r--builtin/commit.c74
-rw-r--r--builtin/config.c6
-rw-r--r--builtin/describe.c49
-rw-r--r--builtin/fetch.c6
-rw-r--r--builtin/fmt-merge-msg.c76
-rw-r--r--builtin/for-each-ref.c3
-rw-r--r--builtin/grep.c10
-rw-r--r--builtin/help.c2
-rw-r--r--builtin/index-pack.c89
-rw-r--r--builtin/log.c46
-rw-r--r--builtin/ls-files.c75
-rw-r--r--builtin/ls-remote.c19
-rw-r--r--builtin/mailinfo.c4
-rw-r--r--builtin/merge-file.c2
-rw-r--r--builtin/merge-tree.c2
-rw-r--r--builtin/merge.c101
-rw-r--r--builtin/notes.c15
-rw-r--r--builtin/pack-objects.c15
-rw-r--r--builtin/patch-id.c107
-rw-r--r--builtin/receive-pack.c130
-rw-r--r--builtin/reflog.c159
-rw-r--r--builtin/remote.c134
-rw-r--r--builtin/rerere.c2
-rw-r--r--builtin/rev-parse.c3
-rw-r--r--builtin/revert.c179
-rw-r--r--builtin/shortlog.c3
34 files changed, 1045 insertions, 553 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 87d2980313..17149cfeed 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -261,12 +261,14 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = xstrdup(git_path("ADD_EDIT.patch"));
const char *apply_argv[] = { "apply", "--recount", "--cached",
- file, NULL };
+ NULL, NULL };
struct child_process child;
struct rev_info rev;
int out;
struct stat st;
+ apply_argv[3] = file;
+
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
if (read_cache() < 0)
diff --git a/builtin/apply.c b/builtin/apply.c
index 771c972c55..562e5345fc 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -56,7 +56,7 @@ static enum ws_error_action {
nowarn_ws_error,
warn_on_ws_error,
die_on_ws_error,
- correct_ws_error,
+ correct_ws_error
} ws_error_action = warn_on_ws_error;
static int whitespace_error;
static int squelch_whitespace_errors = 5;
@@ -64,7 +64,7 @@ static int applied_after_fixing_ws;
static enum ws_ignore {
ignore_ws_none,
- ignore_ws_change,
+ ignore_ws_change
} ws_ignore_action = ignore_ws_none;
@@ -1854,6 +1854,8 @@ static int match_fragment(struct image *img,
{
int i;
char *fixed_buf, *buf, *orig, *target;
+ struct strbuf fixed;
+ size_t fixed_len;
int preimage_limit;
if (preimage->nr + try_lno <= img->nr) {
@@ -1864,13 +1866,13 @@ static int match_fragment(struct image *img,
if (match_end && (preimage->nr + try_lno != img->nr))
return 0;
} else if (ws_error_action == correct_ws_error &&
- (ws_rule & WS_BLANK_AT_EOF) && match_end) {
+ (ws_rule & WS_BLANK_AT_EOF)) {
/*
- * This hunk that matches at the end extends beyond
- * the end of img, and we are removing blank lines
- * at the end of the file. This many lines from the
- * beginning of the preimage must match with img, and
- * the remainder of the preimage must be blank.
+ * This hunk extends beyond the end of img, and we are
+ * removing blank lines at the end of the file. This
+ * many lines from the beginning of the preimage must
+ * match with img, and the remainder of the preimage
+ * must be blank.
*/
preimage_limit = img->nr - try_lno;
} else {
@@ -1977,12 +1979,12 @@ static int match_fragment(struct image *img,
* use the whitespace from the preimage.
*/
extra_chars = preimage_end - preimage_eof;
- fixed_buf = xmalloc(imgoff + extra_chars);
- memcpy(fixed_buf, img->buf + try, imgoff);
- memcpy(fixed_buf + imgoff, preimage_eof, extra_chars);
- imgoff += extra_chars;
+ strbuf_init(&fixed, imgoff + extra_chars);
+ strbuf_add(&fixed, img->buf + try, imgoff);
+ strbuf_add(&fixed, preimage_eof, extra_chars);
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, imgoff, postlen);
+ fixed_buf, fixed_len, postlen);
return 1;
}
@@ -1999,27 +2001,22 @@ static int match_fragment(struct image *img,
* but in this loop we will only handle the part of the
* preimage that falls within the file.
*/
- fixed_buf = xmalloc(preimage->len + 1);
- buf = fixed_buf;
+ strbuf_init(&fixed, preimage->len + 1);
orig = preimage->buf;
target = img->buf + try;
for (i = 0; i < preimage_limit; i++) {
- size_t fixlen; /* length after fixing the preimage */
size_t oldlen = preimage->line[i].len;
size_t tgtlen = img->line[try_lno + i].len;
- size_t tgtfixlen; /* length after fixing the target line */
- char tgtfixbuf[1024], *tgtfix;
+ size_t fixstart = fixed.len;
+ struct strbuf tgtfix;
int match;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
/* Try fixing the line in the target */
- if (sizeof(tgtfixbuf) > tgtlen)
- tgtfix = tgtfixbuf;
- else
- tgtfix = xmalloc(tgtlen);
- tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+ strbuf_init(&tgtfix, tgtlen);
+ ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
/*
* If they match, either the preimage was based on
@@ -2031,15 +2028,15 @@ static int match_fragment(struct image *img,
* so we might as well take the fix together with their
* real change.
*/
- match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+ match = (tgtfix.len == fixed.len - fixstart &&
+ !memcmp(tgtfix.buf, fixed.buf + fixstart,
+ fixed.len - fixstart));
- if (tgtfix != tgtfixbuf)
- free(tgtfix);
+ strbuf_release(&tgtfix);
if (!match)
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
target += tgtlen;
}
@@ -2051,19 +2048,18 @@ static int match_fragment(struct image *img,
* false).
*/
for ( ; i < preimage->nr; i++) {
- size_t fixlen; /* length after fixing the preimage */
+ size_t fixstart = fixed.len; /* start of the fixed preimage */
size_t oldlen = preimage->line[i].len;
int j;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
- for (j = 0; j < fixlen; j++)
- if (!isspace(buf[j]))
+ for (j = fixstart; j < fixed.len; j++)
+ if (!isspace(fixed.buf[j]))
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
}
/*
@@ -2071,12 +2067,13 @@ static int match_fragment(struct image *img,
* has whitespace breakages unfixed, and fixing them makes the
* hunk match. Update the context lines in the postimage.
*/
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, buf - fixed_buf, 0);
+ fixed_buf, fixed_len, 0);
return 1;
unmatch_exit:
- free(fixed_buf);
+ strbuf_release(&fixed);
return 0;
}
@@ -2244,7 +2241,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
int match_beginning, match_end;
const char *patch = frag->patch;
int size = frag->size;
- char *old, *new, *oldlines, *newlines;
+ char *old, *oldlines;
+ struct strbuf newlines;
int new_blank_lines_at_end = 0;
unsigned long leading, trailing;
int pos, applied_pos;
@@ -2254,16 +2252,16 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
memset(&preimage, 0, sizeof(preimage));
memset(&postimage, 0, sizeof(postimage));
oldlines = xmalloc(size);
- newlines = xmalloc(size);
+ strbuf_init(&newlines, size);
old = oldlines;
- new = newlines;
while (size > 0) {
char first;
int len = linelen(patch, size);
- int plen, added;
+ int plen;
int added_blank_line = 0;
int is_blank_context = 0;
+ size_t start;
if (!len)
break;
@@ -2293,7 +2291,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
/* ... followed by '\No newline'; nothing */
break;
*old++ = '\n';
- *new++ = '\n';
+ strbuf_addch(&newlines, '\n');
add_line_info(&preimage, "\n", 1, LINE_COMMON);
add_line_info(&postimage, "\n", 1, LINE_COMMON);
is_blank_context = 1;
@@ -2315,18 +2313,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
if (first == '+' && no_add)
break;
+ start = newlines.len;
if (first != '+' ||
!whitespace_error ||
ws_error_action != correct_ws_error) {
- memcpy(new, patch + 1, plen);
- added = plen;
+ strbuf_add(&newlines, patch + 1, plen);
}
else {
- added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+ ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
}
- add_line_info(&postimage, new, added,
+ add_line_info(&postimage, newlines.buf + start, newlines.len - start,
(first == '+' ? 0 : LINE_COMMON));
- new += added;
if (first == '+' &&
(ws_rule & WS_BLANK_AT_EOF) &&
ws_blank_line(patch + 1, plen, ws_rule))
@@ -2351,9 +2348,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
if (inaccurate_eof &&
old > oldlines && old[-1] == '\n' &&
- new > newlines && new[-1] == '\n') {
+ newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
old--;
- new--;
+ strbuf_setlen(&newlines, newlines.len - 1);
}
leading = frag->leading;
@@ -2385,8 +2382,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
pos = frag->newpos ? (frag->newpos - 1) : 0;
preimage.buf = oldlines;
preimage.len = old - oldlines;
- postimage.buf = newlines;
- postimage.len = new - newlines;
+ postimage.buf = newlines.buf;
+ postimage.len = newlines.len;
preimage.line = preimage.line_allocated;
postimage.line = postimage.line_allocated;
@@ -2462,7 +2459,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
free(oldlines);
- free(newlines);
+ strbuf_release(&newlines);
free(preimage.line_allocated);
free(postimage.line_allocated);
@@ -3141,11 +3138,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
die("unable to remove %s from index", patch->old_name);
}
if (!cached) {
- if (S_ISGITLINK(patch->old_mode)) {
- if (rmdir(patch->old_name))
- warning("unable to remove submodule %s",
- patch->old_name);
- } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
+ if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
remove_path(patch->old_name);
}
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 4dd4c3f494..729b43058a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -39,7 +39,7 @@ static int show_root;
static int reverse;
static int blank_boundary;
static int incremental;
-static int xdl_opts = XDF_NEED_MINIMAL;
+static int xdl_opts;
static enum date_mode blame_date_mode = DATE_ISO8601;
static size_t blame_date_width;
@@ -733,10 +733,11 @@ static int pass_blame_to_parent(struct scoreboard *sb,
{
int last_in_target;
mmfile_t file_p, file_o;
- struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+ struct blame_chunk_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.target = target; d.parent = parent;
last_in_target = find_last_in_target(sb, target);
if (last_in_target < 0)
return 1; /* nothing remains for this target */
@@ -875,10 +876,11 @@ static void find_copy_in_blob(struct scoreboard *sb,
const char *cp;
int cnt;
mmfile_t file_o;
- struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+ struct handle_split_cb_data d;
xpparam_t xpp;
xdemitconf_t xecfg;
-
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
/*
* Prepare mmfile that contains only the lines in ent.
*/
diff --git a/builtin/branch.c b/builtin/branch.c
index 6cf7e721e6..87976f0921 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -43,13 +43,13 @@ enum color_branch {
BRANCH_COLOR_PLAIN = 1,
BRANCH_COLOR_REMOTE = 2,
BRANCH_COLOR_LOCAL = 3,
- BRANCH_COLOR_CURRENT = 4,
+ BRANCH_COLOR_CURRENT = 4
};
static enum merge_filter {
NO_FILTER = 0,
SHOW_NOT_MERGED,
- SHOW_MERGED,
+ SHOW_MERGED
} merge_filter;
static unsigned char merge_filter_ref[20];
@@ -257,9 +257,15 @@ static char *resolve_symref(const char *src, const char *prefix)
return xstrdup(dst);
}
+struct append_ref_cb {
+ struct ref_list *ref_list;
+ int ret;
+};
+
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
- struct ref_list *ref_list = (struct ref_list*)(cb_data);
+ struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
+ struct ref_list *ref_list = cb->ref_list;
struct ref_item *newitem;
struct commit *commit;
int kind, i;
@@ -293,8 +299,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
commit = NULL;
if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
commit = lookup_commit_reference_gently(sha1, 1);
- if (!commit)
- return error("branch '%s' does not point at a commit", refname);
+ if (!commit) {
+ cb->ret = error("branch '%s' does not point at a commit", refname);
+ return 0;
+ }
/* Filter with with_commit if specified */
if (!is_descendant_of(commit, ref_list->with_commit))
@@ -484,9 +492,10 @@ static void show_detached(struct ref_list *ref_list)
}
}
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
{
int i;
+ struct append_ref_cb cb;
struct ref_list ref_list;
memset(&ref_list, 0, sizeof(ref_list));
@@ -496,7 +505,9 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
ref_list.with_commit = with_commit;
if (merge_filter != NO_FILTER)
init_revisions(&ref_list.revs, NULL);
- for_each_rawref(append_ref, &ref_list);
+ cb.ref_list = &ref_list;
+ cb.ret = 0;
+ for_each_rawref(append_ref, &cb);
if (merge_filter != NO_FILTER) {
struct commit *filter;
filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -527,6 +538,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
}
free_ref_list(&ref_list);
+
+ if (cb.ret)
+ error("some refs could not be read");
+
+ return cb.ret;
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -679,7 +695,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
else if (argc == 0)
- print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+ return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
else if (rename && (argc == 1))
rename_branch(head, argv[0], rename > 1);
else if (rename && (argc == 2))
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index a933eaa043..e5118c57da 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -118,7 +118,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
/* custom pretty-print here */
if (type == OBJ_TREE) {
- const char *ls_args[3] = {"ls-tree", obj_name, NULL};
+ const char *ls_args[3] = { NULL };
+ ls_args[0] = "ls-tree";
+ ls_args[1] = obj_name;
return cmd_ls_tree(2, ls_args, NULL);
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 88b1f43e05..1994be92c6 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -33,6 +33,7 @@ struct checkout_opts {
int writeout_error;
const char *new_branch;
+ const char *new_orphan_branch;
int new_branch_log;
enum branch_track track;
};
@@ -492,8 +493,26 @@ static void update_refs_for_switch(struct checkout_opts *opts,
struct strbuf msg = STRBUF_INIT;
const char *old_desc;
if (opts->new_branch) {
- create_branch(old->name, opts->new_branch, new->name, 0,
- opts->new_branch_log, opts->track);
+ if (opts->new_orphan_branch) {
+ if (opts->new_branch_log && !log_all_ref_updates) {
+ int temp;
+ char log_file[PATH_MAX];
+ char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+
+ temp = log_all_ref_updates;
+ log_all_ref_updates = 1;
+ if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+ fprintf(stderr, "Can not do reflog for '%s'\n",
+ opts->new_orphan_branch);
+ log_all_ref_updates = temp;
+ return;
+ }
+ log_all_ref_updates = temp;
+ }
+ }
+ else
+ create_branch(old->name, opts->new_branch, new->name, 0,
+ opts->new_branch_log, opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
}
@@ -515,6 +534,14 @@ static void update_refs_for_switch(struct checkout_opts *opts,
opts->new_branch ? " a new" : "",
new->name);
}
+ if (old->path && old->name) {
+ char log_file[PATH_MAX], ref_file[PATH_MAX];
+
+ git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
+ git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
+ if (!file_exists(ref_file) && file_exists(log_file))
+ remove_path(log_file);
+ }
} else if (strcmp(new->name, "HEAD")) {
update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
REF_NODEREF, DIE_ON_ERR);
@@ -609,7 +636,8 @@ static int check_tracking_name(const char *refname, const unsigned char *sha1,
static const char *unique_tracking_name(const char *name)
{
- struct tracking_name_data cb_data = { name, NULL, 1 };
+ struct tracking_name_data cb_data = { NULL, NULL, 1 };
+ cb_data.name = name;
for_each_ref(check_tracking_name, &cb_data);
if (cb_data.unique)
return cb_data.remote;
@@ -633,6 +661,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
+ OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
@@ -678,6 +707,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.new_branch = argv0 + 1;
}
+ if (opts.new_orphan_branch) {
+ if (opts.new_branch)
+ die("--orphan and -b are mutually exclusive");
+ if (opts.track > 0)
+ die("--orphan cannot be used with -t");
+ opts.new_branch = opts.new_orphan_branch;
+ }
+
if (conflict_style) {
opts.merge = 1; /* implied */
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
diff --git a/builtin/clone.c b/builtin/clone.c
index 0bedde41f0..efb1e6faa5 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -464,7 +464,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
set_git_dir(make_absolute_path(git_dir));
if (0 <= option_verbosity)
- printf("Cloning into %s...\n", get_git_dir());
+ printf("Cloning into %s%s...\n",
+ option_bare ? "bare repository " : "", dir);
init_db(option_template, INIT_DB_QUIET);
/*
@@ -474,9 +475,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
*/
unsetenv(CONFIG_ENVIRONMENT);
- if (option_reference)
- setup_reference(git_dir);
-
git_config(git_default_config, NULL);
if (option_bare) {
@@ -502,12 +500,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set(key.buf, "true");
strbuf_reset(&key);
}
-
- strbuf_addf(&key, "remote.%s.url", option_origin);
- git_config_set(key.buf, repo);
- strbuf_reset(&key);
}
+ strbuf_addf(&key, "remote.%s.url", option_origin);
+ git_config_set(key.buf, repo);
+ strbuf_reset(&key);
+
+ if (option_reference)
+ setup_reference(git_dir);
+
fetch_pattern = value.buf;
refspec = parse_fetch_refspec(1, &fetch_pattern);
@@ -517,7 +518,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refs = clone_local(path, git_dir);
mapped_refs = wanted_peer_refs(refs, refspec);
} else {
- struct remote *remote = remote_get(argv[0]);
+ struct remote *remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
if (!transport->get_refs_list || !transport->fetch)
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 90dac349a3..87f0591c2f 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -9,19 +9,6 @@
#include "builtin.h"
#include "utf8.h"
-/*
- * FIXME! Share the code with "write-tree.c"
- */
-static void check_valid(unsigned char *sha1, enum object_type expect)
-{
- enum object_type type = sha1_object_info(sha1, NULL);
- if (type < 0)
- die("%s is not a valid object", sha1_to_hex(sha1));
- if (type != expect)
- die("%s is not a valid '%s' object", sha1_to_hex(sha1),
- typename(expect));
-}
-
static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
static void new_parent(struct commit *parent, struct commit_list **parents_p)
@@ -38,61 +25,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
commit_list_insert(parent, parents_p);
}
-static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n";
-
-int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author)
-{
- int result;
- int encoding_is_utf8;
- struct strbuf buffer;
-
- check_valid(tree, OBJ_TREE);
-
- /* Not having i18n.commitencoding is the same as having utf-8 */
- encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
-
- strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
- strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
-
- /*
- * NOTE! This ordering means that the same exact tree merged with a
- * different order of parents will be a _different_ changeset even
- * if everything else stays the same.
- */
- while (parents) {
- struct commit_list *next = parents->next;
- strbuf_addf(&buffer, "parent %s\n",
- sha1_to_hex(parents->item->object.sha1));
- free(parents);
- parents = next;
- }
-
- /* Person/date information */
- if (!author)
- author = git_author_info(IDENT_ERROR_ON_NO_NAME);
- strbuf_addf(&buffer, "author %s\n", author);
- strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
- if (!encoding_is_utf8)
- strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
- strbuf_addch(&buffer, '\n');
-
- /* And add the comment */
- strbuf_addstr(&buffer, msg);
-
- /* And check the encoding */
- if (encoding_is_utf8 && !is_utf8(buffer.buf))
- fprintf(stderr, commit_utf8_warn);
-
- result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
- strbuf_release(&buffer);
- return result;
-}
-
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
int i;
@@ -117,7 +49,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (get_sha1(b, sha1))
die("Not a valid object name %s", b);
- check_valid(sha1, OBJ_COMMIT);
+ assert_sha1_type(sha1, OBJ_COMMIT);
new_parent(lookup_commit(sha1), &parents);
}
diff --git a/builtin/commit.c b/builtin/commit.c
index 216ecc91d1..5e4f0f33b7 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -62,7 +62,7 @@ static struct lock_file false_lock; /* used only for partial commits */
static enum {
COMMIT_AS_IS = 1,
COMMIT_NORMAL,
- COMMIT_PARTIAL,
+ COMMIT_PARTIAL
} commit_style;
static const char *logfile, *force_author;
@@ -71,7 +71,7 @@ static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
-static int no_post_rewrite;
+static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date;
/*
* The default commit message cleanup mode will remove the lines
@@ -83,11 +83,12 @@ static char *untracked_files_arg, *force_date;
static enum {
CLEANUP_SPACE,
CLEANUP_NONE,
- CLEANUP_ALL,
+ CLEANUP_ALL
} cleanup_mode;
static char *cleanup_arg;
static int use_editor = 1, initial_commit, in_merge, include_status = 1;
+static int show_ignored_in_status;
static const char *only_include_assumed;
static struct strbuf message;
@@ -95,8 +96,9 @@ static int null_termination;
static enum {
STATUS_FORMAT_LONG,
STATUS_FORMAT_SHORT,
- STATUS_FORMAT_PORCELAIN,
+ STATUS_FORMAT_PORCELAIN
} status_format = STATUS_FORMAT_LONG;
+static int status_show_branch;
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
{
@@ -138,6 +140,7 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
OPT_SET_INT(0, "short", &status_format, "show status concisely",
STATUS_FORMAT_SHORT),
+ OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
OPT_SET_INT(0, "porcelain", &status_format,
"show porcelain output format", STATUS_FORMAT_PORCELAIN),
OPT_BOOLEAN('z', "null", &null_termination,
@@ -145,9 +148,15 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
/* end commit contents options */
+ { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+ "ok to record an empty change",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+ { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+ "ok to record a change with an empty message",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+
OPT_END()
};
@@ -422,7 +431,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(s, null_termination);
+ wt_shortstatus_print(s, null_termination, status_show_branch);
break;
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(s, null_termination);
@@ -460,15 +469,21 @@ static void determine_author_info(void)
if (!a)
die("invalid commit: %s", use_message);
- lb = strstr(a + 8, " <");
- rb = strstr(a + 8, "> ");
- eol = strchr(a + 8, '\n');
- if (!lb || !rb || !eol)
+ lb = strchrnul(a + strlen("\nauthor "), '<');
+ rb = strchrnul(lb, '>');
+ eol = strchrnul(rb, '\n');
+ if (!*lb || !*rb || !*eol)
die("invalid commit: %s", use_message);
- name = xstrndup(a + 8, lb - (a + 8));
- email = xstrndup(lb + 2, rb - (lb + 2));
- date = xstrndup(rb + 2, eol - (rb + 2));
+ if (lb == a + strlen("\nauthor "))
+ /* \nauthor <foo@example.com> */
+ name = xcalloc(1, 1);
+ else
+ name = xmemdupz(a + strlen("\nauthor "),
+ (lb - strlen(" ") -
+ (a + strlen("\nauthor "))));
+ email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
+ date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
}
if (force_author) {
@@ -724,7 +739,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (use_editor) {
char index[PATH_MAX];
- const char *env[2] = { index, NULL };
+ const char *env[2] = { NULL };
+ env[0] = index;
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
if (launch_editor(git_path(commit_editmsg), NULL, env)) {
fprintf(stderr,
@@ -1024,11 +1040,14 @@ static int git_status_config(const char *k, const char *v, void *cb)
int cmd_status(int argc, const char **argv, const char *prefix)
{
struct wt_status s;
+ int fd;
unsigned char sha1[20];
static struct option builtin_status_options[] = {
OPT__VERBOSE(&verbose),
OPT_SET_INT('s', "short", &status_format,
"show status concisely", STATUS_FORMAT_SHORT),
+ OPT_BOOLEAN('b', "branch", &status_show_branch,
+ "show branch information"),
OPT_SET_INT(0, "porcelain", &status_format,
"show porcelain output format",
STATUS_FORMAT_PORCELAIN),
@@ -1038,6 +1057,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
"mode",
"show untracked files, optional modes: all, normal, no. (Default: all)",
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
+ "show ignored files"),
OPT_END(),
};
@@ -1051,12 +1072,21 @@ int cmd_status(int argc, const char **argv, const char *prefix)
builtin_status_options,
builtin_status_usage, 0);
handle_untracked_files_arg(&s);
-
+ if (show_ignored_in_status)
+ s.show_ignored_files = 1;
if (*argv)
s.pathspec = get_pathspec(prefix, argv);
read_cache_preload(s.pathspec);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
+
+ fd = hold_locked_index(&index_lock, 0);
+ if (0 <= fd) {
+ if (!write_cache(fd, active_cache, active_nr))
+ commit_locked_index(&index_lock);
+ rollback_lock_file(&index_lock);
+ }
+
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.in_merge = in_merge;
wt_status_collect(&s);
@@ -1070,7 +1100,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
switch (status_format) {
case STATUS_FORMAT_SHORT:
- wt_shortstatus_print(&s, null_termination);
+ wt_shortstatus_print(&s, null_termination, status_show_branch);
break;
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(&s, null_termination);
@@ -1145,13 +1175,11 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
initial_commit ? " (root-commit)" : "");
if (!log_tree_commit(&rev, commit)) {
- struct pretty_print_context ctx = {0};
- struct strbuf buf = STRBUF_INIT;
- ctx.date_mode = DATE_NORMAL;
- format_commit_message(commit, format.buf + 7, &buf, &ctx);
- printf("%s\n", buf.buf);
- strbuf_release(&buf);
+ rev.always_show_header = 1;
+ rev.use_terminator = 1;
+ log_tree_commit(&rev, commit);
}
+
strbuf_release(&format);
}
@@ -1300,7 +1328,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (cleanup_mode != CLEANUP_NONE)
stripspace(&sb, cleanup_mode == CLEANUP_ALL);
- if (message_is_empty(&sb)) {
+ if (message_is_empty(&sb) && !allow_empty_message) {
rollback_index_files();
fprintf(stderr, "Aborting commit due to empty commit message.\n");
exit(1);
diff --git a/builtin/config.c b/builtin/config.c
index 4bc46b15fd..f3d1660d02 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -197,7 +197,11 @@ static int get_value(const char *key_, const char *regex_)
git_config_from_file(show_config, system_wide, NULL);
if (do_all && global)
git_config_from_file(show_config, global, NULL);
- git_config_from_file(show_config, local, NULL);
+ if (do_all)
+ git_config_from_file(show_config, local, NULL);
+ git_config_from_parameters(show_config, NULL);
+ if (!do_all && !seen)
+ git_config_from_file(show_config, local, NULL);
if (!do_all && !seen && global)
git_config_from_file(show_config, global, NULL);
if (!do_all && !seen && system_wide)
diff --git a/builtin/describe.c b/builtin/describe.c
index 71be2a9364..43caff2ffe 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -35,7 +35,8 @@ static const char *diff_index_args[] = {
struct commit_name {
struct tag *tag;
- int prio; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned name_checked:1;
unsigned char sha1[20];
char path[FLEX_ARRAY]; /* more */
};
@@ -43,18 +44,53 @@ static const char *prio_names[] = {
"head", "lightweight", "annotated",
};
+static int replace_name(struct commit_name *e,
+ int prio,
+ const unsigned char *sha1,
+ struct tag **tag)
+{
+ if (!e || e->prio < prio)
+ return 1;
+
+ if (e->prio == 2 && prio == 2) {
+ /* Multiple annotated tags point to the same commit.
+ * Select one to keep based upon their tagger date.
+ */
+ struct tag *t;
+
+ if (!e->tag) {
+ t = lookup_tag(e->sha1);
+ if (!t || parse_tag(t))
+ return 1;
+ e->tag = t;
+ }
+
+ t = lookup_tag(sha1);
+ if (!t || parse_tag(t))
+ return 0;
+ *tag = t;
+
+ if (e->tag->date < t->date)
+ return 1;
+ }
+
+ return 0;
+}
+
static void add_to_known_names(const char *path,
struct commit *commit,
int prio,
const unsigned char *sha1)
{
struct commit_name *e = commit->util;
- if (!e || e->prio < prio) {
+ struct tag *tag = NULL;
+ if (replace_name(e, prio, sha1, &tag)) {
size_t len = strlen(path)+1;
free(e);
e = xmalloc(sizeof(struct commit_name) + len);
- e->tag = NULL;
+ e->tag = tag;
e->prio = prio;
+ e->name_checked = 0;
hashcpy(e->sha1, sha1);
memcpy(e->path, path, len);
commit->util = e;
@@ -165,10 +201,15 @@ static void display_name(struct commit_name *n)
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(n->sha1);
- if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
+ if (!n->tag || parse_tag(n->tag))
die("annotated tag %s not available", n->path);
+ }
+ if (n->tag && !n->name_checked) {
+ if (!n->tag->tag)
+ die("annotated tag %s has no embedded name", n->path);
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
+ n->name_checked = 1;
}
if (n->tag)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 8470850415..5cb369cfd1 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -574,9 +574,10 @@ static void find_non_local_tags(struct transport *transport,
{
struct string_list existing_refs = { NULL, 0, 0, 0 };
struct string_list remote_refs = { NULL, 0, 0, 0 };
- struct tag_data data = {head, tail};
+ struct tag_data data;
const struct ref *ref;
struct string_list_item *item = NULL;
+ data.head = head; data.tail = tail;
for_each_ref(add_existing, &existing_refs);
for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -778,7 +779,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
static int add_remote_or_group(const char *name, struct string_list *list)
{
int prev_nr = list->nr;
- struct remote_group_data g = { name, list };
+ struct remote_group_data g;
+ g.name = name; g.list = list;
git_config(get_remote_group, &g);
if (list->nr == prev_nr) {
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 379a03131f..44204257c7 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -202,35 +202,10 @@ static void shortlog(const char *name, unsigned char *sha1,
string_list_clear(&subjects, 0);
}
-int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
- int limit = 20, i = 0, pos = 0;
+static void do_fmt_merge_msg_title(struct strbuf *out,
+ const char *current_branch) {
+ int i = 0;
char *sep = "";
- unsigned char head_sha1[20];
- const char *current_branch;
-
- /* get current branch */
- current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
- if (!current_branch)
- die("No current branch");
- if (!prefixcmp(current_branch, "refs/heads/"))
- current_branch += 11;
-
- /* get a line */
- while (pos < in->len) {
- int len;
- char *newline, *p = in->buf + pos;
-
- newline = strchr(p, '\n');
- len = newline ? newline - p : strlen(p);
- pos += len + !!newline;
- i++;
- p[len] = 0;
- if (handle_line(p))
- die ("Error in line %d: %.*s", i, len, p);
- }
-
- if (!srcs.nr)
- return 0;
strbuf_addstr(out, "Merge ");
for (i = 0; i < srcs.nr; i++) {
@@ -278,6 +253,40 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
strbuf_addch(out, '\n');
else
strbuf_addf(out, " into %s\n", current_branch);
+}
+
+static int do_fmt_merge_msg(int merge_title, int merge_summary,
+ struct strbuf *in, struct strbuf *out) {
+ int limit = 20, i = 0, pos = 0;
+ unsigned char head_sha1[20];
+ const char *current_branch;
+
+ /* get current branch */
+ current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+ if (!current_branch)
+ die("No current branch");
+ if (!prefixcmp(current_branch, "refs/heads/"))
+ current_branch += 11;
+
+ /* get a line */
+ while (pos < in->len) {
+ int len;
+ char *newline, *p = in->buf + pos;
+
+ newline = strchr(p, '\n');
+ len = newline ? newline - p : strlen(p);
+ pos += len + !!newline;
+ i++;
+ p[len] = 0;
+ if (handle_line(p))
+ die ("Error in line %d: %.*s", i, len, p);
+ }
+
+ if (!srcs.nr)
+ return 0;
+
+ if (merge_title)
+ do_fmt_merge_msg_title(out, current_branch);
if (merge_summary) {
struct commit *head;
@@ -289,6 +298,9 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
rev.ignore_merges = 1;
rev.limited = 1;
+ if (suffixcmp(out->buf, "\n"))
+ strbuf_addch(out, '\n');
+
for (i = 0; i < origins.nr; i++)
shortlog(origins.items[i].string, origins.items[i].util,
head, &rev, limit, out);
@@ -296,6 +308,14 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
return 0;
}
+int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
+ return do_fmt_merge_msg(1, merge_summary, in, out);
+}
+
+int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
+ return do_fmt_merge_msg(0, 1, in, out);
+}
+
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
const char *inpath = NULL;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 7f5011f75e..a2b28c6962 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -227,6 +227,9 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
strcpy(s, sha1_to_hex(obj->sha1));
v->s = s;
}
+ else if (!strcmp(name, "objectname:short")) {
+ v->s = find_unique_abbrev(obj->sha1, DEFAULT_ABBREV);
+ }
}
}
diff --git a/builtin/grep.c b/builtin/grep.c
index 8e928e2170..d0a73da07a 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -17,8 +17,8 @@
#include "dir.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static char const * const grep_usage[] = {
@@ -724,11 +724,15 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
if (!patterns)
die_errno("cannot open '%s'", arg);
while (strbuf_getline(&sb, patterns, '\n') == 0) {
+ char *s;
+ size_t len;
+
/* ignore empty line like grep does */
if (sb.len == 0)
continue;
- append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
- ++lno, GREP_PATTERN);
+
+ s = strbuf_detach(&sb, &len);
+ append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN);
}
fclose(patterns);
strbuf_release(&sb);
diff --git a/builtin/help.c b/builtin/help.c
index 3182a2bec4..a9836b00ae 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -26,7 +26,7 @@ enum help_format {
HELP_FORMAT_NONE,
HELP_FORMAT_MAN,
HELP_FORMAT_INFO,
- HELP_FORMAT_WEB,
+ HELP_FORMAT_WEB
};
static int show_all = 0;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 03d0cd2f47..a89ae831dd 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -266,26 +266,23 @@ static void unlink_base_data(struct base_data *c)
static void *unpack_entry_data(unsigned long offset, unsigned long size)
{
+ int status;
z_stream stream;
void *buf = xmalloc(size);
memset(&stream, 0, sizeof(stream));
+ git_inflate_init(&stream);
stream.next_out = buf;
stream.avail_out = size;
- stream.next_in = fill(1);
- stream.avail_in = input_len;
- git_inflate_init(&stream);
- for (;;) {
- int ret = git_inflate(&stream, 0);
- use(input_len - stream.avail_in);
- if (stream.total_out == size && ret == Z_STREAM_END)
- break;
- if (ret != Z_OK)
- bad_object(offset, "inflate returned %d", ret);
+ do {
stream.next_in = fill(1);
stream.avail_in = input_len;
- }
+ status = git_inflate(&stream, 0);
+ use(input_len - stream.avail_in);
+ } while (status == Z_OK);
+ if (stream.total_out != size || status != Z_STREAM_END)
+ bad_object(offset, "inflate returned %d", status);
git_inflate_end(&stream);
return buf;
}
@@ -359,34 +356,38 @@ static void *get_data_from_pack(struct object_entry *obj)
{
off_t from = obj[0].idx.offset + obj[0].hdr_size;
unsigned long len = obj[1].idx.offset - from;
- unsigned long rdy = 0;
- unsigned char *src, *data;
+ unsigned char *data, *inbuf;
z_stream stream;
- int st;
+ int status;
- src = xmalloc(len);
- data = src;
- do {
- ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
- if (n < 0)
- die_errno("cannot pread pack file");
- if (!n)
- die("premature end of pack file, %lu bytes missing",
- len - rdy);
- rdy += n;
- } while (rdy < len);
data = xmalloc(obj->size);
+ inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+
memset(&stream, 0, sizeof(stream));
+ git_inflate_init(&stream);
stream.next_out = data;
stream.avail_out = obj->size;
- stream.next_in = src;
- stream.avail_in = len;
- git_inflate_init(&stream);
- while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK);
- git_inflate_end(&stream);
- if (st != Z_STREAM_END || stream.total_out != obj->size)
+
+ do {
+ ssize_t n = (len < 64*1024) ? len : 64*1024;
+ n = pread(pack_fd, inbuf, n, from);
+ if (n < 0)
+ die_errno("cannot pread pack file");
+ if (!n)
+ die("premature end of pack file, %lu bytes missing", len);
+ from += n;
+ len -= n;
+ stream.next_in = inbuf;
+ stream.avail_in = n;
+ status = git_inflate(&stream, 0);
+ } while (len && status == Z_OK && !stream.avail_in);
+
+ /* This has been inflated OK when first encountered, so... */
+ if (status != Z_STREAM_END || stream.total_out != obj->size)
die("serious inflate inconsistency");
- free(src);
+
+ git_inflate_end(&stream);
+ free(inbuf);
return data;
}
@@ -668,25 +669,25 @@ static void parse_pack_objects(unsigned char *sha1)
static int write_compressed(struct sha1file *f, void *in, unsigned int size)
{
z_stream stream;
- unsigned long maxsize;
- void *out;
+ int status;
+ unsigned char outbuf[4096];
memset(&stream, 0, sizeof(stream));
deflateInit(&stream, zlib_compression_level);
- maxsize = deflateBound(&stream, size);
- out = xmalloc(maxsize);
-
- /* Compress it */
stream.next_in = in;
stream.avail_in = size;
- stream.next_out = out;
- stream.avail_out = maxsize;
- while (deflate(&stream, Z_FINISH) == Z_OK);
- deflateEnd(&stream);
+ do {
+ stream.next_out = outbuf;
+ stream.avail_out = sizeof(outbuf);
+ status = deflate(&stream, Z_FINISH);
+ sha1write(f, outbuf, sizeof(outbuf) - stream.avail_out);
+ } while (status == Z_OK);
+
+ if (status != Z_STREAM_END)
+ die("unable to deflate appended object (%d)", status);
size = stream.total_out;
- sha1write(f, out, size);
- free(out);
+ deflateEnd(&stream);
return size;
}
diff --git a/builtin/log.c b/builtin/log.c
index 6208703c06..976e16f9f2 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -24,6 +24,7 @@
static const char *default_date_mode = NULL;
static int default_show_root = 1;
+static int decoration_style;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
@@ -31,11 +32,28 @@ static const char * const builtin_log_usage =
"git log [<options>] [<since>..<until>] [[--] <path>...]\n"
" or: git show [options] <object>...";
+static int parse_decoration_style(const char *var, const char *value)
+{
+ switch (git_config_maybe_bool(var, value)) {
+ case 1:
+ return DECORATE_SHORT_REFS;
+ case 0:
+ return 0;
+ default:
+ break;
+ }
+ if (!strcmp(value, "full"))
+ return DECORATE_FULL_REFS;
+ else if (!strcmp(value, "short"))
+ return DECORATE_SHORT_REFS;
+ return -1;
+}
+
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
int i;
- int decoration_style = 0;
+ int decoration_given = 0;
struct userformat_want w;
rev->abbrev = DEFAULT_ABBREV;
@@ -78,14 +96,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
const char *arg = argv[i];
if (!strcmp(arg, "--decorate")) {
decoration_style = DECORATE_SHORT_REFS;
+ decoration_given = 1;
} else if (!prefixcmp(arg, "--decorate=")) {
const char *v = skip_prefix(arg, "--decorate=");
- if (!strcmp(v, "full"))
- decoration_style = DECORATE_FULL_REFS;
- else if (!strcmp(v, "short"))
- decoration_style = DECORATE_SHORT_REFS;
- else
+ decoration_style = parse_decoration_style(arg, v);
+ if (decoration_style < 0)
die("invalid --decorate option: %s", arg);
+ decoration_given = 1;
+ } else if (!strcmp(arg, "--no-decorate")) {
+ decoration_style = 0;
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
} else if (!strcmp(arg, "-h")) {
@@ -93,6 +112,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
} else
die("unrecognized argument: %s", arg);
}
+
+ /*
+ * defeat log.decorate configuration interacting with --pretty=raw
+ * from the command line.
+ */
+ if (!decoration_given && rev->pretty_given
+ && rev->commit_format == CMIT_FMT_RAW)
+ decoration_style = 0;
+
if (decoration_style) {
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
@@ -258,6 +286,12 @@ static int git_log_config(const char *var, const char *value, void *cb)
return git_config_string(&fmt_patch_subject_prefix, var, value);
if (!strcmp(var, "log.date"))
return git_config_string(&default_date_mode, var, value);
+ if (!strcmp(var, "log.decorate")) {
+ decoration_style = parse_decoration_style(var, value);
+ if (decoration_style < 0)
+ decoration_style = 0; /* maybe warn? */
+ return 0;
+ }
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index c0fbcdcf4f..0804047693 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -26,8 +26,9 @@ static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
+static const char *prefix;
+static int max_prefix_len;
static int prefix_len;
-static int prefix_offset;
static const char **pathspec;
static int error_unmatch;
static char *ps_matched;
@@ -43,10 +44,15 @@ static const char *tag_modified = "";
static const char *tag_skip_worktree = "";
static const char *tag_resolve_undo = "";
+static void write_name(const char* name, size_t len)
+{
+ write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
+ line_terminator);
+}
+
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
- int len = prefix_len;
- int offset = prefix_offset;
+ int len = max_prefix_len;
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
@@ -55,7 +61,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
return;
fputs(tag, stdout);
- write_name_quoted(ent->name + offset, stdout, line_terminator);
+ write_name(ent->name, ent->len);
}
static void show_other_files(struct dir_struct *dir)
@@ -121,8 +127,7 @@ static void show_killed_files(struct dir_struct *dir)
static void show_ce_entry(const char *tag, struct cache_entry *ce)
{
- int len = prefix_len;
- int offset = prefix_offset;
+ int len = max_prefix_len;
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
@@ -156,20 +161,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
find_unique_abbrev(ce->sha1,abbrev),
ce_stage(ce));
}
- write_name_quoted(ce->name + offset, stdout, line_terminator);
+ write_name(ce->name, ce_namelen(ce));
}
static int show_one_ru(struct string_list_item *item, void *cbdata)
{
- int offset = prefix_offset;
const char *path = item->string;
struct resolve_undo_info *ui = item->util;
int i, len;
len = strlen(path);
- if (len < prefix_len)
+ if (len < max_prefix_len)
return 0; /* outside of the prefix */
- if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
+ if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
return 0; /* uninterested */
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
@@ -177,19 +181,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
find_unique_abbrev(ui->sha1[i], abbrev),
i + 1);
- write_name_quoted(path + offset, stdout, line_terminator);
+ write_name(path, len);
}
return 0;
}
-static void show_ru_info(const char *prefix)
+static void show_ru_info(void)
{
if (!the_index.resolve_undo)
return;
for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
}
-static void show_files(struct dir_struct *dir, const char *prefix)
+static void show_files(struct dir_struct *dir)
{
int i;
@@ -243,7 +247,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
*/
static void prune_cache(const char *prefix)
{
- int pos = cache_name_pos(prefix, prefix_len);
+ int pos = cache_name_pos(prefix, max_prefix_len);
unsigned int first, last;
if (pos < 0)
@@ -256,7 +260,7 @@ static void prune_cache(const char *prefix)
while (last > first) {
int next = (last + first) >> 1;
struct cache_entry *ce = active_cache[next];
- if (!strncmp(ce->name, prefix, prefix_len)) {
+ if (!strncmp(ce->name, prefix, max_prefix_len)) {
first = next+1;
continue;
}
@@ -265,11 +269,16 @@ static void prune_cache(const char *prefix)
active_nr = last;
}
-static const char *verify_pathspec(const char *prefix)
+static const char *pathspec_prefix(const char *prefix)
{
const char **p, *n, *prev;
unsigned long max;
+ if (!pathspec) {
+ max_prefix_len = prefix ? strlen(prefix) : 0;
+ return prefix;
+ }
+
prev = NULL;
max = PATH_MAX;
for (p = pathspec; (n = *p) != NULL; p++) {
@@ -291,10 +300,7 @@ static const char *verify_pathspec(const char *prefix)
}
}
- if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
- die("git ls-files: cannot generate relative filenames containing '..'");
-
- prefix_len = max;
+ max_prefix_len = max;
return max ? xmemdupz(prev, max) : NULL;
}
@@ -374,7 +380,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
}
}
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
{
/*
* Make sure all pathspec matched; otherwise it is an error.
@@ -404,7 +410,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
continue;
error("pathspec '%s' did not match any file(s) known to git.",
- pathspec[num] + prefix_offset);
+ pathspec[num] + prefix_len);
errors++;
}
return errors;
@@ -456,9 +462,10 @@ static int option_parse_exclude_standard(const struct option *opt,
return 0;
}
-int cmd_ls_files(int argc, const char **argv, const char *prefix)
+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
{
int require_work_tree = 0, show_tag = 0;
+ const char *max_prefix;
struct dir_struct dir;
struct option builtin_ls_files_options[] = {
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -504,7 +511,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
"add the standard git exclusions",
PARSE_OPT_NOARG, option_parse_exclude_standard },
- { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
+ { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
"make the output relative to the project top directory",
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
@@ -516,8 +523,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
};
memset(&dir, 0, sizeof(dir));
+ prefix = cmd_prefix;
if (prefix)
- prefix_offset = strlen(prefix);
+ prefix_len = strlen(prefix);
git_config(git_default_config, NULL);
if (read_cache() < 0)
@@ -555,9 +563,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
if (pathspec)
strip_trailing_slash_from_submodules();
- /* Verify that the pathspec matches the prefix */
- if (pathspec)
- prefix = verify_pathspec(prefix);
+ /* Find common prefix for all pathspec's */
+ max_prefix = pathspec_prefix(prefix);
/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {
@@ -575,8 +582,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
show_killed | show_modified | show_resolve_undo))
show_cached = 1;
- if (prefix)
- prune_cache(prefix);
+ if (max_prefix)
+ prune_cache(max_prefix);
if (with_tree) {
/*
* Basic sanity check; show-stages and show-unmerged
@@ -584,15 +591,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
*/
if (show_stage || show_unmerged)
die("ls-files --with-tree is incompatible with -s or -u");
- overlay_tree_on_cache(with_tree, prefix);
+ overlay_tree_on_cache(with_tree, max_prefix);
}
- show_files(&dir, prefix);
+ show_files(&dir);
if (show_resolve_undo)
- show_ru_info(prefix);
+ show_ru_info();
if (ps_matched) {
int bad;
- bad = report_path_error(ps_matched, pathspec, prefix_offset);
+ bad = report_path_error(ps_matched, pathspec, prefix_len);
if (bad)
fprintf(stderr, "Did you forget to 'git add'?\n");
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 70f5622d9d..34480cfad6 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -4,7 +4,8 @@
#include "remote.h"
static const char ls_remote_usage[] =
-"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>] <repository> <refs>...";
+"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n"
+" [-q|--quiet] [<repository> [<refs>...]]";
/*
* Is there one among the list of patterns that match the tail part
@@ -33,6 +34,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
const char *dest = NULL;
int nongit;
unsigned flags = 0;
+ int quiet = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
@@ -66,6 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
flags |= REF_NORMAL;
continue;
}
+ if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+ quiet = 1;
+ continue;
+ }
usage(ls_remote_usage);
}
dest = arg;
@@ -73,9 +79,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
break;
}
- if (!dest)
- usage(ls_remote_usage);
-
if (argv[i]) {
int j;
pattern = xcalloc(sizeof(const char *), argc - i + 1);
@@ -87,6 +90,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
}
remote = remote_get(dest);
+ if (!remote) {
+ if (dest)
+ die("bad repository '%s'", dest);
+ die("No remote configured to list refs from.");
+ }
if (!remote->url_nr)
die("remote %s has no configured URL", dest);
transport = transport_get(remote, NULL);
@@ -96,6 +104,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
ref = transport_get_remote_refs(transport);
if (transport_disconnect(transport))
return 1;
+
+ if (!dest && !quiet)
+ fprintf(stderr, "From %s\n", *remote->url);
for ( ; ref; ref = ref->next) {
if (!check_ref_type(ref, flags))
continue;
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 4a9729b9b3..2320d981ce 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -17,10 +17,10 @@ static struct strbuf name = STRBUF_INIT;
static struct strbuf email = STRBUF_INIT;
static enum {
- TE_DONTCARE, TE_QP, TE_BASE64,
+ TE_DONTCARE, TE_QP, TE_BASE64
} transfer_encoding;
static enum {
- TYPE_TEXT, TYPE_OTHER,
+ TYPE_TEXT, TYPE_OTHER
} message_type;
static struct strbuf charset = STRBUF_INIT;
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 610849a653..b8e9e5ba01 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -25,7 +25,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
const char *names[3] = { NULL, NULL, NULL };
mmfile_t mmfs[3];
mmbuffer_t result = {NULL, 0};
- xmparam_t xmp = {{XDF_NEED_MINIMAL}};
+ xmparam_t xmp = {{0}};
int ret = 0, i = 0, to_stdout = 0;
int quiet = 0;
int nongit;
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index a4a4f2ce4c..fc00d794d6 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -106,7 +106,7 @@ static void show_diff(struct merge_list *entry)
xdemitconf_t xecfg;
xdemitcb_t ecb;
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
ecb.outf = show_outf;
diff --git a/builtin/merge.c b/builtin/merge.c
index c043066845..37ce4f589f 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -548,13 +548,53 @@ static void write_tree_trivial(unsigned char *sha1)
die("git write-tree failed to write a tree");
}
-static int try_merge_strategy(const char *strategy, struct commit_list *common,
- const char *head_arg)
+int try_merge_command(const char *strategy, struct commit_list *common,
+ const char *head_arg, struct commit_list *remotes)
{
const char **args;
int i = 0, x = 0, ret;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
+
+ args = xmalloc((4 + xopts_nr + commit_list_count(common) +
+ commit_list_count(remotes)) * sizeof(char *));
+ strbuf_addf(&buf, "merge-%s", strategy);
+ args[i++] = buf.buf;
+ for (x = 0; x < xopts_nr; x++) {
+ char *s = xmalloc(strlen(xopts[x])+2+1);
+ strcpy(s, "--");
+ strcpy(s+2, xopts[x]);
+ args[i++] = s;
+ }
+ for (j = common; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i++] = "--";
+ args[i++] = head_arg;
+ for (j = remotes; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i] = NULL;
+ ret = run_command_v_opt(args, RUN_GIT_CMD);
+ strbuf_release(&buf);
+ i = 1;
+ for (x = 0; x < xopts_nr; x++)
+ free((void *)args[i++]);
+ for (j = common; j; j = j->next)
+ free((void *)args[i++]);
+ i += 2;
+ for (j = remotes; j; j = j->next)
+ free((void *)args[i++]);
+ free(args);
+ discard_cache();
+ if (read_cache() < 0)
+ die("failed to read the cache");
+ resolve_undo_clear();
+
+ return ret;
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+ const char *head_arg)
+{
int index_fd;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
@@ -567,12 +607,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
- int clean;
+ int clean, x;
struct commit *result;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
struct commit_list *reversed = NULL;
struct merge_options o;
+ struct commit_list *j;
if (remoteheads->next) {
error("Not handling anything other than two heads merge.");
@@ -612,39 +653,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
rollback_lock_file(lock);
return clean ? 0 : 1;
} else {
- args = xmalloc((4 + xopts_nr + commit_list_count(common) +
- commit_list_count(remoteheads)) * sizeof(char *));
- strbuf_addf(&buf, "merge-%s", strategy);
- args[i++] = buf.buf;
- for (x = 0; x < xopts_nr; x++) {
- char *s = xmalloc(strlen(xopts[x])+2+1);
- strcpy(s, "--");
- strcpy(s+2, xopts[x]);
- args[i++] = s;
- }
- for (j = common; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
- args[i++] = "--";
- args[i++] = head_arg;
- for (j = remoteheads; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
- args[i] = NULL;
- ret = run_command_v_opt(args, RUN_GIT_CMD);
- strbuf_release(&buf);
- i = 1;
- for (x = 0; x < xopts_nr; x++)
- free((void *)args[i++]);
- for (j = common; j; j = j->next)
- free((void *)args[i++]);
- i += 2;
- for (j = remoteheads; j; j = j->next)
- free((void *)args[i++]);
- free(args);
- discard_cache();
- if (read_cache() < 0)
- die("failed to read the cache");
- resolve_undo_clear();
- return ret;
+ return try_merge_command(strategy, common, head_arg, remoteheads);
}
}
@@ -973,7 +982,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
reset_hard(remote_head->sha1, 0);
return 0;
} else {
- struct strbuf msg = STRBUF_INIT;
+ struct strbuf merge_names = STRBUF_INIT;
/* We are invoked directly as the first-class UI. */
head_arg = "HEAD";
@@ -986,13 +995,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* codepath so we discard the error in this
* loop.
*/
- if (!have_message) {
- for (i = 0; i < argc; i++)
- merge_name(argv[i], &msg);
- fmt_merge_msg(option_log, &msg, &merge_msg);
- if (merge_msg.len)
- strbuf_setlen(&merge_msg, merge_msg.len-1);
- }
+ for (i = 0; i < argc; i++)
+ merge_name(argv[i], &merge_names);
+
+ if (have_message && option_log)
+ fmt_merge_msg_shortlog(&merge_names, &merge_msg);
+ else if (!have_message)
+ fmt_merge_msg(option_log, &merge_names, &merge_msg);
+
+
+ if (!(have_message && !option_log) && merge_msg.len)
+ strbuf_setlen(&merge_msg, merge_msg.len-1);
}
if (head_invalid || !argc)
diff --git a/builtin/notes.c b/builtin/notes.c
index 52b72fca68..648033c27e 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = {
"git notes [--ref <notes_ref>] edit [<object>]",
"git notes [--ref <notes_ref>] show [<object>]",
"git notes [--ref <notes_ref>] remove [<object>]",
- "git notes [--ref <notes_ref>] prune",
+ "git notes [--ref <notes_ref>] prune [-n | -v]",
NULL
};
@@ -67,7 +67,7 @@ static const char * const git_notes_remove_usage[] = {
};
static const char * const git_notes_prune_usage[] = {
- "git notes prune",
+ "git notes prune [<options>]",
NULL
};
@@ -416,7 +416,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd)
{
struct strbuf buf = STRBUF_INIT;
struct notes_rewrite_cfg *c = NULL;
- struct notes_tree *t;
+ struct notes_tree *t = NULL;
int ret = 0;
if (rewrite_cmd) {
@@ -792,7 +792,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
static int prune(int argc, const char **argv, const char *prefix)
{
struct notes_tree *t;
+ int show_only = 0, verbose = 0;
struct option options[] = {
+ OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"),
+ OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"),
OPT_END()
};
@@ -806,8 +809,10 @@ static int prune(int argc, const char **argv, const char *prefix)
t = init_notes_check("prune");
- prune_notes(t);
- commit_notes(t, "Notes removed by 'git notes prune'");
+ prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
+ (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
+ if (!show_only)
+ commit_notes(t, "Notes removed by 'git notes prune'");
free_notes(t);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 97802585ea..0e81673118 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -18,8 +18,8 @@
#include "refs.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static const char pack_usage[] =
@@ -1522,6 +1522,15 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
#ifndef NO_PTHREADS
+static void try_to_free_from_threads(size_t size)
+{
+ read_lock();
+ release_pack_memory(size, -1);
+ read_unlock();
+}
+
+try_to_free_t old_try_to_free_routine;
+
/*
* The main thread waits on the condition that (at least) one of the workers
* has stopped working (which is indicated in the .working member of
@@ -1552,14 +1561,16 @@ static pthread_cond_t progress_cond;
*/
static void init_threaded_search(void)
{
- pthread_mutex_init(&read_mutex, NULL);
+ init_recursive_mutex(&read_mutex);
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
+ old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
}
static void cleanup_threaded_search(void)
{
+ set_try_to_free_routine(old_try_to_free_routine);
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&read_mutex);
pthread_mutex_destroy(&cache_mutex);
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index af0911e4bd..512530022e 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -28,16 +28,42 @@ static int remove_space(char *line)
return dst - line;
}
-static void generate_id_list(void)
+static int scan_hunk_header(const char *p, int *p_before, int *p_after)
+{
+ static const char digits[] = "0123456789";
+ const char *q, *r;
+ int n;
+
+ q = p + 4;
+ n = strspn(q, digits);
+ if (q[n] == ',') {
+ q += n + 1;
+ n = strspn(q, digits);
+ }
+ if (n == 0 || q[n] != ' ' || q[n+1] != '+')
+ return 0;
+
+ r = q + n + 2;
+ n = strspn(r, digits);
+ if (r[n] == ',') {
+ r += n + 1;
+ n = strspn(r, digits);
+ }
+ if (n == 0)
+ return 0;
+
+ *p_before = atoi(q);
+ *p_after = atoi(r);
+ return 1;
+}
+
+int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx)
{
- static unsigned char sha1[20];
static char line[1000];
- git_SHA_CTX ctx;
- int patchlen = 0;
+ int patchlen = 0, found_next = 0;
+ int before = -1, after = -1;
- git_SHA1_Init(&ctx);
while (fgets(line, sizeof(line), stdin) != NULL) {
- unsigned char n[20];
char *p = line;
int len;
@@ -45,32 +71,75 @@ static void generate_id_list(void)
p += 10;
else if (!memcmp(line, "commit ", 7))
p += 7;
+ else if (!memcmp(line, "From ", 5))
+ p += 5;
- if (!get_sha1_hex(p, n)) {
- flush_current_id(patchlen, sha1, &ctx);
- hashcpy(sha1, n);
- patchlen = 0;
- continue;
+ if (!get_sha1_hex(p, next_sha1)) {
+ found_next = 1;
+ break;
}
/* Ignore commit comments */
if (!patchlen && memcmp(line, "diff ", 5))
continue;
- /* Ignore git-diff index header */
- if (!memcmp(line, "index ", 6))
- continue;
+ /* Parsing diff header? */
+ if (before == -1) {
+ if (!memcmp(line, "index ", 6))
+ continue;
+ else if (!memcmp(line, "--- ", 4))
+ before = after = 1;
+ else if (!isalpha(line[0]))
+ break;
+ }
- /* Ignore line numbers when computing the SHA1 of the patch */
- if (!memcmp(line, "@@ -", 4))
- continue;
+ /* Looking for a valid hunk header? */
+ if (before == 0 && after == 0) {
+ if (!memcmp(line, "@@ -", 4)) {
+ /* Parse next hunk, but ignore line numbers. */
+ scan_hunk_header(line, &before, &after);
+ continue;
+ }
+
+ /* Split at the end of the patch. */
+ if (memcmp(line, "diff ", 5))
+ break;
+
+ /* Else we're parsing another header. */
+ before = after = -1;
+ }
+
+ /* If we get here, we're inside a hunk. */
+ if (line[0] == '-' || line[0] == ' ')
+ before--;
+ if (line[0] == '+' || line[0] == ' ')
+ after--;
/* Compute the sha without whitespace */
len = remove_space(line);
patchlen += len;
- git_SHA1_Update(&ctx, line, len);
+ git_SHA1_Update(ctx, line, len);
+ }
+
+ if (!found_next)
+ hashclr(next_sha1);
+
+ return patchlen;
+}
+
+static void generate_id_list(void)
+{
+ unsigned char sha1[20], n[20];
+ git_SHA_CTX ctx;
+ int patchlen;
+
+ git_SHA1_Init(&ctx);
+ hashclr(sha1);
+ while (!feof(stdin)) {
+ patchlen = get_one_patchid(n, &ctx);
+ flush_current_id(patchlen, sha1, &ctx);
+ hashcpy(sha1, n);
}
- flush_current_id(patchlen, sha1, &ctx);
}
static const char patch_id_usage[] = "git patch-id < patch";
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 0559fcc871..29bc8d50bb 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -9,6 +9,7 @@
#include "object.h"
#include "remote.h"
#include "transport.h"
+#include "string-list.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -16,7 +17,7 @@ enum deny_action {
DENY_UNCONFIGURED,
DENY_IGNORE,
DENY_WARN,
- DENY_REFUSE,
+ DENY_REFUSE
};
static int deny_deletes;
@@ -129,13 +130,12 @@ static void write_head_info(void)
struct command {
struct command *next;
const char *error_string;
+ unsigned int skip_update;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char ref_name[FLEX_ARRAY]; /* more */
};
-static struct command *commands;
-
static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive";
@@ -188,7 +188,7 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
-static int run_receive_hook(const char *hook_name)
+static int run_receive_hook(struct command *commands, const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
struct command *cmd;
@@ -447,15 +447,15 @@ static const char *update(struct command *cmd)
static char update_post_hook[] = "hooks/post-update";
-static void run_update_post_hook(struct command *cmd)
+static void run_update_post_hook(struct command *commands)
{
- struct command *cmd_p;
+ struct command *cmd;
int argc;
const char **argv;
struct child_process proc;
- for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
- if (cmd_p->error_string)
+ for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
+ if (cmd->error_string)
continue;
argc++;
}
@@ -464,12 +464,12 @@ static void run_update_post_hook(struct command *cmd)
argv = xmalloc(sizeof(*argv) * (2 + argc));
argv[0] = update_post_hook;
- for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
+ for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
char *p;
- if (cmd_p->error_string)
+ if (cmd->error_string)
continue;
- p = xmalloc(strlen(cmd_p->ref_name) + 1);
- strcpy(p, cmd_p->ref_name);
+ p = xmalloc(strlen(cmd->ref_name) + 1);
+ strcpy(p, cmd->ref_name);
argv[argc] = p;
argc++;
}
@@ -488,37 +488,92 @@ static void run_update_post_hook(struct command *cmd)
}
}
-static void execute_commands(const char *unpacker_error)
+static void check_aliased_update(struct command *cmd, struct string_list *list)
+{
+ struct string_list_item *item;
+ struct command *dst_cmd;
+ unsigned char sha1[20];
+ char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
+ int flag;
+
+ const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+
+ if (!(flag & REF_ISSYMREF))
+ return;
+
+ if ((item = string_list_lookup(dst_name, list)) == NULL)
+ return;
+
+ cmd->skip_update = 1;
+
+ dst_cmd = (struct command *) item->util;
+
+ if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
+ !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
+ return;
+
+ dst_cmd->skip_update = 1;
+
+ strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
+ strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
+ strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
+ strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+ rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
+ " its target '%s' (%s..%s)",
+ cmd->ref_name, cmd_oldh, cmd_newh,
+ dst_cmd->ref_name, dst_oldh, dst_newh);
+
+ cmd->error_string = dst_cmd->error_string =
+ "inconsistent aliased update";
+}
+
+static void check_aliased_updates(struct command *commands)
+{
+ struct command *cmd;
+ struct string_list ref_list = { NULL, 0, 0, 0 };
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ struct string_list_item *item =
+ string_list_append(cmd->ref_name, &ref_list);
+ item->util = (void *)cmd;
+ }
+ sort_string_list(&ref_list);
+
+ for (cmd = commands; cmd; cmd = cmd->next)
+ check_aliased_update(cmd, &ref_list);
+
+ string_list_clear(&ref_list, 0);
+}
+
+static void execute_commands(struct command *commands, const char *unpacker_error)
{
- struct command *cmd = commands;
+ struct command *cmd;
unsigned char sha1[20];
if (unpacker_error) {
- while (cmd) {
+ for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "n/a (unpacker error)";
- cmd = cmd->next;
- }
return;
}
- if (run_receive_hook(pre_receive_hook)) {
- while (cmd) {
+ if (run_receive_hook(commands, pre_receive_hook)) {
+ for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "pre-receive hook declined";
- cmd = cmd->next;
- }
return;
}
+ check_aliased_updates(commands);
+
head_name = resolve_ref("HEAD", sha1, 0, NULL);
- while (cmd) {
- cmd->error_string = update(cmd);
- cmd = cmd->next;
- }
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (!cmd->skip_update)
+ cmd->error_string = update(cmd);
}
-static void read_head_info(void)
+static struct command *read_head_info(void)
{
+ struct command *commands = NULL;
struct command **p = &commands;
for (;;) {
static char line[1000];
@@ -548,15 +603,14 @@ static void read_head_info(void)
if (strstr(refname + reflen + 1, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
}
- cmd = xmalloc(sizeof(struct command) + len - 80);
+ cmd = xcalloc(1, sizeof(struct command) + len - 80);
hashcpy(cmd->old_sha1, old_sha1);
hashcpy(cmd->new_sha1, new_sha1);
memcpy(cmd->ref_name, line + 82, len - 81);
- cmd->error_string = NULL;
- cmd->next = NULL;
*p = cmd;
p = &cmd->next;
}
+ return commands;
}
static const char *parse_pack_header(struct pack_header *hdr)
@@ -643,7 +697,7 @@ static const char *unpack(void)
}
}
-static void report(const char *unpack_status)
+static void report(struct command *commands, const char *unpack_status)
{
struct command *cmd;
struct strbuf buf = STRBUF_INIT;
@@ -667,12 +721,12 @@ static void report(const char *unpack_status)
strbuf_release(&buf);
}
-static int delete_only(struct command *cmd)
+static int delete_only(struct command *commands)
{
- while (cmd) {
+ struct command *cmd;
+ for (cmd = commands; cmd; cmd = cmd->next) {
if (!is_null_sha1(cmd->new_sha1))
return 0;
- cmd = cmd->next;
}
return 1;
}
@@ -722,6 +776,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
int stateless_rpc = 0;
int i;
char *dir = NULL;
+ struct command *commands;
argv++;
for (i = 1; i < argc; i++) {
@@ -772,18 +827,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (advertise_refs)
return 0;
- read_head_info();
- if (commands) {
+ if ((commands = read_head_info()) != NULL) {
const char *unpack_status = NULL;
if (!delete_only(commands))
unpack_status = unpack();
- execute_commands(unpack_status);
+ execute_commands(commands, unpack_status);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
if (report_status)
- report(unpack_status);
- run_receive_hook(post_receive_hook);
+ report(commands, unpack_status);
+ run_receive_hook(commands, post_receive_hook);
run_update_post_hook(commands);
if (auto_gc) {
const char *argv_gc_auto[] = {
diff --git a/builtin/reflog.c b/builtin/reflog.c
index bd7880dc04..ebf610e64a 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -34,8 +34,13 @@ struct cmd_reflog_expire_cb {
struct expire_reflog_cb {
FILE *newlog;
- const char *ref;
- struct commit *ref_commit;
+ enum {
+ UE_NORMAL,
+ UE_ALWAYS,
+ UE_HEAD
+ } unreachable_expire_kind;
+ struct commit_list *mark_list;
+ unsigned long mark_limit;
struct cmd_reflog_expire_cb *cmd;
unsigned char last_kept_sha1[20];
};
@@ -210,46 +215,23 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
return 1;
}
-static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+/*
+ * Starting from commits in the cb->mark_list, mark commits that are
+ * reachable from them. Stop the traversal at commits older than
+ * the expire_limit and queue them back, so that the caller can call
+ * us again to restart the traversal with longer expire_limit.
+ */
+static void mark_reachable(struct expire_reflog_cb *cb)
{
- /*
- * We may or may not have the commit yet - if not, look it
- * up using the supplied sha1.
- */
- if (!commit) {
- if (is_null_sha1(sha1))
- return 0;
-
- commit = lookup_commit_reference_gently(sha1, 1);
-
- /* Not a commit -- keep it */
- if (!commit)
- return 0;
- }
-
- /* Reachable from the current ref? Don't prune. */
- if (commit->object.flags & REACHABLE)
- return 0;
- if (in_merge_bases(commit, &cb->ref_commit, 1))
- return 0;
-
- /* We can't reach it - prune it. */
- return 1;
-}
+ struct commit *commit;
+ struct commit_list *pending;
+ unsigned long expire_limit = cb->mark_limit;
+ struct commit_list *leftover = NULL;
-static void mark_reachable(struct commit *commit, unsigned long expire_limit)
-{
- /*
- * We need to compute whether the commit on either side of a reflog
- * entry is reachable from the tip of the ref for all entries.
- * Mark commits that are reachable from the tip down to the
- * time threshold first; we know a commit marked thusly is
- * reachable from the tip without running in_merge_bases()
- * at all.
- */
- struct commit_list *pending = NULL;
+ for (pending = cb->mark_list; pending; pending = pending->next)
+ pending->item->object.flags &= ~REACHABLE;
- commit_list_insert(commit, &pending);
+ pending = cb->mark_list;
while (pending) {
struct commit_list *entry = pending;
struct commit_list *parent;
@@ -261,8 +243,11 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit)
if (parse_commit(commit))
continue;
commit->object.flags |= REACHABLE;
- if (commit->date < expire_limit)
+ if (commit->date < expire_limit) {
+ commit_list_insert(commit, &leftover);
continue;
+ }
+ commit->object.flags |= REACHABLE;
parent = commit->parents;
while (parent) {
commit = parent->item;
@@ -272,6 +257,36 @@ static void mark_reachable(struct commit *commit, unsigned long expire_limit)
commit_list_insert(commit, &pending);
}
}
+ cb->mark_list = leftover;
+}
+
+static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+{
+ /*
+ * We may or may not have the commit yet - if not, look it
+ * up using the supplied sha1.
+ */
+ if (!commit) {
+ if (is_null_sha1(sha1))
+ return 0;
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+
+ /* Not a commit -- keep it */
+ if (!commit)
+ return 0;
+ }
+
+ /* Reachable from the current ref? Don't prune. */
+ if (commit->object.flags & REACHABLE)
+ return 0;
+
+ if (cb->mark_list && cb->mark_limit) {
+ cb->mark_limit = 0; /* dig down to the root */
+ mark_reachable(cb);
+ }
+
+ return !(commit->object.flags & REACHABLE);
}
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
@@ -293,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
goto prune;
if (timestamp < cb->cmd->expire_unreachable) {
- if (!cb->ref_commit)
+ if (cb->unreachable_expire_kind == UE_ALWAYS)
goto prune;
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
goto prune;
@@ -320,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
return 0;
}
+static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct commit_list **list = cb_data;
+ struct commit *tip_commit;
+ if (flags & REF_ISSYMREF)
+ return 0;
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ return 0;
+ commit_list_insert(tip_commit, list);
+ return 0;
+}
+
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
{
struct cmd_reflog_expire_cb *cmd = cb_data;
struct expire_reflog_cb cb;
struct ref_lock *lock;
char *log_file, *newlog_path = NULL;
+ struct commit *tip_commit;
+ struct commit_list *tips;
int status = 0;
memset(&cb, 0, sizeof(cb));
@@ -345,14 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
cb.newlog = fopen(newlog_path, "w");
}
- cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
- cb.ref = ref;
cb.cmd = cmd;
- if (cb.ref_commit)
- mark_reachable(cb.ref_commit, cmd->expire_total);
+
+ if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
+ tip_commit = NULL;
+ cb.unreachable_expire_kind = UE_HEAD;
+ } else {
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+ else
+ cb.unreachable_expire_kind = UE_NORMAL;
+ }
+
+ if (cmd->expire_unreachable <= cmd->expire_total)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+
+ cb.mark_list = NULL;
+ tips = NULL;
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for_each_ref(push_tip_to_list, &tips);
+ for (elem = tips; elem; elem = elem->next)
+ commit_list_insert(elem->item, &cb.mark_list);
+ } else {
+ commit_list_insert(tip_commit, &cb.mark_list);
+ }
+ cb.mark_limit = cmd->expire_total;
+ mark_reachable(&cb);
+ }
+
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
- if (cb.ref_commit)
- clear_commit_marks(cb.ref_commit, REACHABLE);
+
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for (elem = tips; elem; elem = elem->next)
+ clear_commit_marks(tip_commit, REACHABLE);
+ free_commit_list(tips);
+ } else {
+ clear_commit_marks(tip_commit, REACHABLE);
+ }
+ }
finish:
if (cb.newlog) {
if (fclose(cb.newlog)) {
diff --git a/builtin/remote.c b/builtin/remote.c
index 277765b864..0a52667e0f 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = {
"git remote [-v | --verbose] show [-n] <name>",
"git remote prune [-n | --dry-run] <name>",
"git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+ "git remote set-branches <name> [--add] <branch>...",
"git remote set-url <name> <newurl> [<oldurl>]",
"git remote set-url --add <name> <newurl>",
"git remote set-url --delete <name> <url>",
@@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = {
NULL
};
+static const char * const builtin_remote_setbranches_usage[] = {
+ "git remote set-branches <name> <branch>...",
+ "git remote set-branches --add <name> <branch>...",
+ NULL
+};
+
static const char * const builtin_remote_show_usage[] = {
"git remote show [<options>] <name>",
NULL
@@ -104,9 +111,29 @@ static int fetch_remote(const char *name)
return 0;
}
+enum {
+ TAGS_UNSET = 0,
+ TAGS_DEFAULT = 1,
+ TAGS_SET = 2
+};
+
+static int add_branch(const char *key, const char *branchname,
+ const char *remotename, int mirror, struct strbuf *tmp)
+{
+ strbuf_reset(tmp);
+ strbuf_addch(tmp, '+');
+ if (mirror)
+ strbuf_addf(tmp, "refs/%s:refs/%s",
+ branchname, branchname);
+ else
+ strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+ branchname, remotename, branchname);
+ return git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
static int add(int argc, const char **argv)
{
- int fetch = 0, mirror = 0;
+ int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
struct string_list track = { NULL, 0, 0 };
const char *master = NULL;
struct remote *remote;
@@ -116,6 +143,11 @@ static int add(int argc, const char **argv)
struct option options[] = {
OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+ OPT_SET_INT(0, "tags", &fetch_tags,
+ "import all tags and associated objects when fetching",
+ TAGS_SET),
+ OPT_SET_INT(0, NULL, &fetch_tags,
+ "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
OPT_CALLBACK('t', "track", &track, "branch",
"branch(es) to track", opt_parse_track),
OPT_STRING('m', "master", &master, "branch", "master branch"),
@@ -151,17 +183,8 @@ static int add(int argc, const char **argv)
if (track.nr == 0)
string_list_append("*", &track);
for (i = 0; i < track.nr; i++) {
- struct string_list_item *item = track.items + i;
-
- strbuf_reset(&buf2);
- strbuf_addch(&buf2, '+');
- if (mirror)
- strbuf_addf(&buf2, "refs/%s:refs/%s",
- item->string, item->string);
- else
- strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
- item->string, name, item->string);
- if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+ if (add_branch(buf.buf, track.items[i].string,
+ name, mirror, &buf2))
return 1;
}
@@ -172,6 +195,14 @@ static int add(int argc, const char **argv)
return 1;
}
+ if (fetch_tags != TAGS_DEFAULT) {
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "remote.%s.tagopt", name);
+ if (git_config_set(buf.buf,
+ fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
+ return 1;
+ }
+
if (fetch && fetch_remote(name))
return 1;
@@ -317,7 +348,7 @@ struct push_info {
PUSH_STATUS_UPTODATE,
PUSH_STATUS_FASTFORWARD,
PUSH_STATUS_OUTOFDATE,
- PUSH_STATUS_NOTQUERIED,
+ PUSH_STATUS_NOTQUERIED
} status;
};
@@ -705,11 +736,14 @@ static int rm(int argc, const char **argv)
struct known_remotes known_remotes = { NULL, NULL };
struct string_list branches = { NULL, 0, 0, 1 };
struct string_list skipped = { NULL, 0, 0, 1 };
- struct branches_for_remote cb_data = {
- NULL, &branches, &skipped, &known_remotes
- };
+ struct branches_for_remote cb_data;
int i, result;
+ memset(&cb_data, 0, sizeof(cb_data));
+ cb_data.branches = &branches;
+ cb_data.skipped = &skipped;
+ cb_data.keep = &known_remotes;
+
if (argc != 2)
usage_with_options(builtin_remote_rm_usage, options);
@@ -1265,6 +1299,72 @@ static int update(int argc, const char **argv)
return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
}
+static int remove_all_fetch_refspecs(const char *remote, const char *key)
+{
+ return git_config_set_multivar(key, NULL, NULL, 1);
+}
+
+static int add_branches(struct remote *remote, const char **branches,
+ const char *key)
+{
+ const char *remotename = remote->name;
+ int mirror = remote->mirror;
+ struct strbuf refspec = STRBUF_INIT;
+
+ for (; *branches; branches++)
+ if (add_branch(key, *branches, remotename, mirror, &refspec)) {
+ strbuf_release(&refspec);
+ return 1;
+ }
+
+ strbuf_release(&refspec);
+ return 0;
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+ int add_mode)
+{
+ struct strbuf key = STRBUF_INIT;
+ struct remote *remote;
+
+ strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+ if (!remote_is_configured(remotename))
+ die("No such remote '%s'", remotename);
+ remote = remote_get(remotename);
+
+ if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+ strbuf_release(&key);
+ return 1;
+ }
+ if (add_branches(remote, branches, key.buf)) {
+ strbuf_release(&key);
+ return 1;
+ }
+
+ strbuf_release(&key);
+ return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+ int add_mode = 0;
+ struct option options[] = {
+ OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options,
+ builtin_remote_setbranches_usage, 0);
+ if (argc == 0) {
+ error("no remote specified");
+ usage_with_options(builtin_remote_seturl_usage, options);
+ }
+ argv[argc] = NULL;
+
+ return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
static int set_url(int argc, const char **argv)
{
int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@ -1430,6 +1530,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
result = rm(argc, argv);
else if (!strcmp(argv[0], "set-head"))
result = set_head(argc, argv);
+ else if (!strcmp(argv[0], "set-branches"))
+ result = set_branches(argc, argv);
else if (!strcmp(argv[0], "set-url"))
result = set_url(argc, argv);
else if (!strcmp(argv[0], "show"))
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 34f9acee91..0048f9ef7f 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -89,7 +89,7 @@ static int diff_two(const char *file1, const char *label1,
printf("--- a/%s\n+++ b/%s\n", label1, label2);
fflush(stdout);
memset(&xpp, 0, sizeof(xpp));
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = 0;
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
ecb.outf = outf;
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 8fbf9d0db6..b676e29635 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -408,7 +408,8 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
memset(opts + onb, 0, sizeof(opts[onb]));
argc = parse_options(argc, argv, prefix, opts, usage,
keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
- stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0);
+ stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0 |
+ PARSE_OPT_SHELL_EVAL);
strbuf_addf(&parsed, " --");
sq_quote_argv(&parsed, argv, 0);
diff --git a/builtin/revert.c b/builtin/revert.c
index 7d68ef714e..7976b5a329 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -43,6 +43,7 @@ static const char *commit_name;
static int allow_rerere_auto;
static const char *me;
+static const char *strategy;
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -62,6 +63,7 @@ static void parse_args(int argc, const char **argv)
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+ OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
OPT_END(),
OPT_END(),
OPT_END(),
@@ -174,28 +176,17 @@ static char *get_encoding(const char *message)
return NULL;
}
-static struct lock_file msg_file;
-static int msg_fd;
-
-static void add_to_msg(const char *string)
-{
- int len = strlen(string);
- if (write_in_full(msg_fd, string, len) < 0)
- die_errno ("Could not write to MERGE_MSG");
-}
-
-static void add_message_to_msg(const char *message)
+static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
{
const char *p = message;
while (*p && (*p != '\n' || p[1] != '\n'))
p++;
if (!*p)
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));
p += 2;
- add_to_msg(p);
- return;
+ strbuf_addstr(msgbuf, p);
}
static void set_author_ident_env(const char *message)
@@ -271,6 +262,19 @@ static char *help_msg(const char *name)
return strbuf_detach(&helpbuf, NULL);
}
+static void write_message(struct strbuf *msgbuf, const char *filename)
+{
+ static struct lock_file msg_file;
+
+ int msg_fd = hold_lock_file_for_update(&msg_file, filename,
+ LOCK_DIE_ON_ERROR);
+ if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
+ die_errno("Could not write to %s.", filename);
+ strbuf_release(msgbuf);
+ if (commit_lock_file(&msg_file) < 0)
+ die("Error wrapping up %s", filename);
+}
+
static struct tree *empty_tree(void)
{
struct tree *tree = xcalloc(1, sizeof(struct tree));
@@ -305,17 +309,70 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
return write_ref_sha1(ref_lock, to, "cherry-pick");
}
+static void do_recursive_merge(struct commit *base, struct commit *next,
+ const char *base_label, const char *next_label,
+ unsigned char *head, struct strbuf *msgbuf,
+ char *defmsg)
+{
+ struct merge_options o;
+ struct tree *result, *next_tree, *base_tree, *head_tree;
+ int clean, index_fd;
+ static struct lock_file index_lock;
+
+ index_fd = hold_locked_index(&index_lock, 1);
+
+ read_cache();
+ init_merge_options(&o);
+ o.ancestor = base ? base_label : "(empty tree)";
+ o.branch1 = "HEAD";
+ o.branch2 = next ? next_label : "(empty tree)";
+
+ head_tree = parse_tree_indirect(head);
+ next_tree = next ? next->tree : empty_tree();
+ base_tree = base ? base->tree : empty_tree();
+
+ clean = merge_trees(&o,
+ head_tree,
+ next_tree, base_tree, &result);
+
+ if (active_cache_changed &&
+ (write_cache(index_fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock)))
+ die("%s: Unable to write new index file", me);
+ rollback_lock_file(&index_lock);
+
+ if (!clean) {
+ int i;
+ strbuf_addstr(msgbuf, "\nConflicts:\n\n");
+ for (i = 0; i < active_nr;) {
+ struct cache_entry *ce = active_cache[i++];
+ if (ce_stage(ce)) {
+ strbuf_addch(msgbuf, '\t');
+ strbuf_addstr(msgbuf, ce->name);
+ strbuf_addch(msgbuf, '\n');
+ while (i < active_nr && !strcmp(ce->name,
+ active_cache[i]->name))
+ i++;
+ }
+ }
+ write_message(msgbuf, defmsg);
+ fprintf(stderr, "Automatic %s failed.%s\n",
+ me, help_msg(commit_name));
+ rerere(allow_rerere_auto);
+ exit(1);
+ }
+ write_message(msgbuf, defmsg);
+ fprintf(stderr, "Finished one %s.\n", me);
+}
+
static int revert_or_cherry_pick(int argc, const char **argv)
{
unsigned char head[20];
struct commit *base, *next, *parent;
const char *base_label, *next_label;
- int i, index_fd, clean;
struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
char *defmsg = NULL;
- struct merge_options o;
- struct tree *result, *next_tree, *base_tree, *head_tree;
- static struct lock_file index_lock;
+ struct strbuf msgbuf = STRBUF_INIT;
git_config(git_default_config, NULL);
me = action == REVERT ? "revert" : "cherry-pick";
@@ -403,83 +460,57 @@ static int revert_or_cherry_pick(int argc, const char **argv)
*/
defmsg = git_pathdup("MERGE_MSG");
- msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
- LOCK_DIE_ON_ERROR);
-
- index_fd = hold_locked_index(&index_lock, 1);
if (action == REVERT) {
base = commit;
base_label = msg.label;
next = parent;
next_label = msg.parent_label;
- add_to_msg("Revert \"");
- add_to_msg(msg.subject);
- add_to_msg("\"\n\nThis reverts commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, "Revert \"");
+ strbuf_addstr(&msgbuf, msg.subject);
+ strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
if (commit->parents->next) {
- add_to_msg(", reversing\nchanges made to ");
- add_to_msg(sha1_to_hex(parent->object.sha1));
+ strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
}
- add_to_msg(".\n");
+ strbuf_addstr(&msgbuf, ".\n");
} else {
base = parent;
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
set_author_ident_env(msg.message);
- add_message_to_msg(msg.message);
+ add_message_to_msg(&msgbuf, msg.message);
if (no_replay) {
- add_to_msg("(cherry picked from commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
- add_to_msg(")\n");
+ strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, ")\n");
}
}
- read_cache();
- init_merge_options(&o);
- o.ancestor = base ? base_label : "(empty tree)";
- o.branch1 = "HEAD";
- o.branch2 = next ? next_label : "(empty tree)";
-
- head_tree = parse_tree_indirect(head);
- next_tree = next ? next->tree : empty_tree();
- base_tree = base ? base->tree : empty_tree();
-
- clean = merge_trees(&o,
- head_tree,
- next_tree, base_tree, &result);
-
- if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock)))
- die("%s: Unable to write new index file", me);
- rollback_lock_file(&index_lock);
-
- if (!clean) {
- add_to_msg("\nConflicts:\n\n");
- for (i = 0; i < active_nr;) {
- struct cache_entry *ce = active_cache[i++];
- if (ce_stage(ce)) {
- add_to_msg("\t");
- add_to_msg(ce->name);
- add_to_msg("\n");
- while (i < active_nr && !strcmp(ce->name,
- active_cache[i]->name))
- i++;
- }
+ if (!strategy || !strcmp(strategy, "recursive") || action == REVERT)
+ do_recursive_merge(base, next, base_label, next_label,
+ head, &msgbuf, defmsg);
+ else {
+ int res;
+ struct commit_list *common = NULL;
+ struct commit_list *remotes = NULL;
+ write_message(&msgbuf, defmsg);
+ commit_list_insert(base, &common);
+ commit_list_insert(next, &remotes);
+ res = try_merge_command(strategy, common,
+ sha1_to_hex(head), remotes);
+ free_commit_list(common);
+ free_commit_list(remotes);
+ if (res) {
+ fprintf(stderr, "Automatic %s with strategy %s failed.%s\n",
+ me, strategy, help_msg(commit_name));
+ rerere(allow_rerere_auto);
+ exit(1);
}
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Automatic %s failed.%s\n",
- me, help_msg(commit_name));
- rerere(allow_rerere_auto);
- exit(1);
}
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Finished one %s.\n", me);
/*
*
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 06320f5285..5089502800 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -162,7 +162,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
sha1_to_hex(commit->object.sha1));
if (log->user_format) {
struct pretty_print_context ctx = {0};
- ctx.abbrev = DEFAULT_ABBREV;
+ ctx.abbrev = log->abbrev;
ctx.subject = "";
ctx.after_subject = "";
ctx.date_mode = DATE_NORMAL;
@@ -290,6 +290,7 @@ parse_done:
}
log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
+ log.abbrev = rev.abbrev;
/* assume HEAD if from a tty */
if (!nongit && !rev.pending.nr && isatty(0))