summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/apply.c15
-rw-r--r--builtin/blame.c12
-rw-r--r--builtin/cat-file.c6
-rw-r--r--builtin/commit.c12
-rw-r--r--builtin/for-each-ref.c11
-rw-r--r--builtin/log.c6
-rw-r--r--builtin/push.c5
-rw-r--r--builtin/receive-pack.c235
-rw-r--r--builtin/reflog.c259
-rw-r--r--builtin/remote.c4
-rw-r--r--builtin/send-pack.c6
-rw-r--r--builtin/show-branch.c5
12 files changed, 322 insertions, 254 deletions
diff --git a/builtin/apply.c b/builtin/apply.c
index 7cd9a3b..23bda96 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -657,11 +657,6 @@ static size_t diff_timestamp_len(const char *line, size_t len)
return line + len - end;
}
-static char *null_strdup(const char *s)
-{
- return s ? xstrdup(s) : NULL;
-}
-
static char *find_name_common(const char *line, const char *def,
int p_value, const char *end, int terminate)
{
@@ -684,10 +679,10 @@ static char *find_name_common(const char *line, const char *def,
start = line;
}
if (!start)
- return squash_slash(null_strdup(def));
+ return squash_slash(xstrdup_or_null(def));
len = line - start;
if (!len)
- return squash_slash(null_strdup(def));
+ return squash_slash(xstrdup_or_null(def));
/*
* Generally we prefer the shorter name, especially
@@ -909,7 +904,7 @@ static void parse_traditional_patch(const char *first, const char *second, struc
patch->old_name = name;
} else {
patch->old_name = name;
- patch->new_name = null_strdup(name);
+ patch->new_name = xstrdup_or_null(name);
}
}
if (!name)
@@ -998,7 +993,7 @@ static int gitdiff_delete(const char *line, struct patch *patch)
{
patch->is_delete = 1;
free(patch->old_name);
- patch->old_name = null_strdup(patch->def_name);
+ patch->old_name = xstrdup_or_null(patch->def_name);
return gitdiff_oldmode(line, patch);
}
@@ -1006,7 +1001,7 @@ static int gitdiff_newfile(const char *line, struct patch *patch)
{
patch->is_new = 1;
free(patch->new_name);
- patch->new_name = null_strdup(patch->def_name);
+ patch->new_name = xstrdup_or_null(patch->def_name);
return gitdiff_newmode(line, patch);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index f0fac65..44b0134 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2390,7 +2390,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
return commit;
}
-static const char *prepare_final(struct scoreboard *sb)
+static char *prepare_final(struct scoreboard *sb)
{
int i;
const char *final_commit_name = NULL;
@@ -2415,10 +2415,10 @@ static const char *prepare_final(struct scoreboard *sb)
sb->final = (struct commit *) obj;
final_commit_name = revs->pending.objects[i].name;
}
- return final_commit_name;
+ return xstrdup_or_null(final_commit_name);
}
-static const char *prepare_initial(struct scoreboard *sb)
+static char *prepare_initial(struct scoreboard *sb)
{
int i;
const char *final_commit_name = NULL;
@@ -2445,7 +2445,7 @@ static const char *prepare_initial(struct scoreboard *sb)
}
if (!final_commit_name)
die("No commit to dig down to?");
- return final_commit_name;
+ return xstrdup(final_commit_name);
}
static int blame_copy_callback(const struct option *option, const char *arg, int unset)
@@ -2489,7 +2489,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
struct origin *o;
struct blame_entry *ent = NULL;
long dashdash_pos, lno;
- const char *final_commit_name = NULL;
+ char *final_commit_name = NULL;
enum object_type type;
static struct string_list range_list;
@@ -2786,6 +2786,8 @@ parse_done:
assign_blame(&sb, opt);
+ free(final_commit_name);
+
if (incremental)
return 0;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index c2e4e53..df99df4 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -4,12 +4,8 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
-#include "exec_cmd.h"
-#include "tag.h"
-#include "tree.h"
#include "builtin.h"
#include "parse-options.h"
-#include "diff.h"
#include "userdiff.h"
#include "streaming.h"
@@ -79,8 +75,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
if (type_from_string(exp_type) == OBJ_BLOB) {
unsigned char blob_sha1[20];
if (sha1_object_info(sha1, NULL) == OBJ_TAG) {
- enum object_type type;
- unsigned long size;
char *buffer = read_sha1_file(sha1, &type, &size);
const char *target;
if (!skip_prefix(buffer, "object ", &target) ||
diff --git a/builtin/commit.c b/builtin/commit.c
index 6716e11..7f46713 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -559,20 +559,14 @@ static void set_ident_var(char **buf, char *val)
*buf = val;
}
-static char *envdup(const char *var)
-{
- const char *val = getenv(var);
- return val ? xstrdup(val) : NULL;
-}
-
static void determine_author_info(struct strbuf *author_ident)
{
char *name, *email, *date;
struct ident_split author;
- name = envdup("GIT_AUTHOR_NAME");
- email = envdup("GIT_AUTHOR_EMAIL");
- date = envdup("GIT_AUTHOR_DATE");
+ name = xstrdup_or_null(getenv("GIT_AUTHOR_NAME"));
+ email = xstrdup_or_null(getenv("GIT_AUTHOR_EMAIL"));
+ date = xstrdup_or_null(getenv("GIT_AUTHOR_DATE"));
if (author_message) {
struct ident_split ident;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index c97368a..19be78a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -717,7 +717,10 @@ static void populate_value(struct refinfo *ref)
starts_with(name, "upstream")) {
char buf[40];
- stat_tracking_info(branch, &num_ours, &num_theirs);
+ if (stat_tracking_info(branch, &num_ours,
+ &num_theirs) != 1)
+ continue;
+
if (!num_ours && !num_theirs)
v->s = "";
else if (!num_ours) {
@@ -735,7 +738,11 @@ static void populate_value(struct refinfo *ref)
} else if (!strcmp(formatp, "trackshort") &&
starts_with(name, "upstream")) {
assert(branch);
- stat_tracking_info(branch, &num_ours, &num_theirs);
+
+ if (stat_tracking_info(branch, &num_ours,
+ &num_theirs) != 1)
+ continue;
+
if (!num_ours && !num_theirs)
v->s = "=";
else if (!num_ours)
diff --git a/builtin/log.c b/builtin/log.c
index d65d96a..dd8f3fc 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -38,8 +38,8 @@ static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
static const char * const builtin_log_usage[] = {
- N_("git log [<options>] [<revision-range>] [[--] <path>...]\n")
- N_(" or: git show [<options>] <object>..."),
+ N_("git log [<options>] [<revision range>] [[--] <path>...]"),
+ N_("git show [<options>] <object>..."),
NULL
};
@@ -705,7 +705,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
- !strcmp(var, "color.ui")) {
+ !strcmp(var, "color.ui") || !strcmp(var, "diff.submodule")) {
return 0;
}
if (!strcmp(var, "format.numbered")) {
diff --git a/builtin/push.c b/builtin/push.c
index 12f5e69..fc771a9 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -487,6 +487,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
int flags = 0;
int tags = 0;
int rc;
+ int atomic = 0;
const char *repo = NULL; /* default repository */
struct option options[] = {
OPT__VERBOSITY(&verbosity),
@@ -518,6 +519,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
TRANSPORT_PUSH_FOLLOW_TAGS),
OPT_BIT(0, "signed", &flags, N_("GPG sign the push"), TRANSPORT_PUSH_CERT),
+ OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
OPT_END()
};
@@ -533,6 +535,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
if (tags)
add_refspec("refs/tags/*");
+ if (atomic)
+ flags |= TRANSPORT_PUSH_ATOMIC;
+
if (argc > 0) {
repo = argv[0];
set_refspecs(argv + 1, argc - 1, repo);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 8266c1f..e0ce78e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -38,9 +38,11 @@ static int receive_fsck_objects = -1;
static int transfer_fsck_objects = -1;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
+static int advertise_atomic_push = 1;
static int unpack_limit = 100;
static int report_status;
static int use_sideband;
+static int use_atomic;
static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
@@ -67,6 +69,7 @@ static const char *NONCE_SLOP = "SLOP";
static const char *nonce_status;
static long nonce_stamp_slop;
static unsigned long nonce_stamp_slop_limit;
+static struct ref_transaction *transaction;
static enum deny_action parse_deny_action(const char *var, const char *value)
{
@@ -160,6 +163,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "receive.advertiseatomic") == 0) {
+ advertise_atomic_push = git_config_bool(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -175,6 +183,8 @@ static void show_ref(const char *path, const unsigned char *sha1)
strbuf_addstr(&cap,
"report-status delete-refs side-band-64k quiet");
+ if (advertise_atomic_push)
+ strbuf_addstr(&cap, " atomic");
if (prefer_ofs_delta)
strbuf_addstr(&cap, " ofs-delta");
if (push_cert_nonce)
@@ -733,7 +743,9 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
return 0;
}
-static const char *update_worktree(unsigned char *sha1)
+static const char *push_to_deploy(unsigned char *sha1,
+ struct argv_array *env,
+ const char *work_tree)
{
const char *update_refresh[] = {
"update-index", "-q", "--ignore-submodules", "--refresh", NULL
@@ -748,69 +760,87 @@ static const char *update_worktree(unsigned char *sha1)
const char *read_tree[] = {
"read-tree", "-u", "-m", NULL, NULL
};
- const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : "..";
- struct argv_array env = ARGV_ARRAY_INIT;
struct child_process child = CHILD_PROCESS_INIT;
- if (is_bare_repository())
- return "denyCurrentBranch = updateInstead needs a worktree";
-
- argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir()));
-
child.argv = update_refresh;
- child.env = env.argv;
+ child.env = env->argv;
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
child.git_cmd = 1;
- if (run_command(&child)) {
- argv_array_clear(&env);
+ if (run_command(&child))
return "Up-to-date check failed";
- }
/* run_command() does not clean up completely; reinitialize */
child_process_init(&child);
child.argv = diff_files;
- child.env = env.argv;
+ child.env = env->argv;
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
child.git_cmd = 1;
- if (run_command(&child)) {
- argv_array_clear(&env);
+ if (run_command(&child))
return "Working directory has unstaged changes";
- }
child_process_init(&child);
child.argv = diff_index;
- child.env = env.argv;
+ child.env = env->argv;
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
child.git_cmd = 1;
- if (run_command(&child)) {
- argv_array_clear(&env);
+ if (run_command(&child))
return "Working directory has staged changes";
- }
read_tree[3] = sha1_to_hex(sha1);
child_process_init(&child);
child.argv = read_tree;
- child.env = env.argv;
+ child.env = env->argv;
child.dir = work_tree;
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
child.git_cmd = 1;
- if (run_command(&child)) {
- argv_array_clear(&env);
+ if (run_command(&child))
return "Could not update working tree to new HEAD";
- }
- argv_array_clear(&env);
return NULL;
}
+static const char *push_to_checkout_hook = "push-to-checkout";
+
+static const char *push_to_checkout(unsigned char *sha1,
+ struct argv_array *env,
+ const char *work_tree)
+{
+ argv_array_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
+ if (run_hook_le(env->argv, push_to_checkout_hook,
+ sha1_to_hex(sha1), NULL))
+ return "push-to-checkout hook declined";
+ else
+ return NULL;
+}
+
+static const char *update_worktree(unsigned char *sha1)
+{
+ const char *retval;
+ const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : "..";
+ struct argv_array env = ARGV_ARRAY_INIT;
+
+ if (is_bare_repository())
+ return "denyCurrentBranch = updateInstead needs a worktree";
+
+ argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+
+ if (!find_hook(push_to_checkout_hook))
+ retval = push_to_deploy(sha1, &env, work_tree);
+ else
+ retval = push_to_checkout(sha1, &env, work_tree);
+
+ argv_array_clear(&env);
+ return retval;
+}
+
static const char *update(struct command *cmd, struct shallow_info *si)
{
const char *name = cmd->ref_name;
@@ -910,6 +940,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
if (is_null_sha1(new_sha1)) {
+ struct strbuf err = STRBUF_INIT;
if (!parse_object(old_sha1)) {
old_sha1 = NULL;
if (ref_exists(name)) {
@@ -919,35 +950,36 @@ static const char *update(struct command *cmd, struct shallow_info *si)
cmd->did_not_exist = 1;
}
}
- if (delete_ref(namespaced_name, old_sha1, 0)) {
- rp_error("failed to delete %s", name);
+ if (ref_transaction_delete(transaction,
+ namespaced_name,
+ old_sha1,
+ 0, old_sha1 != NULL,
+ "push", &err)) {
+ rp_error("%s", err.buf);
+ strbuf_release(&err);
return "failed to delete";
}
+ strbuf_release(&err);
return NULL; /* good */
}
else {
struct strbuf err = STRBUF_INIT;
- struct ref_transaction *transaction;
-
if (shallow_update && si->shallow_ref[cmd->index] &&
update_shallow_ref(cmd, si))
return "shallow error";
- transaction = ref_transaction_begin(&err);
- if (!transaction ||
- ref_transaction_update(transaction, namespaced_name,
- new_sha1, old_sha1, 0, 1, "push",
- &err) ||
- ref_transaction_commit(transaction, &err)) {
- ref_transaction_free(transaction);
-
+ if (ref_transaction_update(transaction,
+ namespaced_name,
+ new_sha1, old_sha1,
+ 0, 1, "push",
+ &err)) {
rp_error("%s", err.buf);
strbuf_release(&err);
+
return "failed to update ref";
}
-
- ref_transaction_free(transaction);
strbuf_release(&err);
+
return NULL; /* good */
}
}
@@ -1131,11 +1163,105 @@ static void reject_updates_to_hidden(struct command *commands)
}
}
+static int should_process_cmd(struct command *cmd)
+{
+ return !cmd->error_string && !cmd->skip_update;
+}
+
+static void warn_if_skipped_connectivity_check(struct command *commands,
+ struct shallow_info *si)
+{
+ struct command *cmd;
+ int checked_connectivity = 1;
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) {
+ error("BUG: connectivity check has not been run on ref %s",
+ cmd->ref_name);
+ checked_connectivity = 0;
+ }
+ }
+ if (!checked_connectivity)
+ die("BUG: connectivity check skipped???");
+}
+
+static void execute_commands_non_atomic(struct command *commands,
+ struct shallow_info *si)
+{
+ struct command *cmd;
+ struct strbuf err = STRBUF_INIT;
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (!should_process_cmd(cmd))
+ continue;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction) {
+ rp_error("%s", err.buf);
+ strbuf_reset(&err);
+ cmd->error_string = "transaction failed to start";
+ continue;
+ }
+
+ cmd->error_string = update(cmd, si);
+
+ if (!cmd->error_string
+ && ref_transaction_commit(transaction, &err)) {
+ rp_error("%s", err.buf);
+ strbuf_reset(&err);
+ cmd->error_string = "failed to update ref";
+ }
+ ref_transaction_free(transaction);
+ }
+ strbuf_release(&err);
+}
+
+static void execute_commands_atomic(struct command *commands,
+ struct shallow_info *si)
+{
+ struct command *cmd;
+ struct strbuf err = STRBUF_INIT;
+ const char *reported_error = "atomic push failure";
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction) {
+ rp_error("%s", err.buf);
+ strbuf_reset(&err);
+ reported_error = "transaction failed to start";
+ goto failure;
+ }
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (!should_process_cmd(cmd))
+ continue;
+
+ cmd->error_string = update(cmd, si);
+
+ if (cmd->error_string)
+ goto failure;
+ }
+
+ if (ref_transaction_commit(transaction, &err)) {
+ rp_error("%s", err.buf);
+ reported_error = "atomic transaction failed";
+ goto failure;
+ }
+ goto cleanup;
+
+failure:
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (!cmd->error_string)
+ cmd->error_string = reported_error;
+
+cleanup:
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
+}
+
static void execute_commands(struct command *commands,
const char *unpacker_error,
struct shallow_info *si)
{
- int checked_connectivity;
struct command *cmd;
unsigned char sha1[20];
struct iterate_data data;
@@ -1166,27 +1292,13 @@ static void execute_commands(struct command *commands,
free(head_name_to_free);
head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
- checked_connectivity = 1;
- for (cmd = commands; cmd; cmd = cmd->next) {
- if (cmd->error_string)
- continue;
-
- if (cmd->skip_update)
- continue;
-
- cmd->error_string = update(cmd, si);
- if (shallow_update && !cmd->error_string &&
- si->shallow_ref[cmd->index]) {
- error("BUG: connectivity check has not been run on ref %s",
- cmd->ref_name);
- checked_connectivity = 0;
- }
- }
+ if (use_atomic)
+ execute_commands_atomic(commands, si);
+ else
+ execute_commands_non_atomic(commands, si);
- if (shallow_update && !checked_connectivity)
- error("BUG: run 'git fsck' for safety.\n"
- "If there are errors, try to remove "
- "the reported refs above");
+ if (shallow_update)
+ warn_if_skipped_connectivity_check(commands, si);
}
static struct command **queue_command(struct command **tail,
@@ -1268,6 +1380,9 @@ static struct command *read_head_info(struct sha1_array *shallow)
use_sideband = LARGE_PACKET_MAX;
if (parse_feature_request(feature_list, "quiet"))
quiet = 1;
+ if (advertise_atomic_push
+ && parse_feature_request(feature_list, "atomic"))
+ use_atomic = 1;
}
if (!strcmp(line, "push-cert")) {
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 2d85d26..49c64f9 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -22,18 +22,13 @@ static unsigned long default_reflog_expire_unreachable;
struct cmd_reflog_expire_cb {
struct rev_info revs;
- int dry_run;
int stalefix;
- int rewrite;
- int updateref;
- int verbose;
unsigned long expire_total;
unsigned long expire_unreachable;
int recno;
};
-struct expire_reflog_cb {
- FILE *newlog;
+struct expire_reflog_policy_cb {
enum {
UE_NORMAL,
UE_ALWAYS,
@@ -41,14 +36,16 @@ struct expire_reflog_cb {
} unreachable_expire_kind;
struct commit_list *mark_list;
unsigned long mark_limit;
- struct cmd_reflog_expire_cb *cmd;
- unsigned char last_kept_sha1[20];
+ struct cmd_reflog_expire_cb cmd;
+ struct commit *tip_commit;
+ struct commit_list *tips;
};
struct collected_reflog {
unsigned char sha1[20];
char reflog[FLEX_ARRAY];
};
+
struct collect_reflog_cb {
struct collected_reflog **e;
int alloc;
@@ -220,7 +217,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
* the expire_limit and queue them back, so that the caller can call
* us again to restart the traversal with longer expire_limit.
*/
-static void mark_reachable(struct expire_reflog_cb *cb)
+static void mark_reachable(struct expire_reflog_policy_cb *cb)
{
struct commit *commit;
struct commit_list *pending;
@@ -259,7 +256,7 @@ static void mark_reachable(struct expire_reflog_cb *cb)
cb->mark_list = leftover;
}
-static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, unsigned char *sha1)
{
/*
* We may or may not have the commit yet - if not, look it
@@ -288,55 +285,39 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig
return !(commit->object.flags & REACHABLE);
}
-static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
- const char *email, unsigned long timestamp, int tz,
- const char *message, void *cb_data)
+/*
+ * Return true iff the specified reflog entry should be expired.
+ */
+static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+ const char *email, unsigned long timestamp, int tz,
+ const char *message, void *cb_data)
{
- struct expire_reflog_cb *cb = cb_data;
+ struct expire_reflog_policy_cb *cb = cb_data;
struct commit *old, *new;
- if (timestamp < cb->cmd->expire_total)
- goto prune;
-
- if (cb->cmd->rewrite)
- osha1 = cb->last_kept_sha1;
+ if (timestamp < cb->cmd.expire_total)
+ return 1;
old = new = NULL;
- if (cb->cmd->stalefix &&
+ if (cb->cmd.stalefix &&
(!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
- goto prune;
+ return 1;
- if (timestamp < cb->cmd->expire_unreachable) {
+ if (timestamp < cb->cmd.expire_unreachable) {
if (cb->unreachable_expire_kind == UE_ALWAYS)
- goto prune;
+ return 1;
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
- goto prune;
+ return 1;
}
- if (cb->cmd->recno && --(cb->cmd->recno) == 0)
- goto prune;
-
- if (cb->newlog) {
- char sign = (tz < 0) ? '-' : '+';
- int zone = (tz < 0) ? (-tz) : tz;
- fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
- sha1_to_hex(osha1), sha1_to_hex(nsha1),
- email, timestamp, sign, zone,
- message);
- hashcpy(cb->last_kept_sha1, nsha1);
- }
- if (cb->cmd->verbose)
- printf("keep %s", message);
- return 0;
- prune:
- if (!cb->newlog)
- printf("would prune %s", message);
- else if (cb->cmd->verbose)
- printf("prune %s", message);
+ if (cb->cmd.recno && --(cb->cmd.recno) == 0)
+ return 1;
+
return 0;
}
-static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+static int push_tip_to_list(const char *refname, const unsigned char *sha1,
+ int flags, void *cb_data)
{
struct commit_list **list = cb_data;
struct commit *tip_commit;
@@ -349,104 +330,56 @@ static int push_tip_to_list(const char *refname, const unsigned char *sha1, int
return 0;
}
-static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
+static void reflog_expiry_prepare(const char *refname,
+ const unsigned char *sha1,
+ void *cb_data)
{
- struct cmd_reflog_expire_cb *cmd = cb_data;
- struct expire_reflog_cb cb;
- struct ref_lock *lock;
- char *log_file, *newlog_path = NULL;
- struct commit *tip_commit;
- struct commit_list *tips;
- int status = 0;
-
- memset(&cb, 0, sizeof(cb));
-
- /*
- * we take the lock for the ref itself to prevent it from
- * getting updated.
- */
- lock = lock_any_ref_for_update(ref, sha1, 0, NULL);
- if (!lock)
- return error("cannot lock ref '%s'", ref);
- log_file = git_pathdup("logs/%s", ref);
- if (!reflog_exists(ref))
- goto finish;
- if (!cmd->dry_run) {
- newlog_path = git_pathdup("logs/%s.lock", ref);
- cb.newlog = fopen(newlog_path, "w");
- }
-
- cb.cmd = cmd;
+ struct expire_reflog_policy_cb *cb = cb_data;
- if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
- tip_commit = NULL;
- cb.unreachable_expire_kind = UE_HEAD;
+ if (!cb->cmd.expire_unreachable || !strcmp(refname, "HEAD")) {
+ cb->tip_commit = NULL;
+ cb->unreachable_expire_kind = UE_HEAD;
} else {
- tip_commit = lookup_commit_reference_gently(sha1, 1);
- if (!tip_commit)
- cb.unreachable_expire_kind = UE_ALWAYS;
+ cb->tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!cb->tip_commit)
+ cb->unreachable_expire_kind = UE_ALWAYS;
else
- cb.unreachable_expire_kind = UE_NORMAL;
+ cb->unreachable_expire_kind = UE_NORMAL;
}
- if (cmd->expire_unreachable <= cmd->expire_total)
- cb.unreachable_expire_kind = UE_ALWAYS;
+ if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
+ cb->unreachable_expire_kind = UE_ALWAYS;
- cb.mark_list = NULL;
- tips = NULL;
- if (cb.unreachable_expire_kind != UE_ALWAYS) {
- if (cb.unreachable_expire_kind == UE_HEAD) {
+ cb->mark_list = NULL;
+ cb->tips = NULL;
+ if (cb->unreachable_expire_kind != UE_ALWAYS) {
+ if (cb->unreachable_expire_kind == UE_HEAD) {
struct commit_list *elem;
- for_each_ref(push_tip_to_list, &tips);
- for (elem = tips; elem; elem = elem->next)
- commit_list_insert(elem->item, &cb.mark_list);
+ for_each_ref(push_tip_to_list, &cb->tips);
+ for (elem = cb->tips; elem; elem = elem->next)
+ commit_list_insert(elem->item, &cb->mark_list);
} else {
- commit_list_insert(tip_commit, &cb.mark_list);
+ commit_list_insert(cb->tip_commit, &cb->mark_list);
}
- cb.mark_limit = cmd->expire_total;
- mark_reachable(&cb);
+ cb->mark_limit = cb->cmd.expire_total;
+ mark_reachable(cb);
}
+}
- for_each_reflog_ent(ref, expire_reflog_ent, &cb);
+static void reflog_expiry_cleanup(void *cb_data)
+{
+ struct expire_reflog_policy_cb *cb = cb_data;
- if (cb.unreachable_expire_kind != UE_ALWAYS) {
- if (cb.unreachable_expire_kind == UE_HEAD) {
+ if (cb->unreachable_expire_kind != UE_ALWAYS) {
+ if (cb->unreachable_expire_kind == UE_HEAD) {
struct commit_list *elem;
- for (elem = tips; elem; elem = elem->next)
+ for (elem = cb->tips; elem; elem = elem->next)
clear_commit_marks(elem->item, REACHABLE);
- free_commit_list(tips);
+ free_commit_list(cb->tips);
} else {
- clear_commit_marks(tip_commit, REACHABLE);
+ clear_commit_marks(cb->tip_commit, REACHABLE);
}
}
- finish:
- if (cb.newlog) {
- if (fclose(cb.newlog)) {
- status |= error("%s: %s", strerror(errno),
- newlog_path);
- unlink(newlog_path);
- } else if (cmd->updateref &&
- (write_in_full(lock->lock_fd,
- sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
- write_str_in_full(lock->lock_fd, "\n") != 1 ||
- close_ref(lock) < 0)) {
- status |= error("Couldn't write %s",
- lock->lk->filename.buf);
- unlink(newlog_path);
- } else if (rename(newlog_path, log_file)) {
- status |= error("cannot rename %s to %s",
- newlog_path, log_file);
- unlink(newlog_path);
- } else if (cmd->updateref && commit_ref(lock)) {
- status |= error("Couldn't set %s", lock->ref_name);
- } else {
- adjust_shared_perm(log_file);
- }
- }
- free(newlog_path);
- free(log_file);
- unlock_ref(lock);
- return status;
}
static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
@@ -590,10 +523,11 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
- struct cmd_reflog_expire_cb cb;
+ struct expire_reflog_policy_cb cb;
unsigned long now = time(NULL);
int i, status, do_all;
int explicit_expiry = 0;
+ unsigned int flags = 0;
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
default_reflog_expire = now - 90 * 24 * 3600;
@@ -603,33 +537,33 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
do_all = status = 0;
memset(&cb, 0, sizeof(cb));
- cb.expire_total = default_reflog_expire;
- cb.expire_unreachable = default_reflog_expire_unreachable;
+ cb.cmd.expire_total = default_reflog_expire;
+ cb.cmd.expire_unreachable = default_reflog_expire_unreachable;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
- cb.dry_run = 1;
+ flags |= EXPIRE_REFLOGS_DRY_RUN;
else if (starts_with(arg, "--expire=")) {
- if (parse_expiry_date(arg + 9, &cb.expire_total))
+ if (parse_expiry_date(arg + 9, &cb.cmd.expire_total))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_TOTAL;
}
else if (starts_with(arg, "--expire-unreachable=")) {
- if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
+ if (parse_expiry_date(arg + 21, &cb.cmd.expire_unreachable))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_UNREACH;
}
else if (!strcmp(arg, "--stale-fix"))
- cb.stalefix = 1;
+ cb.cmd.stalefix = 1;
else if (!strcmp(arg, "--rewrite"))
- cb.rewrite = 1;
+ flags |= EXPIRE_REFLOGS_REWRITE;
else if (!strcmp(arg, "--updateref"))
- cb.updateref = 1;
+ flags |= EXPIRE_REFLOGS_UPDATE_REF;
else if (!strcmp(arg, "--all"))
do_all = 1;
else if (!strcmp(arg, "--verbose"))
- cb.verbose = 1;
+ flags |= EXPIRE_REFLOGS_VERBOSE;
else if (!strcmp(arg, "--")) {
i++;
break;
@@ -645,12 +579,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
* even in older repository. We cannot trust what's reachable
* from reflog if the repository was pruned with older git.
*/
- if (cb.stalefix) {
- init_revisions(&cb.revs, prefix);
- if (cb.verbose)
+ if (cb.cmd.stalefix) {
+ init_revisions(&cb.cmd.revs, prefix);
+ if (flags & EXPIRE_REFLOGS_VERBOSE)
printf("Marking reachable objects...");
- mark_reachable_objects(&cb.revs, 0, 0, NULL);
- if (cb.verbose)
+ mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
+ if (flags & EXPIRE_REFLOGS_VERBOSE)
putchar('\n');
}
@@ -662,8 +596,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
for_each_reflog(collect_reflog, &collected);
for (i = 0; i < collected.nr; i++) {
struct collected_reflog *e = collected.e[i];
- set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
- status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
+ set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
+ status |= reflog_expire(e->reflog, e->sha1, flags,
+ reflog_expiry_prepare,
+ should_expire_reflog_ent,
+ reflog_expiry_cleanup,
+ &cb);
free(e);
}
free(collected.e);
@@ -676,8 +614,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
status |= error("%s points nowhere!", argv[i]);
continue;
}
- set_reflog_expiry_param(&cb, explicit_expiry, ref);
- status |= expire_reflog(ref, sha1, 0, &cb);
+ set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
+ status |= reflog_expire(ref, sha1, flags,
+ reflog_expiry_prepare,
+ should_expire_reflog_ent,
+ reflog_expiry_cleanup,
+ &cb);
}
return status;
}
@@ -686,29 +628,30 @@ static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
{
- struct cmd_reflog_expire_cb *cb = cb_data;
- if (!cb->expire_total || timestamp < cb->expire_total)
- cb->recno++;
+ struct expire_reflog_policy_cb *cb = cb_data;
+ if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total)
+ cb->cmd.recno++;
return 0;
}
static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
{
- struct cmd_reflog_expire_cb cb;
+ struct expire_reflog_policy_cb cb;
int i, status = 0;
+ unsigned int flags = 0;
memset(&cb, 0, sizeof(cb));
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
- cb.dry_run = 1;
+ flags |= EXPIRE_REFLOGS_DRY_RUN;
else if (!strcmp(arg, "--rewrite"))
- cb.rewrite = 1;
+ flags |= EXPIRE_REFLOGS_REWRITE;
else if (!strcmp(arg, "--updateref"))
- cb.updateref = 1;
+ flags |= EXPIRE_REFLOGS_UPDATE_REF;
else if (!strcmp(arg, "--verbose"))
- cb.verbose = 1;
+ flags |= EXPIRE_REFLOGS_VERBOSE;
else if (!strcmp(arg, "--")) {
i++;
break;
@@ -740,15 +683,19 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
recno = strtoul(spec + 2, &ep, 10);
if (*ep == '}') {
- cb.recno = -recno;
+ cb.cmd.recno = -recno;
for_each_reflog_ent(ref, count_reflog_ent, &cb);
} else {
- cb.expire_total = approxidate(spec + 2);
+ cb.cmd.expire_total = approxidate(spec + 2);
for_each_reflog_ent(ref, count_reflog_ent, &cb);
- cb.expire_total = 0;
+ cb.cmd.expire_total = 0;
}
- status |= expire_reflog(ref, sha1, 0, &cb);
+ status |= reflog_expire(ref, sha1, flags,
+ reflog_expiry_prepare,
+ should_expire_reflog_ent,
+ reflog_expiry_cleanup,
+ &cb);
free(ref);
}
return status;
diff --git a/builtin/remote.c b/builtin/remote.c
index 33ff7e0..5d3ab90 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -180,7 +180,9 @@ static int add(int argc, const char **argv)
url = argv[1];
remote = remote_get(name);
- if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
+ if (remote && (remote->url_nr > 1 ||
+ (strcmp(name, remote->url[0]) &&
+ strcmp(url, remote->url[0])) ||
remote->fetch_refspec_nr))
die(_("remote %s already exists."), name);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index b564a77..b961e5a 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -13,7 +13,7 @@
#include "sha1-array.h"
static const char send_pack_usage[] =
-"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]\n"
" --all and explicit <ref> specification are mutually exclusive.";
static struct send_pack_args args;
@@ -170,6 +170,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.use_thin_pack = 1;
continue;
}
+ if (!strcmp(arg, "--atomic")) {
+ args.atomic = 1;
+ continue;
+ }
if (!strcmp(arg, "--stateless-rpc")) {
args.stateless_rpc = 1;
continue;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index e9b37dc..f3fb5fb 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -6,7 +6,10 @@
#include "parse-options.h"
static const char* show_branch_usage[] = {
- N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+ N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
+ " [--current] [--color[=<when>] | --no-color] [--sparse]\n"
+ " [--more=<n> | --list | --independent | --merge-base]\n"
+ " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"),
NULL
};