aboutsummaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c5
-rw-r--r--builtin/apply.c13
-rw-r--r--builtin/blame.c7
-rw-r--r--builtin/branch.c96
-rw-r--r--builtin/checkout.c103
-rw-r--r--builtin/clone.c8
-rw-r--r--builtin/commit-tree.c2
-rw-r--r--builtin/commit.c26
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/diff.c12
-rw-r--r--builtin/fast-export.c4
-rw-r--r--builtin/fetch-pack.c15
-rw-r--r--builtin/fetch.c254
-rw-r--r--builtin/fmt-merge-msg.c79
-rw-r--r--builtin/for-each-ref.c7
-rw-r--r--builtin/fsck.c80
-rw-r--r--builtin/gc.c4
-rw-r--r--builtin/grep.c83
-rw-r--r--builtin/index-pack.c10
-rw-r--r--builtin/init-db.c2
-rw-r--r--builtin/log.c73
-rw-r--r--builtin/merge.c161
-rw-r--r--builtin/mktree.c1
-rw-r--r--builtin/mv.c6
-rw-r--r--builtin/name-rev.c2
-rw-r--r--builtin/notes.c13
-rw-r--r--builtin/pack-objects.c197
-rw-r--r--builtin/prune.c13
-rw-r--r--builtin/receive-pack.c23
-rw-r--r--builtin/reflog.c2
-rw-r--r--builtin/remote.c21
-rw-r--r--builtin/replace.c4
-rw-r--r--builtin/reset.c7
-rw-r--r--builtin/revert.c360
-rw-r--r--builtin/send-pack.c7
-rw-r--r--builtin/show-branch.c6
-rw-r--r--builtin/show-ref.c2
-rw-r--r--builtin/stripspace.c4
-rw-r--r--builtin/symbolic-ref.c2
-rw-r--r--builtin/tag.c71
-rw-r--r--builtin/upload-archive.c43
41 files changed, 1200 insertions, 630 deletions
diff --git a/builtin/add.c b/builtin/add.c
index c59b0c98f..1c42900ff 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -13,6 +13,7 @@
#include "diff.h"
#include "diffcore.h"
#include "revision.h"
+#include "bulk-checkin.h"
static const char * const builtin_add_usage[] = {
"git add [options] [--] <filepattern>...",
@@ -458,11 +459,15 @@ int cmd_add(int argc, const char **argv, const char *prefix)
free(seen);
}
+ plug_bulk_checkin();
+
exit_status |= add_files_to_cache(prefix, pathspec, flags);
if (add_new_files)
exit_status |= add_files(&dir, flags);
+ unplug_bulk_checkin();
+
finish:
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
diff --git a/builtin/apply.c b/builtin/apply.c
index 84a8a0b52..c24dc546d 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -250,9 +250,6 @@ static int fuzzy_matchlines(const char *s1, size_t n1,
const char *last2 = s2 + n2 - 1;
int result = 0;
- if (n1 < 0 || n2 < 0)
- return 0;
-
/* ignore line endings */
while ((*last1 == '\r') || (*last1 == '\n'))
last1--;
@@ -3590,15 +3587,12 @@ static int write_out_one_reject(struct patch *patch)
return -1;
}
-static int write_out_results(struct patch *list, int skipped_patch)
+static int write_out_results(struct patch *list)
{
int phase;
int errs = 0;
struct patch *l;
- if (!list && !skipped_patch)
- return error("No changes");
-
for (phase = 0; phase < 2; phase++) {
l = list;
while (l) {
@@ -3724,6 +3718,9 @@ static int apply_patch(int fd, const char *filename, int options)
offset += nr;
}
+ if (!list && !skipped_patch)
+ die("unrecognized input");
+
if (whitespace_error && (ws_error_action == die_on_ws_error))
apply = 0;
@@ -3741,7 +3738,7 @@ static int apply_patch(int fd, const char *filename, int options)
!apply_with_reject)
exit(1);
- if (apply && write_out_results(list, skipped_patch))
+ if (apply && write_out_results(list))
exit(1);
if (fake_ancestor)
diff --git a/builtin/blame.c b/builtin/blame.c
index 26a5d424b..5a67c202f 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1598,7 +1598,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
int tz;
if (show_raw_time) {
- sprintf(time_buf, "%lu %s", time, tz_str);
+ snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
}
else {
tz = atoi(tz_str);
@@ -2096,6 +2096,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;
const char *read_from;
+ char *buf_ptr;
unsigned long buf_len;
if (contents_from) {
@@ -2113,8 +2114,8 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
switch (st.st_mode & S_IFMT) {
case S_IFREG:
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
- buf.len = buf_len;
+ textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+ strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
break;
diff --git a/builtin/branch.c b/builtin/branch.c
index 009b7138a..7095718c1 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -104,6 +104,7 @@ static int branch_merged(int kind, const char *name,
*/
struct commit *reference_rev = NULL;
const char *reference_name = NULL;
+ void *reference_name_to_free = NULL;
int merged;
if (kind == REF_LOCAL_BRANCH) {
@@ -114,8 +115,8 @@ static int branch_merged(int kind, const char *name,
branch->merge &&
branch->merge[0] &&
branch->merge[0]->dst &&
- (reference_name =
- resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+ (reference_name = reference_name_to_free =
+ resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
if (!reference_rev)
@@ -141,6 +142,7 @@ static int branch_merged(int kind, const char *name,
" '%s', even though it is merged to HEAD."),
name, reference_name);
}
+ free(reference_name_to_free);
return merged;
}
@@ -186,7 +188,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
free(name);
name = xstrdup(mkpath(fmt, bname.buf));
- if (!resolve_ref(name, sha1, 1, NULL)) {
+ if (read_ref(name, sha1)) {
error(_("%sbranch '%s' not found."),
remote, bname.buf);
ret = 1;
@@ -250,7 +252,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst, *cp;
- dst = resolve_ref(src, sha1, 0, &flag);
+ dst = resolve_ref_unsafe(src, sha1, 0, &flag);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
if (prefix && (cp = skip_prefix(dst, prefix)))
@@ -565,9 +567,9 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
static void rename_branch(const char *oldname, const char *newname, int force)
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
- unsigned char sha1[20];
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
int recovery = 0;
+ int clobber_head_ok;
if (!oldname)
die(_("cannot rename the current branch while not on any."));
@@ -577,13 +579,19 @@ static void rename_branch(const char *oldname, const char *newname, int force)
* Bad name --- this could be an attempt to rename a
* ref that we used to allow to be created by accident.
*/
- if (resolve_ref(oldref.buf, sha1, 1, NULL))
+ if (ref_exists(oldref.buf))
recovery = 1;
else
die(_("Invalid branch name: '%s'"), oldname);
}
- validate_new_branchname(newname, &newref, force, 0);
+ /*
+ * A command like "git branch -M currentbranch currentbranch" cannot
+ * cause the worktree to become inconsistent with HEAD, so allow it.
+ */
+ clobber_head_ok = !strcmp(oldname, newname);
+
+ validate_new_branchname(newname, &newref, force, clobber_head_ok);
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
@@ -623,11 +631,49 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
return 0;
}
+static const char edit_description[] = "BRANCH_DESCRIPTION";
+
+static int edit_branch_description(const char *branch_name)
+{
+ FILE *fp;
+ int status;
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf name = STRBUF_INIT;
+
+ read_branch_desc(&buf, branch_name);
+ if (!buf.len || buf.buf[buf.len-1] != '\n')
+ strbuf_addch(&buf, '\n');
+ strbuf_addf(&buf,
+ "# Please edit the description for the branch\n"
+ "# %s\n"
+ "# Lines starting with '#' will be stripped.\n",
+ branch_name);
+ fp = fopen(git_path(edit_description), "w");
+ if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
+ strbuf_release(&buf);
+ return error(_("could not write branch description template: %s\n"),
+ strerror(errno));
+ }
+ strbuf_reset(&buf);
+ if (launch_editor(git_path(edit_description), &buf, NULL)) {
+ strbuf_release(&buf);
+ return -1;
+ }
+ stripspace(&buf, 1);
+
+ strbuf_addf(&name, "branch.%s.description", branch_name);
+ status = git_config_set(name.buf, buf.buf);
+ strbuf_release(&name);
+ strbuf_release(&buf);
+
+ return status;
+}
+
int cmd_branch(int argc, const char **argv, const char *prefix)
{
int delete = 0, rename = 0, force_create = 0, list = 0;
int verbose = 0, abbrev = -1, detached = 0;
- int reflog = 0;
+ int reflog = 0, edit_description = 0;
enum branch_track track;
int kinds = REF_LOCAL_BRANCH;
struct commit_list *with_commit = NULL;
@@ -666,6 +712,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
OPT_BOOLEAN(0, "list", &list, "list branch names"),
OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
+ OPT_BOOLEAN(0, "edit-description", &edit_description,
+ "edit the description for the branch"),
OPT__FORCE(&force_create, "force creation (when already exists)"),
{
OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
@@ -689,10 +737,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_ref("HEAD", head_sha1, 0, NULL);
+ head = resolve_refdup("HEAD", head_sha1, 0, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
- head = xstrdup(head);
if (!strcmp(head, "HEAD")) {
detached = 1;
} else {
@@ -705,7 +752,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
- if (!delete && !rename && !force_create && argc == 0)
+ if (!delete && !rename && !edit_description && argc == 0)
list = 1;
if (!!delete + !!rename + !!force_create + !!list > 1)
@@ -719,15 +766,30 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
else if (list)
return print_ref_list(kinds, detached, verbose, abbrev,
with_commit, argv);
- else if (rename && (argc == 1))
- rename_branch(head, argv[0], rename > 1);
- else if (rename && (argc == 2))
- rename_branch(argv[0], argv[1], rename > 1);
- else if (argc <= 2) {
+ else if (edit_description) {
+ const char *branch_name;
+ if (detached)
+ die("Cannot give description to detached HEAD");
+ if (!argc)
+ branch_name = head;
+ else if (argc == 1)
+ branch_name = argv[0];
+ else
+ usage_with_options(builtin_branch_usage, options);
+ if (edit_branch_description(branch_name))
+ return 1;
+ } else if (rename) {
+ if (argc == 1)
+ rename_branch(head, argv[0], rename > 1);
+ else if (argc == 2)
+ rename_branch(argv[0], argv[1], rename > 1);
+ else
+ usage_with_options(builtin_branch_usage, options);
+ } else if (argc > 0 && argc <= 2) {
if (kinds != REF_LOCAL_BRANCH)
die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
- force_create, reflog, track);
+ force_create, reflog, 0, track);
} else
usage_with_options(builtin_branch_usage, options);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2a8077242..f1984d993 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -34,6 +34,7 @@ struct checkout_opts {
int force_detach;
int writeout_stage;
int writeout_error;
+ int overwrite_ignore;
/* not set by parse_options */
int branch_exists;
@@ -114,16 +115,21 @@ static int check_stage(int stage, struct cache_entry *ce, int pos)
return error(_("path '%s' does not have their version"), ce->name);
}
-static int check_all_stages(struct cache_entry *ce, int pos)
+static int check_stages(unsigned stages, struct cache_entry *ce, int pos)
{
- if (ce_stage(ce) != 1 ||
- active_nr <= pos + 2 ||
- strcmp(active_cache[pos+1]->name, ce->name) ||
- ce_stage(active_cache[pos+1]) != 2 ||
- strcmp(active_cache[pos+2]->name, ce->name) ||
- ce_stage(active_cache[pos+2]) != 3)
- return error(_("path '%s' does not have all three versions"),
- ce->name);
+ unsigned seen = 0;
+ const char *name = ce->name;
+
+ while (pos < active_nr) {
+ ce = active_cache[pos];
+ if (strcmp(name, ce->name))
+ break;
+ seen |= (1 << ce_stage(ce));
+ pos++;
+ }
+ if ((stages & seen) != stages)
+ return error(_("path '%s' does not have all necessary versions"),
+ name);
return 0;
}
@@ -150,18 +156,27 @@ static int checkout_merged(int pos, struct checkout *state)
int status;
unsigned char sha1[20];
mmbuffer_t result_buf;
+ unsigned char threeway[3][20];
+ unsigned mode = 0;
+
+ memset(threeway, 0, sizeof(threeway));
+ while (pos < active_nr) {
+ int stage;
+ stage = ce_stage(ce);
+ if (!stage || strcmp(path, ce->name))
+ break;
+ hashcpy(threeway[stage - 1], ce->sha1);
+ if (stage == 2)
+ mode = create_ce_mode(ce->ce_mode);
+ pos++;
+ ce = active_cache[pos];
+ }
+ if (is_null_sha1(threeway[1]) || is_null_sha1(threeway[2]))
+ return error(_("path '%s' does not have necessary versions"), path);
- if (ce_stage(ce) != 1 ||
- active_nr <= pos + 2 ||
- strcmp(active_cache[pos+1]->name, path) ||
- ce_stage(active_cache[pos+1]) != 2 ||
- strcmp(active_cache[pos+2]->name, path) ||
- ce_stage(active_cache[pos+2]) != 3)
- return error(_("path '%s' does not have all 3 versions"), path);
-
- read_mmblob(&ancestor, active_cache[pos]->sha1);
- read_mmblob(&ours, active_cache[pos+1]->sha1);
- read_mmblob(&theirs, active_cache[pos+2]->sha1);
+ read_mmblob(&ancestor, threeway[0]);
+ read_mmblob(&ours, threeway[1]);
+ read_mmblob(&theirs, threeway[2]);
/*
* NEEDSWORK: re-create conflicts from merges with
@@ -192,9 +207,7 @@ static int checkout_merged(int pos, struct checkout *state)
if (write_sha1_file(result_buf.ptr, result_buf.size,
blob_type, sha1))
die(_("Unable to add merge result for '%s'"), path);
- ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
- sha1,
- path, 2, 0);
+ ce = make_cache_entry(mode, sha1, path, 2, 0);
if (!ce)
die(_("make_cache_entry failed for path '%s'"), path);
status = checkout_entry(ce, state, NULL);
@@ -252,7 +265,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
} else if (stage) {
errs |= check_stage(stage, ce, pos);
} else if (opts->merge) {
- errs |= check_all_stages(ce, pos);
+ errs |= check_stages((1<<2) | (1<<3), ce, pos);
} else {
errs = 1;
error(_("path '%s' is unmerged"), ce->name);
@@ -288,7 +301,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- resolve_ref("HEAD", rev, 0, &flag);
+ read_ref_full("HEAD", rev, 0, &flag);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -409,9 +422,11 @@ static int merge_working_tree(struct checkout_opts *opts,
topts.gently = opts->merge && old->commit;
topts.verbose_update = !opts->quiet;
topts.fn = twoway_merge;
- topts.dir = xcalloc(1, sizeof(*topts.dir));
- topts.dir->flags |= DIR_SHOW_IGNORED;
- topts.dir->exclude_per_dir = ".gitignore";
+ if (opts->overwrite_ignore) {
+ topts.dir = xcalloc(1, sizeof(*topts.dir));
+ topts.dir->flags |= DIR_SHOW_IGNORED;
+ setup_standard_excludes(topts.dir);
+ }
tree = parse_tree_indirect(old->commit ?
old->commit->object.sha1 :
EMPTY_TREE_SHA1_BIN);
@@ -540,7 +555,9 @@ static void update_refs_for_switch(struct checkout_opts *opts,
else
create_branch(old->name, opts->new_branch, new->name,
opts->new_branch_force ? 1 : 0,
- opts->new_branch_log, opts->track);
+ opts->new_branch_log,
+ opts->new_branch_force ? 1 : 0,
+ opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
}
@@ -565,8 +582,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
create_symref("HEAD", new->path, msg.buf);
if (!opts->quiet) {
if (old->path && !strcmp(new->path, old->path)) {
- fprintf(stderr, _("Already on '%s'\n"),
- new->name);
+ if (opts->new_branch_force)
+ fprintf(stderr, _("Reset branch '%s'\n"),
+ new->name);
+ else
+ fprintf(stderr, _("Already on '%s'\n"),
+ new->name);
} else if (opts->new_branch) {
if (opts->branch_exists)
fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
@@ -696,15 +717,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
{
int ret = 0;
struct branch_info old;
+ void *path_to_free;
unsigned char rev[20];
int flag;
memset(&old, 0, sizeof(old));
- old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
+ old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
old.commit = lookup_commit_reference_gently(rev, 1);
- if (!(flag & REF_ISSYMREF)) {
- free((char *)old.path);
+ if (!(flag & REF_ISSYMREF))
old.path = NULL;
- }
if (old.path && !prefixcmp(old.path, "refs/heads/"))
old.name = old.path + strlen("refs/heads/");
@@ -718,8 +738,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
}
ret = merge_working_tree(opts, &old, new);
- if (ret)
+ if (ret) {
+ free(path_to_free);
return ret;
+ }
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
orphaned_commit_warning(old.commit);
@@ -727,7 +749,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
update_refs_for_switch(opts, &old, new);
ret = post_checkout_hook(old.commit, new->commit, 1);
- free((char *)old.path);
+ free(path_to_free);
return ret || opts->writeout_error;
}
@@ -866,7 +888,7 @@ static int parse_branchname_arg(int argc, const char **argv,
setup_branch_path(new);
if (!check_refname_format(new->path, 0) &&
- resolve_ref(new->path, branch_rev, 1, NULL))
+ !read_ref(new->path, branch_rev))
hashcpy(rev, branch_rev);
else
new->path = NULL; /* not an existing branch */
@@ -926,6 +948,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
3),
OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
+ OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, "update ignored files (default)"),
OPT_STRING(0, "conflict", &conflict_style, "style",
"conflict style (merge or diff3)"),
OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
@@ -937,6 +960,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
memset(&opts, 0, sizeof(opts));
memset(&new, 0, sizeof(new));
+ opts.overwrite_ignore = 1;
gitmodules_config();
git_config(git_checkout_config, &opts);
@@ -1057,7 +1081,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
- !!opts.new_branch_force, 0);
+ !!opts.new_branch_force,
+ !!opts.new_branch_force);
strbuf_release(&buf);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index 488f48e9a..86db95473 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -84,8 +84,8 @@ static struct option builtin_clone_options[] = {
"directory from which templates will be used"),
OPT_CALLBACK(0 , "reference", &option_reference, "repo",
"reference repository", &opt_parse_reference),
- OPT_STRING('o', "origin", &option_origin, "branch",
- "use <branch> instead of 'origin' to track upstream"),
+ OPT_STRING('o', "origin", &option_origin, "name",
+ "use <name> instead of 'origin' to track upstream"),
OPT_STRING('b', "branch", &option_branch, "branch",
"checkout <branch> instead of the remote's HEAD"),
OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
@@ -577,9 +577,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (0 <= option_verbosity) {
if (option_bare)
- printf(_("Cloning into bare repository %s...\n"), dir);
+ printf(_("Cloning into bare repository '%s'...\n"), dir);
else
- printf(_("Cloning into %s...\n"), dir);
+ printf(_("Cloning into '%s'...\n"), dir);
}
init_db(option_template, INIT_DB_QUIET);
write_config(&option_config);
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index d5e19af54..164b655df 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -116,7 +116,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
die_errno("git commit-tree: failed to read");
}
- if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1,
+ if (commit_tree(&buffer, tree_sha1, parents, commit_sha1,
NULL, sign_commit)) {
strbuf_release(&buffer);
return 1;
diff --git a/builtin/commit.c b/builtin/commit.c
index 970a83662..eba1377eb 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -82,7 +82,8 @@ static const char *template_file;
static const char *author_message, *author_message_buffer;
static char *edit_message, *use_message;
static char *fixup_message, *squash_message;
-static int all, edit_flag, also, interactive, patch_interactive, only, amend, signoff;
+static int all, also, interactive, patch_interactive, only, amend, signoff;
+static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
@@ -106,7 +107,7 @@ static enum commit_whence whence;
static int use_editor = 1, include_status = 1;
static int show_ignored_in_status;
static const char *only_include_assumed;
-static struct strbuf message;
+static struct strbuf message = STRBUF_INIT;
static int null_termination;
static enum {
@@ -141,10 +142,10 @@ static struct option builtin_commit_options[] = {
OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
- OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
+ OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_FILENAME('t', "template", &template_file, "use specified template file"),
- OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+ OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
@@ -399,6 +400,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
fd = hold_locked_index(&index_lock, 1);
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
refresh_cache_or_die(refresh_flags);
+ update_main_cache_tree(1);
if (write_cache(fd, active_cache, active_nr) ||
close_lock_file(&index_lock))
die(_("unable to write new_index file"));
@@ -419,6 +421,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
fd = hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
if (active_cache_changed) {
+ update_main_cache_tree(1);
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(&index_lock))
die(_("unable to write new_index file"));
@@ -867,10 +870,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
*/
discard_cache();
read_cache_from(index_file);
- if (!active_cache_tree)
- active_cache_tree = cache_tree();
- if (cache_tree_update(active_cache_tree,
- active_cache, active_nr, 0, 0) < 0) {
+ if (update_main_cache_tree(0)) {
error(_("Error building trees"));
return 0;
}
@@ -1025,8 +1025,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (logfile || message.len || use_message || fixup_message)
use_editor = 0;
- if (edit_flag)
- use_editor = 1;
+ if (0 <= edit_flag)
+ use_editor = edit_flag;
if (!use_editor)
setenv("GIT_EDITOR", ":", 1);
@@ -1264,7 +1264,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
struct commit *commit;
struct strbuf format = STRBUF_INIT;
unsigned char junk_sha1[20];
- const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
+ const char *head;
struct pretty_print_context pctx = {0};
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
@@ -1309,6 +1309,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
+ head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
printf("[%s%s ",
!prefixcmp(head, "refs/heads/") ?
head + 11 :
@@ -1501,7 +1502,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
append_merge_tag_headers(parents, &tail);
}
- if (commit_tree_extended(sb.buf, active_cache_tree->sha1, parents, sha1,
+ if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
author_ident.buf, sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
@@ -1533,6 +1534,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
unlink(git_path("CHERRY_PICK_HEAD"));
+ unlink(git_path("REVERT_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
diff --git a/builtin/config.c b/builtin/config.c
index 0315ad76f..d35c06ae5 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -444,7 +444,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
ret = git_config_set(argv[0], value);
if (ret == CONFIG_NOTHING_SET)
error("cannot overwrite multiple values with a single value\n"
- " Use a regexp, --add or --set-all to change %s.", argv[0]);
+ " Use a regexp, --add or --replace-all to change %s.", argv[0]);
return ret;
}
else if (actions == ACTION_SET_ALL) {
diff --git a/builtin/diff.c b/builtin/diff.c
index 1118689fb..387afa756 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -14,6 +14,7 @@
#include "log-tree.h"
#include "builtin.h"
#include "submodule.h"
+#include "sha1-array.h"
struct blobinfo {
unsigned char sha1[20];
@@ -169,7 +170,7 @@ static int builtin_diff_combined(struct rev_info *revs,
struct object_array_entry *ent,
int ents)
{
- const unsigned char (*parent)[20];
+ struct sha1_array parents = SHA1_ARRAY_INIT;
int i;
if (argc > 1)
@@ -177,12 +178,11 @@ static int builtin_diff_combined(struct rev_info *revs,
if (!revs->dense_combined_merges && !revs->combine_merges)
revs->dense_combined_merges = revs->combine_merges = 1;
- parent = xmalloc(ents * sizeof(*parent));
- for (i = 0; i < ents; i++)
- hashcpy((unsigned char *)(parent + i), ent[i].item->sha1);
- diff_tree_combined(parent[0], parent + 1, ents - 1,
+ for (i = 1; i < ents; i++)
+ sha1_array_append(&parents, ent[i].item->sha1);
+ diff_tree_combined(ent[0].item->sha1, &parents,
revs->dense_combined_merges, revs);
- free(parent);
+ sha1_array_clear(&parents);
return 0;
}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 9836e6b7c..08fed989a 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -25,7 +25,7 @@ static const char *fast_export_usage[] = {
static int progress;
static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
-static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
+static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ERROR;
static int fake_missing_tagger;
static int use_done_feature;
static int no_data;
@@ -51,7 +51,7 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt,
const char *arg, int unset)
{
if (unset || !strcmp(arg, "abort"))
- tag_of_filtered_mode = ABORT;
+ tag_of_filtered_mode = ERROR;
else if (!strcmp(arg, "drop"))
tag_of_filtered_mode = DROP;
else if (!strcmp(arg, "rewrite"))
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index c6bc8eb0a..6207ecd29 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -556,11 +556,16 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
continue;
}
else {
- int order = path_match(ref->name, nr_match, match);
- if (order) {
- return_refs[order-1] = ref;
- continue; /* we will link it later */
+ int i;
+ for (i = 0; i < nr_match; i++) {
+ if (!strcmp(ref->name, match[i])) {
+ match[i][0] = '\0';
+ return_refs[i] = ref;
+ break;
+ }
}
+ if (i < nr_match)
+ continue; /* we will link it later */
}
free(ref);
}
@@ -976,7 +981,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.verbose ? CONNECT_VERBOSE : 0);
}
- get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
+ get_remote_heads(fd[0], &ref, 0, NULL);
ref = fetch_pack(&args, fd, conn, ref, dest,
nr_heads, heads, pack_lockfile_ptr);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index d061e2c95..0481c169c 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -240,23 +240,23 @@ static int s_update_ref(const char *action,
static int update_local_ref(struct ref *ref,
const char *remote,
- char *display)
+ struct strbuf *display)
{
struct commit *current = NULL, *updated;
enum object_type type;
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
- *display = 0;
type = sha1_object_info(ref->new_sha1, NULL);
if (type < 0)
die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
if (verbosity > 0)
- sprintf(display, "= %-*s %-*s -> %s", TRANSPORT_SUMMARY_WIDTH,
- _("[up to date]"), REFCOL_WIDTH, remote,
- pretty_ref);
+ strbuf_addf(display, "= %-*s %-*s -> %s",
+ TRANSPORT_SUMMARY_WIDTH,
+ _("[up to date]"), REFCOL_WIDTH,
+ remote, pretty_ref);
return 0;
}
@@ -268,9 +268,10 @@ static int update_local_ref(struct ref *ref,
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
- sprintf(display, _("! %-*s %-*s -> %s (can't fetch in current branch)"),
- TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
- pretty_ref);
+ strbuf_addf(display,
+ _("! %-*s %-*s -> %s (can't fetch in current branch)"),
+ TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+ REFCOL_WIDTH, remote, pretty_ref);
return 1;
}
@@ -278,9 +279,11 @@ static int update_local_ref(struct ref *ref,
!prefixcmp(ref->name, "refs/tags/")) {
int r;
r = s_update_ref("updating tag", ref, 0);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
- TRANSPORT_SUMMARY_WIDTH, _("[tag update]"), REFCOL_WIDTH, remote,
- pretty_ref, r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : '-',
+ TRANSPORT_SUMMARY_WIDTH, _("[tag update]"),
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
}
@@ -303,9 +306,11 @@ static int update_local_ref(struct ref *ref,
}
r = s_update_ref(msg, ref, 0);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
- TRANSPORT_SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : '*',
+ TRANSPORT_SUMMARY_WIDTH, what,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
}
@@ -319,9 +324,11 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("fast-forward", ref, 1);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
- TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
- pretty_ref, r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : ' ',
+ TRANSPORT_SUMMARY_WIDTH, quickref,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
} else if (force || ref->force) {
char quickref[84];
@@ -333,15 +340,17 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("forced-update", ref, 1);
- sprintf(display, "%c %-*s %-*s -> %s (%s)", r ? '!' : '+',
- TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
- pretty_ref,
- r ? _("unable to update local ref") : _("forced update"));
+ strbuf_addf(display, "%c %-*s %-*s -> %s (%s)",
+ r ? '!' : '+',
+ TRANSPORT_SUMMARY_WIDTH, quickref,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _("unable to update local ref") : _("forced update"));
return r;
} else {
- sprintf(display, "! %-*s %-*s -> %s %s",
- TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
- pretty_ref, _("(non-fast-forward)"));
+ strbuf_addf(display, "! %-*s %-*s -> %s %s",
+ TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+ REFCOL_WIDTH, remote, pretty_ref,
+ _("(non-fast-forward)"));
return 1;
}
}
@@ -363,11 +372,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
{
FILE *fp;
struct commit *commit;
- int url_len, i, note_len, shown_url = 0, rc = 0;
- char note[1024];
+ int url_len, i, shown_url = 0, rc = 0;
+ struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+ int want_merge;
fp = fopen(filename, "a");
if (!fp)
@@ -384,83 +394,95 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
goto abort;
}
- for (rm = ref_map; rm; rm = rm->next) {
- struct ref *ref = NULL;
-
- if (rm->peer_ref) {
- ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
- strcpy(ref->name, rm->peer_ref->name);
- hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
- hashcpy(ref->new_sha1, rm->old_sha1);
- ref->force = rm->peer_ref->force;
- }
+ /*
+ * The first pass writes objects to be merged and then the
+ * second pass writes the rest, in order to allow using
+ * FETCH_HEAD as a refname to refer to the ref to be merged.
+ */
+ for (want_merge = 1; 0 <= want_merge; want_merge--) {
+ for (rm = ref_map; rm; rm = rm->next) {
+ struct ref *ref = NULL;
+
+ commit = lookup_commit_reference_gently(rm->old_sha1, 1);
+ if (!commit)
+ rm->merge = 0;
+
+ if (rm->merge != want_merge)
+ continue;
+
+ if (rm->peer_ref) {
+ ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
+ strcpy(ref->name, rm->peer_ref->name);
+ hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
+ hashcpy(ref->new_sha1, rm->old_sha1);
+ ref->force = rm->peer_ref->force;
+ }
- commit = lookup_commit_reference_gently(rm->old_sha1, 1);
- if (!commit)
- rm->merge = 0;
- if (!strcmp(rm->name, "HEAD")) {
- kind = "";
- what = "";
- }
- else if (!prefixcmp(rm->name, "refs/heads/")) {
- kind = "branch";
- what = rm->name + 11;
- }
- else if (!prefixcmp(rm->name, "refs/tags/")) {
- kind = "tag";
- what = rm->name + 10;
- }
- else if (!prefixcmp(rm->name, "refs/remotes/")) {
- kind = "remote-tracking branch";
- what = rm->name + 13;
- }
- else {
- kind = "";
- what = rm->name;
- }
+ if (!strcmp(rm->name, "HEAD")) {
+ kind = "";
+ what = "";
+ }
+ else if (!prefixcmp(rm->name, "refs/heads/")) {
+ kind = "branch";
+ what = rm->name + 11;
+ }
+ else if (!prefixcmp(rm->name, "refs/tags/")) {
+ kind = "tag";
+ what = rm->name + 10;
+ }
+ else if (!prefixcmp(rm->name, "refs/remotes/")) {
+ kind = "remote-tracking branch";
+ what = rm->name + 13;
+ }
+ else {
+ kind = "";
+ what = rm->name;
+ }
- url_len = strlen(url);
- for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
- ;
- url_len = i + 1;
- if (4 < i && !strncmp(".git", url + i - 3, 4))
- url_len = i - 3;
-
- note_len = 0;
- if (*what) {
- if (*kind)
- note_len += sprintf(note + note_len, "%s ",
- kind);
- note_len += sprintf(note + note_len, "'%s' of ", what);
- }
- note[note_len] = '\0';
- fprintf(fp, "%s\t%s\t%s",
- sha1_to_hex(rm->old_sha1),
- rm->merge ? "" : "not-for-merge",
- note);
- for (i = 0; i < url_len; ++i)
- if ('\n' == url[i])
- fputs("\\n", fp);
- else
- fputc(url[i], fp);
- fputc('\n', fp);
-
- if (ref) {
- rc |= update_local_ref(ref, what, note);
- free(ref);
- } else
- sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
- TRANSPORT_SUMMARY_WIDTH, *kind ? kind : "branch",
- REFCOL_WIDTH, *what ? what : "HEAD");
- if (*note) {
- if (verbosity >= 0 && !shown_url) {
- fprintf(stderr, _("From %.*s\n"),
- url_len, url);
- shown_url = 1;
+ url_len = strlen(url);
+ for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+ ;
+ url_len = i + 1;
+ if (4 < i && !strncmp(".git", url + i - 3, 4))
+ url_len = i - 3;
+
+ strbuf_reset(&note);
+ if (*what) {
+ if (*kind)
+ strbuf_addf(&note, "%s ", kind);
+ strbuf_addf(&note, "'%s' of ", what);
+ }
+ fprintf(fp, "%s\t%s\t%s",
+ sha1_to_hex(rm->old_sha1),
+ rm->merge ? "" : "not-for-merge",
+ note.buf);
+ for (i = 0; i < url_len; ++i)
+ if ('\n' == url[i])
+ fputs("\\n", fp);
+ else
+ fputc(url[i], fp);
+ fputc('\n', fp);
+
+ strbuf_reset(&note);
+ if (ref) {
+ rc |= update_local_ref(ref, what, &note);
+ free(ref);
+ } else
+ strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
+ TRANSPORT_SUMMARY_WIDTH,
+ *kind ? kind : "branch",
+ REFCOL_WIDTH,
+ *what ? what : "HEAD");
+ if (note.len) {
+ if (verbosity >= 0 && !shown_url) {
+ fprintf(stderr, _("From %.*s\n"),
+ url_len, url);
+ shown_url = 1;
+ }
+ if (verbosity >= 0)
+ fprintf(stderr, " %s\n", note.buf);
}
- if (verbosity >= 0)
- fprintf(stderr, " %s\n", note);
}
}
@@ -470,6 +492,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
"branches"), remote_name);
abort:
+ strbuf_release(&note);
free(url);
fclose(fp);
return rc;
@@ -509,10 +532,10 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
return ret;
}
-static int prune_refs(struct transport *transport, struct ref *ref_map)
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
{
int result = 0;
- struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
+ struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
const char *dangling_msg = dry_run
? _(" (%s will become dangling)\n")
: _(" (%s has become dangling)\n");
@@ -703,8 +726,31 @@ static int do_fetch(struct transport *transport,
free_refs(ref_map);
return 1;
}
- if (prune)
- prune_refs(transport, ref_map);
+ if (prune) {
+ /* If --tags was specified, pretend the user gave us the canonical tags refspec */
+ if (tags == TAGS_SET) {
+ const char *tags_str = "refs/tags/*:refs/tags/*";
+ struct refspec *tags_refspec, *refspec;
+
+ /* Copy the refspec and add the tags to it */
+ refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
+ tags_refspec = parse_fetch_refspec(1, &tags_str);
+ memcpy(refspec, refs, ref_count * sizeof(struct refspec));
+ memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
+ ref_count++;
+
+ prune_refs(refspec, ref_count, ref_map);
+
+ ref_count--;
+ /* The rest of the strings belong to fetch_one */
+ free_refspec(1, tags_refspec);
+ free(refspec);
+ } else if (ref_count) {
+ prune_refs(refs, ref_count, ref_map);
+ } else {
+ prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
+ }
+ }
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -887,7 +933,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
atexit(unlock_pack);
refspec = parse_fetch_refspec(ref_nr, refs);
exit_code = do_fetch(transport, refspec, ref_nr);
- free(refspec);
+ free_refspec(ref_nr, refspec);
transport_disconnect(transport);
transport = NULL;
return exit_code;
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 7dae846c5..c81a7fef2 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -5,6 +5,8 @@
#include "revision.h"
#include "tag.h"
#include "string-list.h"
+#include "branch.h"
+#include "fmt-merge-msg.h"
#include "gpg-interface.h"
static const char * const fmt_merge_msg_usage[] = {
@@ -12,17 +14,19 @@ static const char * const fmt_merge_msg_usage[] = {
NULL
};
-static int shortlog_len;
+static int use_branch_desc;
-static int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+int fmt_merge_msg_config(const char *key, const char *value, void *cb)
{
if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
int is_bool;
- shortlog_len = git_config_bool_or_int(key, value, &is_bool);
- if (!is_bool && shortlog_len < 0)
+ merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+ if (!is_bool && merge_log_config < 0)
return error("%s: negative length %s", key, value);
- if (is_bool && shortlog_len)
- shortlog_len = DEFAULT_MERGE_LOG_LEN;
+ if (is_bool && merge_log_config)
+ merge_log_config = DEFAULT_MERGE_LOG_LEN;
+ } else if (!strcmp(key, "merge.branchdesc")) {
+ use_branch_desc = git_config_bool(key, value);
}
return 0;
}
@@ -33,6 +37,11 @@ struct src_data {
int head_status;
};
+struct origin_data {
+ unsigned char sha1[20];
+ unsigned is_local_branch:1;
+};
+
static void init_src_data(struct src_data *data)
{
data->branch.strdup_strings = 1;
@@ -47,7 +56,7 @@ static struct string_list origins = STRING_LIST_INIT_DUP;
static int handle_line(char *line)
{
int i, len = strlen(line);
- unsigned char *sha1;
+ struct origin_data *origin_data;
char *src, *origin;
struct src_data *src_data;
struct string_list_item *item;
@@ -63,11 +72,13 @@ static int handle_line(char *line)
return 2;
line[40] = 0;
- sha1 = xmalloc(20);
- i = get_sha1(line, sha1);
+ origin_data = xcalloc(1, sizeof(struct origin_data));
+ i = get_sha1(line, origin_data->sha1);
line[40] = '\t';
- if (i)
+ if (i) {
+ free(origin_data);
return 3;
+ }
if (line[len - 1] == '\n')
line[len - 1] = 0;
@@ -100,6 +111,7 @@ static int handle_line(char *line)
origin = src;
src_data->head_status |= 1;
} else if (!prefixcmp(line, "branch ")) {
+ origin_data->is_local_branch = 1;
origin = line + 7;
string_list_append(&src_data->branch, origin);
src_data->head_status |= 2;
@@ -126,7 +138,9 @@ static int handle_line(char *line)
sprintf(new_origin, "%s of %s", origin, src);
origin = new_origin;
}
- string_list_append(&origins, origin)->util = sha1;
+ if (strcmp(".", src))
+ origin_data->is_local_branch = 0;
+ string_list_append(&origins, origin)->util = origin_data;
return 0;
}
@@ -147,9 +161,30 @@ static void print_joined(const char *singular, const char *plural,
}
}
-static void shortlog(const char *name, unsigned char *sha1,
- struct commit *head, struct rev_info *rev, int limit,
- struct strbuf *out)
+static void add_branch_desc(struct strbuf *out, const char *name)
+{
+ struct strbuf desc = STRBUF_INIT;
+
+ if (!read_branch_desc(&desc, name)) {
+ const char *bp = desc.buf;
+ while (*bp) {
+ const char *ep = strchrnul(bp, '\n');
+ if (*ep)
+ ep++;
+ strbuf_addf(out, " : %.*s", (int)(ep - bp), bp);
+ bp = ep;
+ }
+ if (out->buf[out->len - 1] != '\n')
+ strbuf_addch(out, '\n');
+ }
+ strbuf_release(&desc);
+}
+
+static void shortlog(const char *name,
+ struct origin_data *origin_data,
+ struct commit *head,
+ struct rev_info *rev, int limit,
+ struct strbuf *out)
{
int i, count = 0;
struct commit *commit;
@@ -157,6 +192,7 @@ static void shortlog(const char *name, unsigned char *sha1,
struct string_list subjects = STRING_LIST_INIT_DUP;
int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
struct strbuf sb = STRBUF_INIT;
+ const unsigned char *sha1 = origin_data->sha1;
branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
if (!branch || branch->type != OBJ_COMMIT)
@@ -195,6 +231,9 @@ static void shortlog(const char *name, unsigned char *sha1,
else
strbuf_addf(out, "\n* %s:\n", name);
+ if (origin_data->is_local_branch && use_branch_desc)
+ add_branch_desc(out, name);
+
for (i = 0; i < subjects.nr; i++)
if (i >= limit)
strbuf_addf(out, " ...\n");
@@ -333,9 +372,11 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
int i = 0, pos = 0;
unsigned char head_sha1[20];
const char *current_branch;
+ void *current_branch_to_free;
/* get current branch */
- current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+ current_branch = current_branch_to_free =
+ resolve_refdup("HEAD", head_sha1, 1, NULL);
if (!current_branch)
die("No current branch");
if (!prefixcmp(current_branch, "refs/heads/"))
@@ -375,11 +416,13 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
strbuf_addch(out, '\n');
for (i = 0; i < origins.nr; i++)
- shortlog(origins.items[i].string, origins.items[i].util,
+ shortlog(origins.items[i].string,
+ origins.items[i].util,
head, &rev, opts->shortlog_len, out);
}
strbuf_complete_line(out);
+ free(current_branch_to_free);
return 0;
}
@@ -387,6 +430,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
const char *inpath = NULL;
const char *message = NULL;
+ int shortlog_len = -1;
struct option options[] = {
{ OPTION_INTEGER, 0, "log", &shortlog_len, "n",
"populate log with at most <n> entries from shortlog",
@@ -411,9 +455,8 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
0);
if (argc > 0)
usage_with_options(fmt_merge_msg_usage, options);
-
if (shortlog_len < 0)
- die("Negative --log=%d", shortlog_len);
+ shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
if (inpath && strcmp(inpath, "-")) {
in = fopen(inpath, "r");
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index d90e5d2b2..b01d76a24 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -628,11 +628,8 @@ static void populate_value(struct refinfo *ref)
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
unsigned char unused1[20];
- const char *symref;
- symref = resolve_ref(ref->refname, unused1, 1, NULL);
- if (symref)
- ref->symref = xstrdup(symref);
- else
+ ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+ if (!ref->symref)
ref->symref = "";
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index df1a88b51..8c479a791 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -11,6 +11,7 @@
#include "fsck.h"
#include "parse-options.h"
#include "dir.h"
+#include "progress.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -27,8 +28,10 @@ static const char *head_points_at;
static int errors_found;
static int write_lost_and_found;
static int verbose;
+static int show_progress = -1;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
+#define ERROR_PACK 04
#ifdef NO_D_INO_IN_DIRENT
#define SORT_DIRENT 0
@@ -137,7 +140,11 @@ static int traverse_one_object(struct object *obj)
static int traverse_reachable(void)
{
+ struct progress *progress = NULL;
+ unsigned int nr = 0;
int result = 0;
+ if (show_progress)
+ progress = start_progress_delay("Checking connectivity", 0, 0, 2);
while (pending.nr) {
struct object_array_entry *entry;
struct object *obj;
@@ -145,7 +152,9 @@ static int traverse_reachable(void)
entry = pending.objects + --pending.nr;
obj = entry->item;
result |= traverse_one_object(obj);
+ display_progress(progress, ++nr);
}
+ stop_progress(&progress);
return !!result;
}
@@ -281,14 +290,8 @@ static void check_connectivity(void)
}
}
-static int fsck_sha1(const unsigned char *sha1)
+static int fsck_obj(struct object *obj)
{
- struct object *obj = parse_object(sha1);
- if (!obj) {
- errors_found |= ERROR_OBJECT;
- return error("%s: object corrupt or missing",
- sha1_to_hex(sha1));
- }
if (obj->flags & SEEN)
return 0;
obj->flags |= SEEN;
@@ -331,6 +334,29 @@ static int fsck_sha1(const unsigned char *sha1)
return 0;
}
+static int fsck_sha1(const unsigned char *sha1)
+{
+ struct object *obj = parse_object(sha1);
+ if (!obj) {
+ errors_found |= ERROR_OBJECT;
+ return error("%s: object corrupt or missing",
+ sha1_to_hex(sha1));
+ }
+ return fsck_obj(obj);
+}
+
+static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
+ unsigned long size, void *buffer, int *eaten)
+{
+ struct object *obj;
+ obj = parse_object_buffer(sha1, type, size, buffer, eaten);
+ if (!obj) {
+ errors_found |= ERROR_OBJECT;
+ return error("%s: object corrupt or missing", sha1_to_hex(sha1));
+ }
+ return fsck_obj(obj);
+}
+
/*
* This is the sorting chunk size: make it reasonably
* big so that we can sort well..
@@ -512,15 +538,20 @@ static void get_default_heads(void)
static void fsck_object_dir(const char *path)
{
int i;
+ struct progress *progress = NULL;
if (verbose)
fprintf(stderr, "Checking object directory\n");
+ if (show_progress)
+ progress = start_progress("Checking object directories", 256);
for (i = 0; i < 256; i++) {
static char dir[4096];
sprintf(dir, "%s/%02x", path, i);
fsck_dir(i, dir);
+ display_progress(progress, i+1);
}
+ stop_progress(&progress);
fsck_sha1_list();
}
@@ -532,7 +563,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
@@ -591,6 +622,7 @@ static struct option fsck_opts[] = {
OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"),
OPT_BOOLEAN(0, "lost-found", &write_lost_and_found,
"write dangling objects in .git/lost-found"),
+ OPT_BOOL(0, "progress", &show_progress, "show progress"),
OPT_END(),
};
@@ -603,6 +635,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
read_replace_refs = 0;
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
+
+ if (show_progress == -1)
+ show_progress = isatty(2);
+ if (verbose)
+ show_progress = 0;
+
if (write_lost_and_found) {
check_full = 1;
include_reflogs = 0;
@@ -622,20 +660,28 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
if (check_full) {
struct packed_git *p;
+ uint32_t total = 0, count = 0;
+ struct progress *progress = NULL;
prepare_packed_git();
- for (p = packed_git; p; p = p->next)
- /* verify gives error messages itself */
- verify_pack(p);
+ if (show_progress) {
+ for (p = packed_git; p; p = p->next) {
+ if (open_pack_index(p))
+ continue;
+ total += p->num_objects;
+ }
+
+ progress = start_progress("Checking objects", total);
+ }
for (p = packed_git; p; p = p->next) {
- uint32_t j, num;
- if (open_pack_index(p))
- continue;
- num = p->num_objects;
- for (j = 0; j < num; j++)
- fsck_sha1(nth_packed_object_sha1(p, j));
+ /* verify gives error messages itself */
+ if (verify_pack(p, fsck_obj_buffer,
+ progress, count))
+ errors_found |= ERROR_PACK;
+ count += p->num_objects;
}
+ stop_progress(&progress);
}
heads = 0;
diff --git a/builtin/gc.c b/builtin/gc.c
index 049809471..271376d82 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -32,7 +32,7 @@ static const char *prune_expire = "2.weeks.ago";
static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
-static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
+static const char *argv_prune[] = {"prune", "--expire", NULL, NULL, NULL};
static const char *argv_rerere[] = {"rerere", "gc", NULL};
static int gc_config(const char *var, const char *value, void *cb)
@@ -243,6 +243,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (prune_expire) {
argv_prune[2] = prune_expire;
+ if (quiet)
+ argv_prune[3] = "--no-progress";
if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
return error(FAILED_RUN, argv_prune[0]);
}
diff --git a/builtin/grep.c b/builtin/grep.c
index 7d0779f6c..9ce064ac1 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -17,7 +17,6 @@
#include "grep.h"
#include "quote.h"
#include "dir.h"
-#include "thread-utils.h"
static char const * const grep_usage[] = {
"git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]",
@@ -74,13 +73,32 @@ static int all_work_added;
/* This lock protects all the variables above. */
static pthread_mutex_t grep_mutex;
+static inline void grep_lock(void)
+{
+ if (use_threads)
+ pthread_mutex_lock(&grep_mutex);
+}
+
+static inline void grep_unlock(void)
+{
+ if (use_threads)
+ pthread_mutex_unlock(&grep_mutex);
+}
+
/* Used to serialize calls to read_sha1_file. */
static pthread_mutex_t read_sha1_mutex;
-#define grep_lock() pthread_mutex_lock(&grep_mutex)
-#define grep_unlock() pthread_mutex_unlock(&grep_mutex)
-#define read_sha1_lock() pthread_mutex_lock(&read_sha1_mutex)
-#define read_sha1_unlock() pthread_mutex_unlock(&read_sha1_mutex)
+static inline void read_sha1_lock(void)
+{
+ if (use_threads)
+ pthread_mutex_lock(&read_sha1_mutex);
+}
+
+static inline void read_sha1_unlock(void)
+{
+ if (use_threads)
+ pthread_mutex_unlock(&read_sha1_mutex);
+}
/* Signalled when a new work_item is added to todo. */
static pthread_cond_t cond_add;
@@ -237,6 +255,7 @@ static void start_threads(struct grep_opt *opt)
pthread_mutex_init(&grep_mutex, NULL);
pthread_mutex_init(&read_sha1_mutex, NULL);
+ pthread_mutex_init(&grep_attr_mutex, NULL);
pthread_cond_init(&cond_add, NULL);
pthread_cond_init(&cond_write, NULL);
pthread_cond_init(&cond_result, NULL);
@@ -284,6 +303,7 @@ static int wait_all(void)
pthread_mutex_destroy(&grep_mutex);
pthread_mutex_destroy(&read_sha1_mutex);
+ pthread_mutex_destroy(&grep_attr_mutex);
pthread_cond_destroy(&cond_add);
pthread_cond_destroy(&cond_write);
pthread_cond_destroy(&cond_result);
@@ -354,13 +374,9 @@ static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type
{
void *data;
- if (use_threads) {
- read_sha1_lock();
- data = read_sha1_file(sha1, type, size);
- read_sha1_unlock();
- } else {
- data = read_sha1_file(sha1, type, size);
- }
+ read_sha1_lock();
+ data = read_sha1_file(sha1, type, size);
+ read_sha1_unlock();
return data;
}
@@ -542,18 +558,19 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
struct tree_desc *tree, struct strbuf *base, int tn_len)
{
- int hit = 0, match = 0;
+ int hit = 0;
+ enum interesting match = entry_not_interesting;
struct name_entry entry;
int old_baselen = base->len;
while (tree_entry(tree, &entry)) {
- int te_len = tree_entry_len(entry.path, entry.sha1);
+ int te_len = tree_entry_len(&entry);
- if (match != 2) {
+ if (match != all_entries_interesting) {
match = tree_entry_interesting(&entry, base, tn_len, pathspec);
- if (match < 0)
+ if (match == all_entries_not_interesting)
break;
- if (match == 0)
+ if (match == entry_not_interesting)
continue;
}
@@ -985,20 +1002,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!opt.fixed && opt.ignore_case)
opt.regflags |= REG_ICASE;
-#ifndef NO_PTHREADS
- if (online_cpus() == 1 || !grep_threads_ok(&opt))
- use_threads = 0;
-
- if (use_threads) {
- if (opt.pre_context || opt.post_context || opt.file_break ||
- opt.funcbody)
- skip_first_line = 1;
- start_threads(&opt);
- }
-#else
- use_threads = 0;
-#endif
-
compile_grep_patterns(&opt);
/* Check revs and then paths */
@@ -1020,6 +1023,24 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
break;
}
+#ifndef NO_PTHREADS
+ if (list.nr || cached || online_cpus() == 1)
+ use_threads = 0;
+#else
+ use_threads = 0;
+#endif
+
+ opt.use_threads = use_threads;
+
+#ifndef NO_PTHREADS
+ if (use_threads) {
+ if (opt.pre_context || opt.post_context || opt.file_break ||
+ opt.funcbody)
+ skip_first_line = 1;
+ start_threads(&opt);
+ }
+#endif
+
/* The rest are paths */
if (!seen_dashdash) {
int j;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 0945adbb3..af7dc37a4 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -172,10 +172,10 @@ static const char *open_pack_file(const char *pack_name)
if (from_stdin) {
input_fd = 0;
if (!pack_name) {
- static char tmpfile[PATH_MAX];
- output_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
+ static char tmp_file[PATH_MAX];
+ output_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
"pack/tmp_pack_XXXXXX");
- pack_name = xstrdup(tmpfile);
+ pack_name = xstrdup(tmp_file);
} else
output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
if (output_fd < 0)
@@ -1122,8 +1122,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (!index_name)
die("--verify with no packfile name given");
read_idx_option(&opts, index_name);
- opts.flags |= WRITE_IDX_VERIFY;
+ opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT;
}
+ if (strict)
+ opts.flags |= WRITE_IDX_STRICT;
curr_pack = open_pack_file(pack_name);
parse_pack_header();
diff --git a/builtin/init-db.c b/builtin/init-db.c
index d07554c88..0dacb8b79 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -351,7 +351,7 @@ static void separate_git_dir(const char *git_dir)
else if (S_ISDIR(st.st_mode))
src = git_link;
else
- die(_("unable to handle file type %d"), st.st_mode);
+ die(_("unable to handle file type %d"), (int)st.st_mode);
if (rename(src, git_dir))
die_errno(_("unable to move %s to %s"), src, git_dir);
diff --git a/builtin/log.c b/builtin/log.c
index f5d493059..7d1f6f88a 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -19,6 +19,7 @@
#include "remote.h"
#include "string-list.h"
#include "parse-options.h"
+#include "branch.h"
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@@ -72,8 +73,6 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse
static void cmd_log_init_defaults(struct rev_info *rev)
{
- rev->abbrev = DEFAULT_ABBREV;
- rev->commit_format = CMIT_FMT_DEFAULT;
if (fmt_pretty)
get_commit_format(fmt_pretty, rev);
rev->verbose_header = 1;
@@ -746,10 +745,24 @@ static void print_signature(void)
printf("-- \n%s\n\n", signature);
}
+static void add_branch_description(struct strbuf *buf, const char *branch_name)
+{
+ struct strbuf desc = STRBUF_INIT;
+ if (!branch_name || !*branch_name)
+ return;
+ read_branch_desc(&desc, branch_name);
+ if (desc.len) {
+ strbuf_addch(buf, '\n');
+ strbuf_add(buf, desc.buf, desc.len);
+ strbuf_addch(buf, '\n');
+ }
+}
+
static void make_cover_letter(struct rev_info *rev, int use_stdout,
int numbered, int numbered_files,
struct commit *origin,
int nr, struct commit **list, struct commit *head,
+ const char *branch_name,
int quiet)
{
const char *committer;
@@ -807,6 +820,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
pp_user_info(&pp, NULL, &sb, committer, encoding);
pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
pp_remainder(&pp, &msg, &sb, 0);
+ add_branch_description(&sb, branch_name);
printf("%s\n", sb.buf);
strbuf_release(&sb);
@@ -1006,6 +1020,35 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+static char *find_branch_name(struct rev_info *rev)
+{
+ int i, positive = -1;
+ unsigned char branch_sha1[20];
+ struct strbuf buf = STRBUF_INIT;
+ const char *branch;
+
+ for (i = 0; i < rev->cmdline.nr; i++) {
+ if (rev->cmdline.rev[i].flags & UNINTERESTING)
+ continue;
+ if (positive < 0)
+ positive = i;
+ else
+ return NULL;
+ }
+ if (positive < 0)
+ return NULL;
+ strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name);
+ branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL);
+ if (!branch ||
+ prefixcmp(branch, "refs/heads/") ||
+ hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1))
+ branch = NULL;
+ strbuf_release(&buf);
+ if (branch)
+ return xstrdup(rev->cmdline.rev[positive].name);
+ return NULL;
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -1027,6 +1070,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
int use_patch_format = 0;
int quiet = 0;
+ char *branch_name = NULL;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
"use [PATCH n/m] even with a single patch",
@@ -1217,8 +1261,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* origin" that prepares what the origin side still
* does not have.
*/
+ unsigned char sha1[20];
+ const char *ref;
+
rev.pending.objects[0].item->flags |= UNINTERESTING;
add_head_to_pending(&rev);
+ ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ if (ref && !prefixcmp(ref, "refs/heads/"))
+ branch_name = xstrdup(ref + strlen("refs/heads/"));
+ else
+ branch_name = xstrdup(""); /* no branch */
}
/*
* Otherwise, it is "format-patch -22 HEAD", and/or
@@ -1234,16 +1286,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.show_root_diff = 1;
if (cover_letter) {
- /* remember the range */
+ /*
+ * NEEDSWORK:randomly pick one positive commit to show
+ * diffstat; this is often the tip and the command
+ * happens to do the right thing in most cases, but a
+ * complex command like "--cover-letter a b c ^bottom"
+ * picks "c" and shows diffstat between bottom..c
+ * which may not match what the series represents at
+ * all and totally broken.
+ */
int i;
for (i = 0; i < rev.pending.nr; i++) {
struct object *o = rev.pending.objects[i].item;
if (!(o->flags & UNINTERESTING))
head = (struct commit *)o;
}
- /* We can't generate a cover letter without any patches */
+ /* There is nothing to show; it is not an error, though. */
if (!head)
return 0;
+ if (!branch_name)
+ branch_name = find_branch_name(&rev);
}
if (ignore_if_in_upstream) {
@@ -1294,7 +1356,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout, numbered, numbered_files,
- origin, nr, list, head, quiet);
+ origin, nr, list, head, branch_name, quiet);
total++;
start_number--;
}
@@ -1366,6 +1428,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
fclose(stdout);
}
free(list);
+ free(branch_name);
string_list_clear(&extra_to, 0);
string_list_clear(&extra_cc, 0);
string_list_clear(&extra_hdr, 0);
diff --git a/builtin/merge.c b/builtin/merge.c
index e5afe64ce..3a451727d 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -26,6 +26,7 @@
#include "merge-recursive.h"
#include "resolve-undo.h"
#include "remote.h"
+#include "fmt-merge-msg.h"
#include "gpg-interface.h"
#define DEFAULT_TWOHEAD (1<<0)
@@ -45,11 +46,12 @@ static const char * const builtin_merge_usage[] = {
NULL
};
-static int show_diffstat = 1, shortlog_len, squash;
+static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = 1, allow_fast_forward = 1;
static int fast_forward_only, option_edit;
static int allow_trivial = 1, have_message;
-static struct strbuf merge_msg;
+static int overwrite_ignore = 1;
+static struct strbuf merge_msg = STRBUF_INIT;
static struct commit_list *remoteheads;
static struct strategy **use_strategies;
static size_t use_strategies_nr, use_strategies_alloc;
@@ -211,6 +213,7 @@ static struct option builtin_merge_options[] = {
OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
"GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
OPT_END()
};
@@ -320,13 +323,15 @@ static void squash_message(struct commit *commit)
struct rev_info rev;
struct strbuf out = STRBUF_INIT;
struct commit_list *j;
+ const char *filename;
int fd;
struct pretty_print_context ctx = {0};
printf(_("Squash commit -- not updating HEAD\n"));
- fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+ filename = git_path("SQUASH_MSG");
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not write to '%s'"), git_path("SQUASH_MSG"));
+ die_errno(_("Could not write to '%s'"), filename);
init_revisions(&rev, NULL);
rev.ignore_merges = 1;
@@ -414,7 +419,7 @@ static void finish(struct commit *head_commit,
static void merge_name(const char *remote, struct strbuf *msg)
{
struct commit *remote_head;
- unsigned char branch_head[20], buf_sha[20];
+ unsigned char branch_head[20];
struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
const char *ptr;
@@ -478,7 +483,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_addstr(&truname, "refs/heads/");
strbuf_addstr(&truname, remote);
strbuf_setlen(&truname, truname.len - len);
- if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
+ if (ref_exists(truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
sha1_to_hex(remote_head->object.sha1),
@@ -491,14 +496,16 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (!strcmp(remote, "FETCH_HEAD") &&
!access(git_path("FETCH_HEAD"), R_OK)) {
+ const char *filename;
FILE *fp;
struct strbuf line = STRBUF_INIT;
char *ptr;
- fp = fopen(git_path("FETCH_HEAD"), "r");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "r");
if (!fp)
die_errno(_("could not open '%s' for reading"),
- git_path("FETCH_HEAD"));
+ filename);
strbuf_getline(&line, fp, '\n');
fclose(fp);
ptr = strstr(line.buf, "\tnot-for-merge\t");
@@ -555,15 +562,7 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return git_config_string(&pull_octopus, k, v);
else if (!strcmp(k, "merge.renormalize"))
option_renormalize = git_config_bool(k, v);
- else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
- int is_bool;
- shortlog_len = git_config_bool_or_int(k, v, &is_bool);
- if (!is_bool && shortlog_len < 0)
- return error(_("%s: negative length %s"), k, v);
- if (is_bool && shortlog_len)
- shortlog_len = DEFAULT_MERGE_LOG_LEN;
- return 0;
- } else if (!strcmp(k, "merge.ff")) {
+ else if (!strcmp(k, "merge.ff")) {
int boolval = git_config_maybe_bool(k, v);
if (0 <= boolval) {
allow_fast_forward = boolval;
@@ -577,6 +576,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return 0;
}
+ status = fmt_merge_msg_config(k, v, cb);
+ if (status)
+ return status;
status = git_gpg_config(k, v, NULL);
if (status)
return status;
@@ -774,10 +776,12 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
memset(&trees, 0, sizeof(trees));
memset(&opts, 0, sizeof(opts));
memset(&t, 0, sizeof(t));
- memset(&dir, 0, sizeof(dir));
- dir.flags |= DIR_SHOW_IGNORED;
- dir.exclude_per_dir = ".gitignore";
- opts.dir = &dir;
+ if (overwrite_ignore) {
+ memset(&dir, 0, sizeof(dir));
+ dir.flags |= DIR_SHOW_IGNORED;
+ setup_standard_excludes(&dir);
+ opts.dir = &dir;
+ }
opts.head_idx = 1;
opts.src_index = &the_index;
@@ -852,20 +856,22 @@ static void add_strategies(const char *string, unsigned attr)
static void write_merge_msg(struct strbuf *msg)
{
- int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+ const char *filename = git_path("MERGE_MSG");
+ int fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MSG"));
+ filename);
if (write_in_full(fd, msg->buf, msg->len) != msg->len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG"));
+ die_errno(_("Could not write to '%s'"), filename);
close(fd);
}
static void read_merge_msg(struct strbuf *msg)
{
+ const char *filename = git_path("MERGE_MSG");
strbuf_reset(msg);
- if (strbuf_read_file(msg, git_path("MERGE_MSG"), 0) < 0)
- die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG"));
+ if (strbuf_read_file(msg, filename, 0) < 0)
+ die_errno(_("Could not read from '%s'"), filename);
}
static void write_merge_state(void);
@@ -912,8 +918,9 @@ static int merge_trivial(struct commit *head)
parent->next->item = remoteheads->item;
parent->next->next = NULL;
prepare_to_commit();
- commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL,
- sign_commit);
+ if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
+ sign_commit))
+ die(_("failed to write commit object"));
finish(head, result_commit, "In-index merge");
drop_save();
return 0;
@@ -944,8 +951,9 @@ static int finish_automerge(struct commit *head,
strbuf_addch(&merge_msg, '\n');
prepare_to_commit();
free_commit_list(remoteheads);
- commit_tree(merge_msg.buf, result_tree, parents, result_commit,
- NULL, sign_commit);
+ if (commit_tree(&merge_msg, result_tree, parents, result_commit,
+ NULL, sign_commit))
+ die(_("failed to write commit object"));
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
finish(head, result_commit, buf.buf);
strbuf_release(&buf);
@@ -955,13 +963,14 @@ static int finish_automerge(struct commit *head,
static int suggest_conflicts(int renormalizing)
{
+ const char *filename;
FILE *fp;
int pos;
- fp = fopen(git_path("MERGE_MSG"), "a");
+ filename = git_path("MERGE_MSG");
+ fp = fopen(filename, "a");
if (!fp)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MSG"));
+ die_errno(_("Could not open '%s' for writing"), filename);
fprintf(fp, "\nConflicts:\n");
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
@@ -1053,6 +1062,7 @@ static int setup_with_upstream(const char ***argv)
static void write_merge_state(void)
{
+ const char *filename;
int fd;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
@@ -1067,24 +1077,25 @@ static void write_merge_state(void)
}
strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
}
- fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+ filename = git_path("MERGE_HEAD");
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_HEAD"));
+ die_errno(_("Could not open '%s' for writing"), filename);
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD"));
+ die_errno(_("Could not write to '%s'"), filename);
close(fd);
strbuf_addch(&merge_msg, '\n');
write_merge_msg(&merge_msg);
- fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ filename = git_path("MERGE_MODE");
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MODE"));
+ die_errno(_("Could not open '%s' for writing"), filename);
strbuf_reset(&buf);
if (!allow_fast_forward)
strbuf_addf(&buf, "no-ff");
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE"));
+ die_errno(_("Could not write to '%s'"), filename);
close(fd);
}
@@ -1096,11 +1107,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit *head_commit;
struct strbuf buf = STRBUF_INIT;
const char *head_arg;
- int flag, i;
+ int flag, i, ret = 0;
int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
struct commit_list *common = NULL;
const char *best_strategy = NULL, *wt_strategy = NULL;
struct commit_list **remotes = &remoteheads;
+ void *branch_to_free;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1109,7 +1121,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = resolve_ref("HEAD", head_sha1, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
if (branch && !prefixcmp(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
@@ -1123,6 +1135,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
parse_branch_merge_options(branch_mergeoptions);
argc = parse_options(argc, argv, prefix, builtin_merge_options,
builtin_merge_usage, 0);
+ if (shortlog_len < 0)
+ shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
if (verbosity < 0 && show_progress == -1)
show_progress = 0;
@@ -1135,7 +1149,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("There is no merge to abort (MERGE_HEAD missing)."));
/* Invoke 'git reset --merge' */
- return cmd_reset(nargc, nargv, prefix);
+ ret = cmd_reset(nargc, nargv, prefix);
+ goto done;
}
if (read_cache_unmerged())
@@ -1174,9 +1189,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("You cannot combine --no-ff with --ff-only."));
if (!abort_current_merge) {
- if (!argc && default_to_upstream)
- argc = setup_with_upstream(&argv);
- else if (argc == 1 && !strcmp(argv[0], "-"))
+ if (!argc) {
+ if (default_to_upstream)
+ argc = setup_with_upstream(&argv);
+ else
+ die(_("No commit specified and merge.defaultToUpstream not set."));
+ } else if (argc == 1 && !strcmp(argv[0], "-"))
argv[0] = "@{-1}";
}
if (!argc)
@@ -1219,7 +1237,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
read_empty(remote_head->object.sha1, 0);
update_ref("initial pull", "HEAD", remote_head->object.sha1,
NULL, 0, DIE_ON_ERR);
- return 0;
+ goto done;
} else {
struct strbuf merge_names = STRBUF_INIT;
@@ -1308,7 +1326,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* but first the most common case of merging one remote.
*/
finish_up_to_date("Already up-to-date.");
- return 0;
+ goto done;
} else if (allow_fast_forward && !remoteheads->next &&
!common->next &&
!hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
@@ -1329,16 +1347,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
strbuf_addstr(&msg,
" (no commit created; -m option ignored)");
commit = remoteheads->item;
- if (!commit)
- return 1;
+ if (!commit) {
+ ret = 1;
+ goto done;
+ }
if (checkout_fast_forward(head_commit->object.sha1,
- commit->object.sha1))
- return 1;
+ commit->object.sha1)) {
+ ret = 1;
+ goto done;
+ }
finish(head_commit, commit->object.sha1, msg.buf);
drop_save();
- return 0;
+ goto done;
} else if (!remoteheads->next && common->next)
;
/*
@@ -1356,8 +1378,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
git_committer_info(IDENT_ERROR_ON_NO_NAME);
printf(_("Trying really trivial in-index merge...\n"));
if (!read_tree_trivial(common->item->object.sha1,
- head_commit->object.sha1, remoteheads->item->object.sha1))
- return merge_trivial(head_commit);
+ head_commit->object.sha1,
+ remoteheads->item->object.sha1)) {
+ ret = merge_trivial(head_commit);
+ goto done;
+ }
printf(_("Nope.\n"));
}
} else {
@@ -1385,7 +1410,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (up_to_date) {
finish_up_to_date("Already up-to-date. Yeeah!");
- return 0;
+ goto done;
}
}
@@ -1467,9 +1492,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* If we have a resulting tree, that means the strategy module
* auto resolved the merge cleanly.
*/
- if (automerge_was_ok)
- return finish_automerge(head_commit, common, result_tree,
- wt_strategy);
+ if (automerge_was_ok) {
+ ret = finish_automerge(head_commit, common, result_tree,
+ wt_strategy);
+ goto done;
+ }
/*
* Pick the result from the best strategy and have the user fix
@@ -1483,7 +1510,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
fprintf(stderr, _("Merge with strategy %s failed.\n"),
use_strategies[0]->name);
- return 2;
+ ret = 2;
+ goto done;
} else if (best_strategy == wt_strategy)
; /* We already have its result in the working tree. */
else {
@@ -1499,10 +1527,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
write_merge_state();
- if (merge_was_ok) {
+ if (merge_was_ok)
fprintf(stderr, _("Automatic merge went well; "
"stopped before committing as requested\n"));
- return 0;
- } else
- return suggest_conflicts(option_renormalize);
+ else
+ ret = suggest_conflicts(option_renormalize);
+
+done:
+ free(branch_to_free);
+ return ret;
}
diff --git a/builtin/mktree.c b/builtin/mktree.c
index 098395fda..4ae1c412d 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -60,6 +60,7 @@ static void write_tree(unsigned char *sha1)
}
write_sha1_file(buf.buf, buf.len, tree_type, sha1);
+ strbuf_release(&buf);
}
static const char *mktree_usage[] = {
diff --git a/builtin/mv.c b/builtin/mv.c
index 5efe6c576..2a144b011 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -59,6 +59,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int i, newfd;
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
struct option builtin_mv_options[] = {
+ OPT__VERBOSE(&verbose, "be verbose"),
OPT__DRY_RUN(&show_only, "dry run"),
OPT__FORCE(&force, "force move/rename even if target exists"),
OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
@@ -93,7 +94,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
destination = copy_pathspec(dest_path[0], argv, argc, 1);
} else {
if (argc != 1)
- usage_with_options(builtin_mv_usage, builtin_mv_options);
+ die("destination '%s' is not a directory", dest_path[0]);
destination = dest_path;
}
@@ -176,7 +177,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
* check both source and destination
*/
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- warning(_("%s; will overwrite!"), bad);
+ if (verbose)
+ warning(_("overwriting '%s'"), dst);
bad = NULL;
} else
bad = _("Cannot overwrite");
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 7864056f1..1b374583c 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -291,7 +291,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
max = get_max_object_index();
for (i = 0; i < max; i++) {
struct object *obj = get_indexed_object(i);
- if (!obj)
+ if (!obj || obj->type != OBJ_COMMIT)
continue;
show_name(obj, NULL,
always, allow_undefined, data.name_only);
diff --git a/builtin/notes.c b/builtin/notes.c
index f8e437db0..3644d140e 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -301,12 +301,12 @@ void commit_notes(struct notes_tree *t, const char *msg)
return; /* don't have to commit an unchanged tree */
/* Prepare commit message and reflog message */
- strbuf_addstr(&buf, "notes: "); /* commit message starts at index 7 */
strbuf_addstr(&buf, msg);
if (buf.buf[buf.len - 1] != '\n')
strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
- create_notes_commit(t, NULL, buf.buf + 7, commit_sha1);
+ create_notes_commit(t, NULL, &buf, commit_sha1);
+ strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
strbuf_release(&buf);
@@ -804,6 +804,8 @@ static int merge_commit(struct notes_merge_options *o)
struct notes_tree *t;
struct commit *partial;
struct pretty_print_context pretty_ctx;
+ void *local_ref_to_free;
+ int ret;
/*
* Read partial merge result from .git/NOTES_MERGE_PARTIAL,
@@ -825,7 +827,8 @@ static int merge_commit(struct notes_merge_options *o)
t = xcalloc(1, sizeof(struct notes_tree));
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
- o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL);
+ o->local_ref = local_ref_to_free =
+ resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
if (!o->local_ref)
die("Failed to resolve NOTES_MERGE_REF");
@@ -843,7 +846,9 @@ static int merge_commit(struct notes_merge_options *o)
free_notes(t);
strbuf_release(&msg);
- return merge_abort(o);
+ ret = merge_abort(o);
+ free(local_ref_to_free);
+ return ret;
}
static int merge(int argc, const char **argv, const char *prefix)
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 2b18de5dc..96c168097 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -76,7 +76,7 @@ static struct pack_idx_option pack_idx_opts;
static const char *base_name;
static int progress = 1;
static int window = 10;
-static unsigned long pack_size_limit, pack_size_limit_cfg;
+static unsigned long pack_size_limit;
static int depth = 50;
static int delta_search_threads;
static int pack_to_stdout;
@@ -409,25 +409,56 @@ static unsigned long write_object(struct sha1file *f,
return hdrlen + datalen;
}
-static int write_one(struct sha1file *f,
- struct object_entry *e,
- off_t *offset)
+enum write_one_status {
+ WRITE_ONE_SKIP = -1, /* already written */
+ WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
+ WRITE_ONE_WRITTEN = 1, /* normal */
+ WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
+};
+
+static enum write_one_status write_one(struct sha1file *f,
+ struct object_entry *e,
+ off_t *offset)
{
unsigned long size;
+ int recursing;
- /* offset is non zero if object is written already. */
- if (e->idx.offset || e->preferred_base)
- return -1;
+ /*
+ * we set offset to 1 (which is an impossible value) to mark
+ * the fact that this object is involved in "write its base
+ * first before writing a deltified object" recursion.
+ */
+ recursing = (e->idx.offset == 1);
+ if (recursing) {
+ warning("recursive delta detected for object %s",
+ sha1_to_hex(e->idx.sha1));
+ return WRITE_ONE_RECURSIVE;
+ } else if (e->idx.offset || e->preferred_base) {
+ /* offset is non zero if object is written already. */
+ return WRITE_ONE_SKIP;
+ }
/* if we are deltified, write out base object first. */
- if (e->delta && !write_one(f, e->delta, offset))
- return 0;
+ if (e->delta) {
+ e->idx.offset = 1; /* now recurse */
+ switch (write_one(f, e->delta, offset)) {
+ case WRITE_ONE_RECURSIVE:
+ /* we cannot depend on this one */
+ e->delta = NULL;
+ break;
+ default:
+ break;
+ case WRITE_ONE_BREAK:
+ e->idx.offset = recursing;
+ return WRITE_ONE_BREAK;
+ }
+ }
e->idx.offset = *offset;
size = write_object(f, e, *offset);
if (!size) {
- e->idx.offset = 0;
- return 0;
+ e->idx.offset = recursing;
+ return WRITE_ONE_BREAK;
}
written_list[nr_written++] = &e->idx;
@@ -435,7 +466,7 @@ static int write_one(struct sha1file *f,
if (signed_add_overflows(*offset, size))
die("pack too large for current definition of off_t");
*offset += size;
- return 1;
+ return WRITE_ONE_WRITTEN;
}
static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
@@ -454,8 +485,8 @@ static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
return 0;
}
-static void add_to_write_order(struct object_entry **wo,
- int *endp,
+static inline void add_to_write_order(struct object_entry **wo,
+ unsigned int *endp,
struct object_entry *e)
{
if (e->filled)
@@ -465,32 +496,62 @@ static void add_to_write_order(struct object_entry **wo,
}
static void add_descendants_to_write_order(struct object_entry **wo,
- int *endp,
+ unsigned int *endp,
struct object_entry *e)
{
- struct object_entry *child;
-
- for (child = e->delta_child; child; child = child->delta_sibling)
- add_to_write_order(wo, endp, child);
- for (child = e->delta_child; child; child = child->delta_sibling)
- add_descendants_to_write_order(wo, endp, child);
+ int add_to_order = 1;
+ while (e) {
+ if (add_to_order) {
+ struct object_entry *s;
+ /* add this node... */
+ add_to_write_order(wo, endp, e);
+ /* all its siblings... */
+ for (s = e->delta_sibling; s; s = s->delta_sibling) {
+ add_to_write_order(wo, endp, s);
+ }
+ }
+ /* drop down a level to add left subtree nodes if possible */
+ if (e->delta_child) {
+ add_to_order = 1;
+ e = e->delta_child;
+ } else {
+ add_to_order = 0;
+ /* our sibling might have some children, it is next */
+ if (e->delta_sibling) {
+ e = e->delta_sibling;
+ continue;
+ }
+ /* go back to our parent node */
+ e = e->delta;
+ while (e && !e->delta_sibling) {
+ /* we're on the right side of a subtree, keep
+ * going up until we can go right again */
+ e = e->delta;
+ }
+ if (!e) {
+ /* done- we hit our original root node */
+ return;
+ }
+ /* pass it off to sibling at this level */
+ e = e->delta_sibling;
+ }
+ };
}
static void add_family_to_write_order(struct object_entry **wo,
- int *endp,
+ unsigned int *endp,
struct object_entry *e)
{
struct object_entry *root;
for (root = e; root->delta; root = root->delta)
; /* nothing */
- add_to_write_order(wo, endp, root);
add_descendants_to_write_order(wo, endp, root);
}
static struct object_entry **compute_write_order(void)
{
- int i, wo_end;
+ unsigned int i, wo_end, last_untagged;
struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));
@@ -506,8 +567,8 @@ static struct object_entry **compute_write_order(void)
* Make sure delta_sibling is sorted in the original
* recency order.
*/
- for (i = nr_objects - 1; 0 <= i; i--) {
- struct object_entry *e = &objects[i];
+ for (i = nr_objects; i > 0;) {
+ struct object_entry *e = &objects[--i];
if (!e->delta)
continue;
/* Mark me as the first child */
@@ -521,7 +582,7 @@ static struct object_entry **compute_write_order(void)
for_each_tag_ref(mark_tagged, NULL);
/*
- * Give the commits in the original recency order until
+ * Give the objects in the original recency order until
* we see a tagged tip.
*/
for (i = wo_end = 0; i < nr_objects; i++) {
@@ -529,6 +590,7 @@ static struct object_entry **compute_write_order(void)
break;
add_to_write_order(wo, &wo_end, &objects[i]);
}
+ last_untagged = i;
/*
* Then fill all the tagged tips.
@@ -541,7 +603,7 @@ static struct object_entry **compute_write_order(void)
/*
* And then all remaining commits and tags.
*/
- for (i = 0; i < nr_objects; i++) {
+ for (i = last_untagged; i < nr_objects; i++) {
if (objects[i].type != OBJ_COMMIT &&
objects[i].type != OBJ_TAG)
continue;
@@ -551,7 +613,7 @@ static struct object_entry **compute_write_order(void)
/*
* And then all the trees.
*/
- for (i = 0; i < nr_objects; i++) {
+ for (i = last_untagged; i < nr_objects; i++) {
if (objects[i].type != OBJ_TREE)
continue;
add_to_write_order(wo, &wo_end, &objects[i]);
@@ -560,8 +622,13 @@ static struct object_entry **compute_write_order(void)
/*
* Finally all the rest in really tight order
*/
- for (i = 0; i < nr_objects; i++)
- add_family_to_write_order(wo, &wo_end, &objects[i]);
+ for (i = last_untagged; i < nr_objects; i++) {
+ if (!objects[i].filled)
+ add_family_to_write_order(wo, &wo_end, &objects[i]);
+ }
+
+ if (wo_end != nr_objects)
+ die("ordered %u objects, expected %"PRIu32, wo_end, nr_objects);
return wo;
}
@@ -571,7 +638,6 @@ static void write_pack_file(void)
uint32_t i = 0, j;
struct sha1file *f;
off_t offset;
- struct pack_header hdr;
uint32_t nr_remaining = nr_result;
time_t last_mtime = 0;
struct object_entry **write_order;
@@ -585,26 +651,18 @@ static void write_pack_file(void)
unsigned char sha1[20];
char *pack_tmp_name = NULL;
- if (pack_to_stdout) {
+ if (pack_to_stdout)
f = sha1fd_throughput(1, "<stdout>", progress_state);
- } else {
- char tmpname[PATH_MAX];
- int fd;
- fd = odb_mkstemp(tmpname, sizeof(tmpname),
- "pack/tmp_pack_XXXXXX");
- pack_tmp_name = xstrdup(tmpname);
- f = sha1fd(fd, pack_tmp_name);
- }
-
- hdr.hdr_signature = htonl(PACK_SIGNATURE);
- hdr.hdr_version = htonl(PACK_VERSION);
- hdr.hdr_entries = htonl(nr_remaining);
- sha1write(f, &hdr, sizeof(hdr));
- offset = sizeof(hdr);
+ else
+ f = create_tmp_packfile(&pack_tmp_name);
+
+ offset = write_pack_header(f, nr_remaining);
+ if (!offset)
+ die_errno("unable to write pack header");
nr_written = 0;
for (; i < nr_objects; i++) {
struct object_entry *e = write_order[i];
- if (!write_one(f, e, &offset))
+ if (write_one(f, e, &offset) == WRITE_ONE_BREAK)
break;
display_progress(progress_state, written);
}
@@ -626,20 +684,8 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
struct stat st;
- const char *idx_tmp_name;
char tmpname[PATH_MAX];
- idx_tmp_name = write_idx_file(NULL, written_list, nr_written,
- &pack_idx_opts, sha1);
-
- snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
- base_name, sha1_to_hex(sha1));
- free_pack_by_name(tmpname);
- if (adjust_shared_perm(pack_tmp_name))
- die_errno("unable to make temporary pack file readable");
- if (rename(pack_tmp_name, tmpname))
- die_errno("unable to rename temporary pack file");
-
/*
* Packs are runtime accessed in their mtime
* order since newer packs are more likely to contain
@@ -647,28 +693,27 @@ static void write_pack_file(void)
* packs then we should modify the mtime of later ones
* to preserve this property.
*/
- if (stat(tmpname, &st) < 0) {
+ if (stat(pack_tmp_name, &st) < 0) {
warning("failed to stat %s: %s",
- tmpname, strerror(errno));
+ pack_tmp_name, strerror(errno));
} else if (!last_mtime) {
last_mtime = st.st_mtime;
} else {
struct utimbuf utb;
utb.actime = st.st_atime;
utb.modtime = --last_mtime;
- if (utime(tmpname, &utb) < 0)
+ if (utime(pack_tmp_name, &utb) < 0)
warning("failed utime() on %s: %s",
tmpname, strerror(errno));
}
- snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
- base_name, sha1_to_hex(sha1));
- if (adjust_shared_perm(idx_tmp_name))
- die_errno("unable to make temporary index file readable");
- if (rename(idx_tmp_name, tmpname))
- die_errno("unable to rename temporary index file");
-
- free((void *) idx_tmp_name);
+ /* Enough space for "-<sha-1>.pack"? */
+ if (sizeof(tmpname) <= strlen(base_name) + 50)
+ die("pack base name '%s' too long", base_name);
+ snprintf(tmpname, sizeof(tmpname), "%s-", base_name);
+ finish_tmp_packfile(tmpname, pack_tmp_name,
+ written_list, nr_written,
+ &pack_idx_opts, sha1);
free(pack_tmp_name);
puts(sha1_to_hex(sha1));
}
@@ -804,6 +849,10 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
off_t offset = find_pack_entry_one(sha1, p);
if (offset) {
if (!found_pack) {
+ if (!is_pack_valid(p)) {
+ warning("packfile %s cannot be accessed", p->pack_name);
+ continue;
+ }
found_offset = offset;
found_pack = p;
}
@@ -975,7 +1024,7 @@ static void add_pbase_object(struct tree_desc *tree,
while (tree_entry(tree,&entry)) {
if (S_ISGITLINK(entry.mode))
continue;
- cmp = tree_entry_len(entry.path, entry.sha1) != cmplen ? 1 :
+ cmp = tree_entry_len(&entry) != cmplen ? 1 :
memcmp(name, entry.path, cmplen);
if (cmp > 0)
continue;
@@ -2027,10 +2076,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)
pack_idx_opts.version);
return 0;
}
- if (!strcmp(k, "pack.packsizelimit")) {
- pack_size_limit_cfg = git_config_ulong(k, v);
- return 0;
- }
return git_default_config(k, v, cb);
}
diff --git a/builtin/prune.c b/builtin/prune.c
index e65690ba3..58d7cb832 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -5,6 +5,7 @@
#include "builtin.h"
#include "reachable.h"
#include "parse-options.h"
+#include "progress.h"
#include "dir.h"
static const char * const prune_usage[] = {
@@ -14,6 +15,7 @@ static const char * const prune_usage[] = {
static int show_only;
static int verbose;
static unsigned long expire;
+static int show_progress = -1;
static int prune_tmp_object(const char *path, const char *filename)
{
@@ -124,9 +126,11 @@ static void remove_temporary_files(const char *path)
int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
+ struct progress *progress = NULL;
const struct option options[] = {
OPT__DRY_RUN(&show_only, "do not remove, show only"),
OPT__VERBOSE(&verbose, "report pruned objects"),
+ OPT_BOOL(0, "progress", &show_progress, "show progress"),
OPT_DATE(0, "expire", &expire,
"expire objects older than <time>"),
OPT_END()
@@ -152,7 +156,14 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
else
die("unrecognized argument: %s", name);
}
- mark_reachable_objects(&revs, 1);
+
+ if (show_progress == -1)
+ show_progress = isatty(2);
+ if (show_progress)
+ progress = start_progress_delay("Checking connectivity", 0, 0, 2);
+
+ mark_reachable_objects(&revs, 1, progress);
+ stop_progress(&progress);
prune_object_dir(get_object_directory());
prune_packed_objects(show_only);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 261b610d2..d2dcb7e4a 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -37,6 +37,7 @@ static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
static const char *head_name;
+static void *head_name_to_free;
static int sent_capabilities;
static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -571,7 +572,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+ dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
@@ -634,7 +635,7 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
struct command **cmd_list = cb_data;
struct command *cmd = *cmd_list;
- if (!cmd)
+ if (!cmd || is_null_sha1(cmd->new_sha1))
return -1; /* end of list */
*cmd_list = NULL; /* this returns only one */
hashcpy(sha1, cmd->new_sha1);
@@ -659,11 +660,16 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
struct command **cmd_list = cb_data;
struct command *cmd = *cmd_list;
- if (!cmd)
- return -1; /* end of list */
- *cmd_list = cmd->next;
- hashcpy(sha1, cmd->new_sha1);
- return 0;
+ while (cmd) {
+ if (!is_null_sha1(cmd->new_sha1)) {
+ hashcpy(sha1, cmd->new_sha1);
+ *cmd_list = cmd->next;
+ return 0;
+ }
+ cmd = cmd->next;
+ }
+ *cmd_list = NULL;
+ return -1; /* end of list */
}
static void execute_commands(struct command *commands, const char *unpacker_error)
@@ -690,7 +696,8 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
check_aliased_updates(commands);
- head_name = resolve_ref("HEAD", sha1, 0, NULL);
+ free(head_name_to_free);
+ head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
for (cmd = commands; cmd; cmd = cmd->next)
if (!cmd->skip_update)
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 3a9c80f3d..062d7dad1 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -647,7 +647,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
init_revisions(&cb.revs, prefix);
if (cb.verbose)
printf("Marking reachable objects...");
- mark_reachable_objects(&cb.revs, 0);
+ mark_reachable_objects(&cb.revs, 0, NULL);
if (cb.verbose)
putchar('\n');
}
diff --git a/builtin/remote.c b/builtin/remote.c
index b25dfb442..583eec90e 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -343,13 +343,13 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
states->tracked.strdup_strings = 1;
states->stale.strdup_strings = 1;
for (ref = fetch_map; ref; ref = ref->next) {
- unsigned char sha1[20];
- if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
+ if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
string_list_append(&states->new, abbrev_branch(ref->name));
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
}
- stale_refs = get_stale_heads(states->remote, fetch_map);
+ stale_refs = get_stale_heads(states->remote->fetch,
+ states->remote->fetch_refspec_nr, fetch_map);
for (ref = stale_refs; ref; ref = ref->next) {
struct string_list_item *item =
string_list_append(&states->stale, abbrev_branch(ref->name));
@@ -389,8 +389,8 @@ static int get_push_ref_states(const struct ref *remote_refs,
local_refs = get_local_heads();
push_map = copy_ref_list(remote_refs);
- match_refs(local_refs, &push_map, remote->push_refspec_nr,
- remote->push_refspec, MATCH_REFS_NONE);
+ match_push_refs(local_refs, &push_map, remote->push_refspec_nr,
+ remote->push_refspec, MATCH_REFS_NONE);
states->push.strdup_strings = 1;
for (ref = push_map; ref; ref = ref->next) {
@@ -573,7 +573,7 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (!prefixcmp(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
- symref = resolve_ref(refname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
@@ -709,7 +709,7 @@ static int mv(int argc, const char **argv)
int flag = 0;
unsigned char sha1[20];
- resolve_ref(item->string, sha1, 1, &flag);
+ read_ref_full(item->string, sha1, 1, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
@@ -1219,10 +1219,9 @@ static int set_head(int argc, const char **argv)
usage_with_options(builtin_remote_sethead_usage, options);
if (head_name) {
- unsigned char sha1[20];
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
/* make sure it's valid */
- if (!resolve_ref(buf2.buf, sha1, 1, NULL))
+ if (!ref_exists(buf2.buf))
result |= error("Not a valid ref: %s", buf2.buf);
else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
result |= error("Could not setup %s", buf.buf);
@@ -1398,7 +1397,7 @@ static int set_branches(int argc, const char **argv)
builtin_remote_setbranches_usage, 0);
if (argc == 0) {
error("no remote specified");
- usage_with_options(builtin_remote_seturl_usage, options);
+ usage_with_options(builtin_remote_setbranches_usage, options);
}
argv[argc] = NULL;
@@ -1426,7 +1425,7 @@ static int set_url(int argc, const char **argv)
"delete URLs"),
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
+ argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage,
PARSE_OPT_KEEP_ARGV0);
if (add_mode && delete_mode)
diff --git a/builtin/replace.c b/builtin/replace.c
index 517fa1031..4a8970e9c 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -58,7 +58,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
had_error = 1;
continue;
}
- if (!resolve_ref(ref, sha1, 1, NULL)) {
+ if (read_ref(ref, sha1)) {
error("replace ref '%s' not found.", *p);
had_error = 1;
continue;
@@ -97,7 +97,7 @@ static int replace_object(const char *object_ref, const char *replace_ref,
if (check_refname_format(ref, 0))
die("'%s' is not a valid ref name.", ref);
- if (!resolve_ref(ref, prev, 1, NULL))
+ if (read_ref(ref, prev))
hashclr(prev);
else if (!force)
die("replace ref '%s' already exists", ref);
diff --git a/builtin/reset.c b/builtin/reset.c
index 811e8e252..8c2c1d52a 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -43,6 +43,7 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
int nr = 1;
int newfd;
struct tree_desc desc[2];
+ struct tree *tree;
struct unpack_trees_options opts;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
@@ -84,6 +85,12 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
return error(_("Failed to find tree of %s."), sha1_to_hex(sha1));
if (unpack_trees(nr, desc, &opts))
return -1;
+
+ if (reset_type == MIXED || reset_type == HARD) {
+ tree = parse_tree_indirect(sha1);
+ prime_cache_tree(&active_cache_tree, tree);
+ }
+
if (write_cache(newfd, active_cache, active_nr) ||
commit_locked_index(lock))
return error(_("Could not write new index file."));
diff --git a/builtin/revert.c b/builtin/revert.c
index 87df70edc..0d8020cf6 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -40,7 +40,12 @@ static const char * const cherry_pick_usage[] = {
};
enum replay_action { REVERT, CHERRY_PICK };
-enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
+enum replay_subcommand {
+ REPLAY_NONE,
+ REPLAY_REMOVE_STATE,
+ REPLAY_CONTINUE,
+ REPLAY_ROLLBACK
+};
struct replay_opts {
enum replay_action action;
@@ -55,13 +60,14 @@ struct replay_opts {
int allow_rerere_auto;
int mainline;
- int commit_argc;
- const char **commit_argv;
/* Merge strategy */
const char *strategy;
const char **xopts;
size_t xopts_nr, xopts_alloc;
+
+ /* Only used by REPLAY_NONE */
+ struct rev_info *revs;
};
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -133,11 +139,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
{
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
- int reset = 0;
+ int remove_state = 0;
int contin = 0;
+ int rollback = 0;
struct option options[] = {
- OPT_BOOLEAN(0, "reset", &reset, "forget the current operation"),
- OPT_BOOLEAN(0, "continue", &contin, "continue the current operation"),
+ OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
OPT_NOOP_NOARG('r', NULL),
@@ -162,31 +170,38 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
die(_("program error"));
}
- opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
- PARSE_OPT_KEEP_ARGV0 |
- PARSE_OPT_KEEP_UNKNOWN);
+ argc = parse_options(argc, argv, NULL, options, usage_str,
+ PARSE_OPT_KEEP_ARGV0 |
+ PARSE_OPT_KEEP_UNKNOWN);
/* Check for incompatible subcommands */
verify_opt_mutually_compatible(me,
- "--reset", reset,
+ "--quit", remove_state,
"--continue", contin,
+ "--abort", rollback,
NULL);
/* Set the subcommand */
- if (reset)
- opts->subcommand = REPLAY_RESET;
+ if (remove_state)
+ opts->subcommand = REPLAY_REMOVE_STATE;
else if (contin)
opts->subcommand = REPLAY_CONTINUE;
+ else if (rollback)
+ opts->subcommand = REPLAY_ROLLBACK;
else
opts->subcommand = REPLAY_NONE;
/* Check for incompatible command line arguments */
if (opts->subcommand != REPLAY_NONE) {
char *this_operation;
- if (opts->subcommand == REPLAY_RESET)
- this_operation = "--reset";
- else
+ if (opts->subcommand == REPLAY_REMOVE_STATE)
+ this_operation = "--quit";
+ else if (opts->subcommand == REPLAY_CONTINUE)
this_operation = "--continue";
+ else {
+ assert(opts->subcommand == REPLAY_ROLLBACK);
+ this_operation = "--abort";
+ }
verify_opt_compatible(me, this_operation,
"--no-commit", opts->no_commit,
@@ -199,9 +214,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
NULL);
}
- else if (opts->commit_argc < 2)
- usage_with_options(usage_str, options);
-
if (opts->allow_ff)
verify_opt_compatible(me, "--ff",
"--signoff", opts->signoff,
@@ -209,7 +221,20 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
"-x", opts->record_origin,
"--edit", opts->edit,
NULL);
- opts->commit_argv = argv;
+
+ if (opts->subcommand != REPLAY_NONE) {
+ opts->revs = NULL;
+ } else {
+ opts->revs = xmalloc(sizeof(*opts->revs));
+ init_revisions(opts->revs, NULL);
+ opts->revs->no_walk = 1;
+ if (argc < 2)
+ usage_with_options(usage_str, options);
+ argc = setup_revisions(argc, argv, opts->revs, NULL);
+ }
+
+ if (argc > 1)
+ usage_with_options(usage_str, options);
}
struct commit_message {
@@ -286,19 +311,20 @@ static char *get_encoding(const char *message)
return NULL;
}
-static void write_cherry_pick_head(struct commit *commit)
+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
{
+ const char *filename;
int fd;
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
- fd = open(git_path("CHERRY_PICK_HEAD"), O_WRONLY | O_CREAT, 0666);
+ filename = git_path("%s", pseudoref);
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("CHERRY_PICK_HEAD"));
+ die_errno(_("Could not open '%s' for writing"), filename);
if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
- die_errno(_("Could not write to '%s'"), git_path("CHERRY_PICK_HEAD"));
+ die_errno(_("Could not write to '%s'"), filename);
strbuf_release(&buf);
}
@@ -331,7 +357,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
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);
+ die_errno(_("Could not write to %s"), filename);
strbuf_release(msgbuf);
if (commit_lock_file(&msg_file) < 0)
die(_("Error wrapping up %s"), filename);
@@ -593,7 +619,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
* write it at all.
*/
if (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))
- write_cherry_pick_head(commit);
+ write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+ if (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))
+ write_cherry_pick_head(commit, "REVERT_HEAD");
if (res) {
error(opts->action == REVERT
@@ -614,23 +642,15 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
return res;
}
-static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)
+static void prepare_revs(struct replay_opts *opts)
{
- int argc;
-
- init_revisions(revs, NULL);
- revs->no_walk = 1;
if (opts->action != REVERT)
- revs->reverse = 1;
+ opts->revs->reverse ^= 1;
- argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
- if (argc > 1)
- usage(*revert_or_cherry_pick_usage(opts));
-
- if (prepare_revision_walk(revs))
+ if (prepare_revision_walk(opts->revs))
die(_("revision walk setup failed"));
- if (!revs->commits)
+ if (!opts->revs->commits)
die(_("empty commit set passed"));
}
@@ -680,44 +700,47 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
struct replay_opts *opts)
{
struct commit_list *cur = NULL;
- struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
const char *sha1_abbrev = NULL;
const char *action_str = opts->action == REVERT ? "revert" : "pick";
+ const char *subject;
+ int subject_len;
for (cur = todo_list; cur; cur = cur->next) {
sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
- if (get_message(cur->item, &msg))
- return error(_("Cannot get commit message for %s"), sha1_abbrev);
- strbuf_addf(buf, "%s %s %s\n", action_str, sha1_abbrev, msg.subject);
+ subject_len = find_commit_subject(cur->item->buffer, &subject);
+ strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
+ subject_len, subject);
}
return 0;
}
-static struct commit *parse_insn_line(char *start, struct replay_opts *opts)
+static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)
{
unsigned char commit_sha1[20];
- char sha1_abbrev[40];
enum replay_action action;
- int insn_len = 0;
- char *p, *q;
+ char *end_of_object_name;
+ int saved, status, padding;
- if (!prefixcmp(start, "pick ")) {
+ if (!prefixcmp(bol, "pick")) {
action = CHERRY_PICK;
- insn_len = strlen("pick");
- p = start + insn_len + 1;
- } else if (!prefixcmp(start, "revert ")) {
+ bol += strlen("pick");
+ } else if (!prefixcmp(bol, "revert")) {
action = REVERT;
- insn_len = strlen("revert");
- p = start + insn_len + 1;
+ bol += strlen("revert");
} else
return NULL;
- q = strchr(p, ' ');
- if (!q)
+ /* Eat up extra spaces/ tabs before object name */
+ padding = strspn(bol, " \t");
+ if (!padding)
return NULL;
- q++;
+ bol += padding;
- strlcpy(sha1_abbrev, p, q - p);
+ end_of_object_name = bol + strcspn(bol, " \t\n");
+ saved = *end_of_object_name;
+ *end_of_object_name = '\0';
+ status = get_sha1(bol, commit_sha1);
+ *end_of_object_name = saved;
/*
* Verify that the action matches up with the one in
@@ -730,7 +753,7 @@ static struct commit *parse_insn_line(char *start, struct replay_opts *opts)
return NULL;
}
- if (get_sha1(sha1_abbrev, commit_sha1) < 0)
+ if (status < 0)
return NULL;
return lookup_commit_reference(commit_sha1);
@@ -745,13 +768,12 @@ static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
int i;
for (i = 1; *p; i++) {
- commit = parse_insn_line(p, opts);
+ char *eol = strchrnul(p, '\n');
+ commit = parse_insn_line(p, eol, opts);
if (!commit)
return error(_("Could not parse line %d."), i);
next = commit_list_append(commit, next);
- p = strchrnul(p, '\n');
- if (*p)
- p++;
+ p = *eol ? eol + 1 : eol;
}
if (!*todo_list)
return error(_("No commits parsed."));
@@ -767,7 +789,7 @@ static void read_populate_todo(struct commit_list **todo_list,
fd = open(todo_file, O_RDONLY);
if (fd < 0)
- die_errno(_("Could not open %s."), todo_file);
+ die_errno(_("Could not open %s"), todo_file);
if (strbuf_read(&buf, fd, 0) < 0) {
close(fd);
strbuf_release(&buf);
@@ -827,14 +849,13 @@ static void read_populate_opts(struct replay_opts **opts_ptr)
static void walk_revs_populate_todo(struct commit_list **todo_list,
struct replay_opts *opts)
{
- struct rev_info revs;
struct commit *commit;
struct commit_list **next;
- prepare_revs(&revs, opts);
+ prepare_revs(opts);
next = todo_list;
- while ((commit = get_revision(&revs)))
+ while ((commit = get_revision(opts->revs)))
next = commit_list_append(commit, next);
}
@@ -842,10 +863,13 @@ static int create_seq_dir(void)
{
const char *seq_dir = git_path(SEQ_DIR);
- if (file_exists(seq_dir))
- return error(_("%s already exists."), seq_dir);
+ if (file_exists(seq_dir)) {
+ error(_("a cherry-pick or revert is already in progress"));
+ advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
+ return -1;
+ }
else if (mkdir(seq_dir, 0777) < 0)
- die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
+ die_errno(_("Could not create sequencer directory %s"), seq_dir);
return 0;
}
@@ -859,11 +883,77 @@ static void save_head(const char *head)
fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
strbuf_addf(&buf, "%s\n", head);
if (write_in_full(fd, buf.buf, buf.len) < 0)
- die_errno(_("Could not write to %s."), head_file);
+ die_errno(_("Could not write to %s"), head_file);
if (commit_lock_file(&head_lock) < 0)
die(_("Error wrapping up %s."), head_file);
}
+static int reset_for_rollback(const unsigned char *sha1)
+{
+ const char *argv[4]; /* reset --merge <arg> + NULL */
+ argv[0] = "reset";
+ argv[1] = "--merge";
+ argv[2] = sha1_to_hex(sha1);
+ argv[3] = NULL;
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int rollback_single_pick(void)
+{
+ unsigned char head_sha1[20];
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ if (read_ref_full("HEAD", head_sha1, 0, NULL))
+ return error(_("cannot resolve HEAD"));
+ if (is_null_sha1(head_sha1))
+ return error(_("cannot abort from a branch yet to be born"));
+ return reset_for_rollback(head_sha1);
+}
+
+static int sequencer_rollback(struct replay_opts *opts)
+{
+ const char *filename;
+ FILE *f;
+ unsigned char sha1[20];
+ struct strbuf buf = STRBUF_INIT;
+
+ filename = git_path(SEQ_HEAD_FILE);
+ f = fopen(filename, "r");
+ if (!f && errno == ENOENT) {
+ /*
+ * There is no multiple-cherry-pick in progress.
+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
+ * a single-cherry-pick in progress, abort that.
+ */
+ return rollback_single_pick();
+ }
+ if (!f)
+ return error(_("cannot open %s: %s"), filename,
+ strerror(errno));
+ if (strbuf_getline(&buf, f, '\n')) {
+ error(_("cannot read %s: %s"), filename, ferror(f) ?
+ strerror(errno) : _("unexpected end of file"));
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
+ error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
+ filename);
+ goto fail;
+ }
+ if (reset_for_rollback(sha1))
+ goto fail;
+ remove_sequencer_state();
+ strbuf_release(&buf);
+ return 0;
+fail:
+ strbuf_release(&buf);
+ return -1;
+}
+
static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
{
const char *todo_file = git_path(SEQ_TODO_FILE);
@@ -876,7 +966,7 @@ static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
die(_("Could not format %s."), todo_file);
if (write_in_full(fd, buf.buf, buf.len) < 0) {
strbuf_release(&buf);
- die_errno(_("Could not write to %s."), todo_file);
+ die_errno(_("Could not write to %s"), todo_file);
}
if (commit_lock_file(&todo_lock) < 0) {
strbuf_release(&buf);
@@ -930,33 +1020,64 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
for (cur = todo_list; cur; cur = cur->next) {
save_todo(cur, opts);
res = do_pick_commit(cur->item, opts);
- if (res) {
- if (!cur->next)
- /*
- * An error was encountered while
- * picking the last commit; the
- * sequencer state is useless now --
- * the user simply needs to resolve
- * the conflict and commit
- */
- remove_sequencer_state(0);
+ if (res)
return res;
- }
}
/*
* Sequence of picks finished successfully; cleanup by
* removing the .git/sequencer directory
*/
- remove_sequencer_state(1);
+ remove_sequencer_state();
return 0;
}
+static int continue_single_pick(void)
+{
+ const char *argv[] = { "commit", NULL };
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int sequencer_continue(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+
+ if (!file_exists(git_path(SEQ_TODO_FILE)))
+ return continue_single_pick();
+ read_populate_opts(&opts);
+ read_populate_todo(&todo_list, opts);
+
+ /* Verify that the conflict has been resolved */
+ if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
+ file_exists(git_path("REVERT_HEAD"))) {
+ int ret = continue_single_pick();
+ if (ret)
+ return ret;
+ }
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ todo_list = todo_list->next;
+ return pick_commits(todo_list, opts);
+}
+
+static int single_pick(struct commit *cmit, struct replay_opts *opts)
+{
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ return do_pick_commit(cmit, opts);
+}
+
static int pick_revisions(struct replay_opts *opts)
{
struct commit_list *todo_list = NULL;
unsigned char sha1[20];
+ if (opts->subcommand == REPLAY_NONE)
+ assert(opts->revs);
+
read_and_refresh_cache(opts);
/*
@@ -964,43 +1085,52 @@ static int pick_revisions(struct replay_opts *opts)
* cherry-pick should be handled differently from an existing
* one that is being continued
*/
- if (opts->subcommand == REPLAY_RESET) {
- remove_sequencer_state(1);
+ if (opts->subcommand == REPLAY_REMOVE_STATE) {
+ remove_sequencer_state();
return 0;
- } else if (opts->subcommand == REPLAY_CONTINUE) {
- if (!file_exists(git_path(SEQ_TODO_FILE)))
- goto error;
- read_populate_opts(&opts);
- read_populate_todo(&todo_list, opts);
-
- /* Verify that the conflict has been resolved */
- if (!index_differs_from("HEAD", 0))
- todo_list = todo_list->next;
- } else {
- /*
- * Start a new cherry-pick/ revert sequence; but
- * first, make sure that an existing one isn't in
- * progress
- */
+ }
+ if (opts->subcommand == REPLAY_ROLLBACK)
+ return sequencer_rollback(opts);
+ if (opts->subcommand == REPLAY_CONTINUE)
+ return sequencer_continue(opts);
- walk_revs_populate_todo(&todo_list, opts);
- if (create_seq_dir() < 0) {
- error(_("A cherry-pick or revert is in progress."));
- advise(_("Use --continue to continue the operation"));
- advise(_("or --reset to forget about it"));
- return -1;
- }
- if (get_sha1("HEAD", sha1)) {
- if (opts->action == REVERT)
- return error(_("Can't revert as initial commit"));
- return error(_("Can't cherry-pick into empty head"));
- }
- save_head(sha1_to_hex(sha1));
- save_opts(opts);
+ /*
+ * If we were called as "git cherry-pick <commit>", just
+ * cherry-pick/revert it, set CHERRY_PICK_HEAD /
+ * REVERT_HEAD, and don't touch the sequencer state.
+ * This means it is possible to cherry-pick in the middle
+ * of a cherry-pick sequence.
+ */
+ if (opts->revs->cmdline.nr == 1 &&
+ opts->revs->cmdline.rev->whence == REV_CMD_REV &&
+ opts->revs->no_walk &&
+ !opts->revs->cmdline.rev->flags) {
+ struct commit *cmit;
+ if (prepare_revision_walk(opts->revs))
+ die(_("revision walk setup failed"));
+ cmit = get_revision(opts->revs);
+ if (!cmit || get_revision(opts->revs))
+ die("BUG: expected exactly one commit from walk");
+ return single_pick(cmit, opts);
+ }
+
+ /*
+ * Start a new cherry-pick/ revert sequence; but
+ * first, make sure that an existing one isn't in
+ * progress
+ */
+
+ walk_revs_populate_todo(&todo_list, opts);
+ if (create_seq_dir() < 0)
+ return -1;
+ if (get_sha1("HEAD", sha1)) {
+ if (opts->action == REVERT)
+ return error(_("Can't revert as initial commit"));
+ return error(_("Can't cherry-pick into empty head"));
}
+ save_head(sha1_to_hex(sha1));
+ save_opts(opts);
return pick_commits(todo_list, opts);
-error:
- return error(_("No %s in progress"), action_name(opts));
}
int cmd_revert(int argc, const char **argv, const char *prefix)
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index c1f6ddd92..cd1115ffc 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -334,7 +334,7 @@ int send_pack(struct send_pack_args *args,
demux.data = fd;
demux.out = -1;
if (start_async(&demux))
- die("receive-pack: unable to fork off sideband demultiplexer");
+ die("send-pack: unable to fork off sideband demultiplexer");
in = demux.out;
}
@@ -494,8 +494,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
memset(&extra_have, 0, sizeof(extra_have));
- get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
- &extra_have);
+ get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have);
transport_verify_remote_names(nr_refspecs, refspecs);
@@ -509,7 +508,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
flags |= MATCH_REFS_MIRROR;
/* match them up */
- if (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
+ if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
return -1;
set_ref_status_for_push(remote_refs, args.send_mirror,
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 4b480d7c7..a59e088cf 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -726,10 +726,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- const char *refname;
- refname = resolve_ref("HEAD", sha1, 1, NULL);
- fake_av[0] = xstrdup(refname);
+ fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -791,7 +789,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
}
- head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+ head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index fafb6dd50..391166190 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -225,7 +225,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
unsigned char sha1[20];
if (!prefixcmp(*pattern, "refs/") &&
- resolve_ref(*pattern, sha1, 1, NULL)) {
+ !read_ref(*pattern, sha1)) {
if (!quiet)
show_one(*pattern, sha1);
}
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 4d3b93fed..f16986c0a 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -22,8 +22,6 @@ static size_t cleanup(char *line, size_t len)
* Remove empty lines from the beginning and end
* and also trailing spaces from every line.
*
- * Note that the buffer will not be NUL-terminated.
- *
* Turn multiple consecutive empty lines between paragraphs
* into just one empty line.
*
@@ -77,7 +75,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
!strcmp(argv[1], "--strip-comments")))
strip_comments = 1;
else if (argc > 1)
- usage("git stripspace [-s | --strip-comments] < <stream>");
+ usage("git stripspace [-s | --strip-comments] < input");
if (strbuf_read(&buf, 0, 1024) < 0)
die_errno("could not read the input");
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index dea849c3c..2ef596238 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -12,7 +12,7 @@ static void check_symref(const char *HEAD, int quiet)
{
unsigned char sha1[20];
int flag;
- const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+ const char *refs_heads_master = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
if (!refs_heads_master)
die("No such ref: %s", HEAD);
diff --git a/builtin/tag.c b/builtin/tag.c
index cca120552..31f02e80f 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -173,7 +173,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
had_error = 1;
continue;
}
- if (!resolve_ref(ref, sha1, 1, NULL)) {
+ if (read_ref(ref, sha1)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
continue;
@@ -214,6 +214,15 @@ static const char tag_template[] =
N_("\n"
"#\n"
"# Write a tag message\n"
+ "# Lines starting with '#' will be ignored.\n"
+ "#\n");
+
+static const char tag_template_nocleanup[] =
+ N_("\n"
+ "#\n"
+ "# Write a tag message\n"
+ "# Lines starting with '#' will be kept; you may remove them"
+ " yourself if you want to.\n"
"#\n");
static int git_tag_config(const char *var, const char *value, void *cb)
@@ -255,8 +264,18 @@ static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
return 0;
}
+struct create_tag_options {
+ unsigned int message_given:1;
+ unsigned int sign;
+ enum {
+ CLEANUP_NONE,
+ CLEANUP_SPACE,
+ CLEANUP_ALL
+ } cleanup_mode;
+};
+
static void create_tag(const unsigned char *object, const char *tag,
- struct strbuf *buf, int message, int sign,
+ struct strbuf *buf, struct create_tag_options *opt,
unsigned char *prev, unsigned char *result)
{
enum object_type type;
@@ -281,7 +300,7 @@ static void create_tag(const unsigned char *object, const char *tag,
if (header_len > sizeof(header_buf) - 1)
die(_("tag header too big."));
- if (!message) {
+ if (!opt->message_given) {
int fd;
/* write the template message before editing: */
@@ -292,8 +311,12 @@ static void create_tag(const unsigned char *object, const char *tag,
if (!is_null_sha1(prev))
write_tag_body(fd, prev);
+ else if (opt->cleanup_mode == CLEANUP_ALL)
+ write_or_die(fd, _(tag_template),
+ strlen(_(tag_template)));
else
- write_or_die(fd, _(tag_template), strlen(_(tag_template)));
+ write_or_die(fd, _(tag_template_nocleanup),
+ strlen(_(tag_template_nocleanup)));
close(fd);
if (launch_editor(path, buf, NULL)) {
@@ -303,14 +326,15 @@ static void create_tag(const unsigned char *object, const char *tag,
}
}
- stripspace(buf, 1);
+ if (opt->cleanup_mode != CLEANUP_NONE)
+ stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
- if (!message && !buf->len)
+ if (!opt->message_given && !buf->len)
die(_("no tag message?"));
strbuf_insert(buf, 0, header_buf, header_len);
- if (build_tag_object(buf, sign, result) < 0) {
+ if (build_tag_object(buf, opt->sign, result) < 0) {
if (path)
fprintf(stderr, _("The tag message has been left in %s\n"),
path);
@@ -358,9 +382,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
unsigned char object[20], prev[20];
const char *object_ref, *tag;
struct ref_lock *lock;
-
- int annotate = 0, sign = 0, force = 0, lines = -1,
- list = 0, delete = 0, verify = 0;
+ struct create_tag_options opt;
+ char *cleanup_arg = NULL;
+ int annotate = 0, force = 0, lines = -1, list = 0,
+ delete = 0, verify = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
@@ -378,7 +403,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_CALLBACK('m', "message", &msg, "message",
"tag message", parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, "read message from file"),
- OPT_BOOLEAN('s', "sign", &sign, "annotated and GPG-signed tag"),
+ OPT_BOOLEAN('s', "sign", &opt.sign, "annotated and GPG-signed tag"),
+ OPT_STRING(0, "cleanup", &cleanup_arg, "mode",
+ "how to strip spaces and #comments from message"),
OPT_STRING('u', "local-user", &keyid, "key-id",
"use another key to sign the tag"),
OPT__FORCE(&force, "replace the tag if exists"),
@@ -395,13 +422,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
git_config(git_tag_config, NULL);
+ memset(&opt, 0, sizeof(opt));
+
argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
if (keyid) {
- sign = 1;
+ opt.sign = 1;
set_signing_key(keyid);
}
- if (sign)
+ if (opt.sign)
annotate = 1;
if (argc == 0 && !(delete || verify))
list = 1;
@@ -454,14 +483,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (!resolve_ref(ref.buf, prev, 1, NULL))
+ if (read_ref(ref.buf, prev))
hashclr(prev);
else if (!force)
die(_("tag '%s' already exists"), tag);
+ opt.message_given = msg.given || msgfile;
+
+ if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
+ opt.cleanup_mode = CLEANUP_ALL;
+ else if (!strcmp(cleanup_arg, "verbatim"))
+ opt.cleanup_mode = CLEANUP_NONE;
+ else if (!strcmp(cleanup_arg, "whitespace"))
+ opt.cleanup_mode = CLEANUP_SPACE;
+ else
+ die(_("Invalid cleanup mode %s"), cleanup_arg);
+
if (annotate)
- create_tag(object, tag, &buf, msg.given || msgfile,
- sign, prev, object);
+ create_tag(object, tag, &buf, &opt, prev, object);
lock = lock_any_ref_for_update(ref.buf, prev, 0);
if (!lock)
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 2d0b38333..b928beb8e 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -6,6 +6,7 @@
#include "archive.h"
#include "pkt-line.h"
#include "sideband.h"
+#include "run-command.h"
static const char upload_archive_usage[] =
"git upload-archive <repo>";
@@ -13,12 +14,9 @@ static const char upload_archive_usage[] =
static const char deadchild[] =
"git upload-archive: archiver died with error";
-static const char lostchild[] =
-"git upload-archive: archiver process was lost";
-
#define MAX_ARGS (64)
-static int run_upload_archive(int argc, const char **argv, const char *prefix)
+int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
{
const char *sent_argv[MAX_ARGS];
const char *arg_cmd = "argument ";
@@ -96,8 +94,8 @@ static ssize_t process_input(int child_fd, int band)
int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
- pid_t writer;
- int fd1[2], fd2[2];
+ struct child_process writer = { argv };
+
/*
* Set up sideband subprocess.
*
@@ -105,39 +103,24 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
* multiplexed out to our fd#1. If the child dies, we tell the other
* end over channel #3.
*/
- if (pipe(fd1) < 0 || pipe(fd2) < 0) {
- int err = errno;
- packet_write(1, "NACK pipe failed on the remote side\n");
- die("upload-archive: %s", strerror(err));
- }
- writer = fork();
- if (writer < 0) {
+ argv[0] = "upload-archive--writer";
+ writer.out = writer.err = -1;
+ writer.git_cmd = 1;
+ if (start_command(&writer)) {
int err = errno;
- packet_write(1, "NACK fork failed on the remote side\n");
+ packet_write(1, "NACK unable to spawn subprocess\n");
die("upload-archive: %s", strerror(err));
}
- if (!writer) {
- /* child - connect fd#1 and fd#2 to the pipe */
- dup2(fd1[1], 1);
- dup2(fd2[1], 2);
- close(fd1[1]); close(fd2[1]);
- close(fd1[0]); close(fd2[0]); /* we do not read from pipe */
-
- exit(run_upload_archive(argc, argv, prefix));
- }
- /* parent - read from child, multiplex and send out to fd#1 */
- close(fd1[1]); close(fd2[1]); /* we do not write to pipe */
packet_write(1, "ACK\n");
packet_flush(1);
while (1) {
struct pollfd pfd[2];
- int status;
- pfd[0].fd = fd1[0];
+ pfd[0].fd = writer.out;
pfd[0].events = POLLIN;
- pfd[1].fd = fd2[0];
+ pfd[1].fd = writer.err;
pfd[1].events = POLLIN;
if (poll(pfd, 2, -1) < 0) {
if (errno != EINTR) {
@@ -156,9 +139,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
if (process_input(pfd[0].fd, 1))
continue;
- if (waitpid(writer, &status, 0) < 0)
- error_clnt("%s", lostchild);
- else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
+ if (finish_command(&writer))
error_clnt("%s", deadchild);
packet_flush(1);
break;