Merge branch 'dt/refs-backend-preamble'
In preparation for allowing different "backends" to store the refs in a way different from the traditional "one ref per file in $GIT_DIR or in a $GIT_DIR/packed-refs file" filesystem storage, reduce direct filesystem access to ref-like things like CHERRY_PICK_HEAD from scripts and programs. * dt/refs-backend-preamble: git-stash: use update-ref --create-reflog instead of creating files update-ref and tag: add --create-reflog arg refs: add REF_FORCE_CREATE_REFLOG flag git-reflog: add exists command refs: new public ref function: safe_create_reflog refs: break out check for reflog autocreation refs.c: add err arguments to reflog functions
This commit is contained in:
commit
b6d323f164
@ -23,6 +23,7 @@ depending on the subcommand:
|
||||
[--dry-run] [--verbose] [--all | <refs>...]
|
||||
'git reflog delete' [--rewrite] [--updateref]
|
||||
[--dry-run] [--verbose] ref@\{specifier\}...
|
||||
'git reflog exists' <ref>
|
||||
|
||||
Reference logs, or "reflogs", record when the tips of branches and
|
||||
other references were updated in the local repository. Reflogs are
|
||||
@ -52,6 +53,9 @@ argument must be an _exact_ entry (e.g. "`git reflog delete
|
||||
master@{2}`"). This subcommand is also typically not used directly by
|
||||
end users.
|
||||
|
||||
The "exists" subcommand checks whether a ref has a reflog. It exits
|
||||
with zero status if the reflog exists, and non-zero status if it does
|
||||
not.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
@ -13,7 +13,7 @@ SYNOPSIS
|
||||
<tagname> [<commit> | <object>]
|
||||
'git tag' -d <tagname>...
|
||||
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
|
||||
[--column[=<options>] | --no-column] [<pattern>...]
|
||||
[--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]
|
||||
'git tag' -v <tagname>...
|
||||
|
||||
DESCRIPTION
|
||||
@ -142,6 +142,9 @@ This option is only applicable when listing tags without annotation lines.
|
||||
all, 'whitespace' removes just leading/trailing whitespace lines and
|
||||
'strip' removes both whitespace and commentary.
|
||||
|
||||
--create-reflog::
|
||||
Create a reflog for the tag.
|
||||
|
||||
<tagname>::
|
||||
The name of the tag to create, delete, or describe.
|
||||
The new tag name must pass all checks defined by
|
||||
|
@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
|
||||
'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -67,6 +67,9 @@ performs all modifications together. Specify commands of the form:
|
||||
verify SP <ref> [SP <oldvalue>] LF
|
||||
option SP <opt> LF
|
||||
|
||||
With `--create-reflog`, update-ref will create a reflog for each ref
|
||||
even if one would not ordinarily be created.
|
||||
|
||||
Quote fields containing whitespace as if they were strings in C source
|
||||
code; i.e., surrounded by double-quotes and with backslash escapes.
|
||||
Use 40 "0" characters or the empty string to specify a zero value. To
|
||||
|
@ -612,22 +612,20 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
|
||||
if (opts->new_branch) {
|
||||
if (opts->new_orphan_branch) {
|
||||
if (opts->new_branch_log && !log_all_ref_updates) {
|
||||
int temp;
|
||||
struct strbuf log_file = STRBUF_INIT;
|
||||
int ret;
|
||||
const char *ref_name;
|
||||
char *refname;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
|
||||
ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
|
||||
temp = log_all_ref_updates;
|
||||
log_all_ref_updates = 1;
|
||||
ret = log_ref_setup(ref_name, &log_file);
|
||||
log_all_ref_updates = temp;
|
||||
strbuf_release(&log_file);
|
||||
refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
|
||||
ret = safe_create_reflog(refname, 1, &err);
|
||||
free(refname);
|
||||
if (ret) {
|
||||
fprintf(stderr, _("Can not do reflog for '%s'\n"),
|
||||
opts->new_orphan_branch);
|
||||
fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
|
||||
opts->new_orphan_branch, err.buf);
|
||||
strbuf_release(&err);
|
||||
return;
|
||||
}
|
||||
strbuf_release(&err);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -13,6 +13,8 @@ static const char reflog_expire_usage[] =
|
||||
"git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all] <refs>...";
|
||||
static const char reflog_delete_usage[] =
|
||||
"git reflog delete [--rewrite] [--updateref] [--dry-run | -n] [--verbose] <refs>...";
|
||||
static const char reflog_exists_usage[] =
|
||||
"git reflog exists <ref>";
|
||||
|
||||
static unsigned long default_reflog_expire;
|
||||
static unsigned long default_reflog_expire_unreachable;
|
||||
@ -699,12 +701,38 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, start = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (!strcmp(arg, "--")) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
else if (arg[0] == '-')
|
||||
usage(reflog_exists_usage);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
start = i;
|
||||
|
||||
if (argc - start != 1)
|
||||
usage(reflog_exists_usage);
|
||||
|
||||
if (check_refname_format(argv[start], REFNAME_ALLOW_ONELEVEL))
|
||||
die("invalid ref format: %s", argv[start]);
|
||||
return !reflog_exists(argv[start]);
|
||||
}
|
||||
|
||||
/*
|
||||
* main "reflog"
|
||||
*/
|
||||
|
||||
static const char reflog_usage[] =
|
||||
"git reflog [ show | expire | delete ]";
|
||||
"git reflog [ show | expire | delete | exists ]";
|
||||
|
||||
int cmd_reflog(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
@ -724,5 +752,8 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
|
||||
if (!strcmp(argv[1], "delete"))
|
||||
return cmd_reflog_delete(argc - 1, argv + 1, prefix);
|
||||
|
||||
if (!strcmp(argv[1], "exists"))
|
||||
return cmd_reflog_exists(argc - 1, argv + 1, prefix);
|
||||
|
||||
return cmd_log_reflog(argc, argv, prefix);
|
||||
}
|
||||
|
@ -579,6 +579,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
struct create_tag_options opt;
|
||||
char *cleanup_arg = NULL;
|
||||
int annotate = 0, force = 0, lines = -1;
|
||||
int create_reflog = 0;
|
||||
int cmdmode = 0;
|
||||
const char *msgfile = NULL, *keyid = NULL;
|
||||
struct msg_arg msg = { 0, STRBUF_INIT };
|
||||
@ -605,6 +606,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
OPT_STRING('u', "local-user", &keyid, N_("key-id"),
|
||||
N_("use another key to sign the tag")),
|
||||
OPT__FORCE(&force, N_("replace the tag if exists")),
|
||||
OPT_BOOL(0, "create-reflog", &create_reflog, N_("create_reflog")),
|
||||
|
||||
OPT_GROUP(N_("Tag listing options")),
|
||||
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
|
||||
@ -733,7 +735,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, ref.buf, object, prev,
|
||||
0, NULL, &err) ||
|
||||
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
|
||||
NULL, &err) ||
|
||||
ref_transaction_commit(transaction, &err))
|
||||
die("%s", err.buf);
|
||||
ref_transaction_free(transaction);
|
||||
|
@ -14,6 +14,7 @@ static const char * const git_update_ref_usage[] = {
|
||||
|
||||
static char line_termination = '\n';
|
||||
static int update_flags;
|
||||
static unsigned create_reflog_flag;
|
||||
static const char *msg;
|
||||
|
||||
/*
|
||||
@ -200,7 +201,8 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
|
||||
|
||||
if (ref_transaction_update(transaction, refname,
|
||||
new_sha1, have_old ? old_sha1 : NULL,
|
||||
update_flags, msg, &err))
|
||||
update_flags | create_reflog_flag,
|
||||
msg, &err))
|
||||
die("%s", err.buf);
|
||||
|
||||
update_flags = 0;
|
||||
@ -231,7 +233,8 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
|
||||
die("create %s: extra input: %s", refname, next);
|
||||
|
||||
if (ref_transaction_create(transaction, refname, new_sha1,
|
||||
update_flags, msg, &err))
|
||||
update_flags | create_reflog_flag,
|
||||
msg, &err))
|
||||
die("%s", err.buf);
|
||||
|
||||
update_flags = 0;
|
||||
@ -354,6 +357,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
|
||||
unsigned char sha1[20], oldsha1[20];
|
||||
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
|
||||
unsigned int flags = 0;
|
||||
int create_reflog = 0;
|
||||
struct option options[] = {
|
||||
OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
|
||||
OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
|
||||
@ -361,6 +365,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
|
||||
N_("update <refname> not the one it points to")),
|
||||
OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
|
||||
OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
|
||||
OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create_reflog")),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
@ -370,6 +375,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
|
||||
if (msg && !*msg)
|
||||
die("Refusing to perform update with empty message.");
|
||||
|
||||
create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0;
|
||||
|
||||
if (read_stdin) {
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
struct ref_transaction *transaction;
|
||||
@ -431,5 +438,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
|
||||
flags);
|
||||
else
|
||||
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
|
||||
flags, UPDATE_REFS_DIE_ON_ERR);
|
||||
flags | create_reflog_flag,
|
||||
UPDATE_REFS_DIE_ON_ERR);
|
||||
}
|
||||
|
@ -183,9 +183,7 @@ store_stash () {
|
||||
stash_msg="Created via \"git stash store\"."
|
||||
fi
|
||||
|
||||
# Make sure the reflog for stash is kept.
|
||||
: >>"$(git rev-parse --git-path logs/$ref_stash)"
|
||||
git update-ref -m "$stash_msg" $ref_stash $w_commit
|
||||
git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
|
||||
ret=$?
|
||||
test $ret != 0 && test -z $quiet &&
|
||||
die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
|
||||
@ -262,7 +260,7 @@ save_stash () {
|
||||
say "$(gettext "No local changes to save")"
|
||||
exit 0
|
||||
fi
|
||||
test -f "$(git rev-parse --git-path logs/$ref_stash)" ||
|
||||
git reflog exists $ref_stash ||
|
||||
clear_stash || die "$(gettext "Cannot initialize stash")"
|
||||
|
||||
create_stash "$stash_msg" $untracked
|
||||
|
169
refs.c
169
refs.c
@ -62,6 +62,11 @@ static unsigned char refname_disposition[256] = {
|
||||
*/
|
||||
#define REF_NEEDS_COMMIT 0x20
|
||||
|
||||
/*
|
||||
* 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
|
||||
* value to ref_update::flags
|
||||
*/
|
||||
|
||||
/*
|
||||
* Try to read one refname component from the front of refname.
|
||||
* Return the length of the component found, or -1 if the component is
|
||||
@ -2975,9 +2980,11 @@ static int rename_ref_available(const char *oldname, const char *newname)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1);
|
||||
static int write_ref_to_lockfile(struct ref_lock *lock,
|
||||
const unsigned char *sha1, struct strbuf *err);
|
||||
static int commit_ref_update(struct ref_lock *lock,
|
||||
const unsigned char *sha1, const char *logmsg);
|
||||
const unsigned char *sha1, const char *logmsg,
|
||||
int flags, struct strbuf *err);
|
||||
|
||||
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
|
||||
{
|
||||
@ -3038,9 +3045,10 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
|
||||
}
|
||||
hashcpy(lock->old_oid.hash, orig_sha1);
|
||||
|
||||
if (write_ref_to_lockfile(lock, orig_sha1) ||
|
||||
commit_ref_update(lock, orig_sha1, logmsg)) {
|
||||
error("unable to write current sha1 into %s", newrefname);
|
||||
if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
|
||||
commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
|
||||
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
|
||||
strbuf_release(&err);
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
@ -3056,9 +3064,11 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
|
||||
|
||||
flag = log_all_ref_updates;
|
||||
log_all_ref_updates = 0;
|
||||
if (write_ref_to_lockfile(lock, orig_sha1) ||
|
||||
commit_ref_update(lock, orig_sha1, NULL))
|
||||
error("unable to write current sha1 into %s", oldrefname);
|
||||
if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
|
||||
commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
|
||||
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
|
||||
strbuf_release(&err);
|
||||
}
|
||||
log_all_ref_updates = flag;
|
||||
|
||||
rollbacklog:
|
||||
@ -3113,8 +3123,23 @@ static int copy_msg(char *buf, const char *msg)
|
||||
return cp - buf;
|
||||
}
|
||||
|
||||
/* This function must set a meaningful errno on failure */
|
||||
int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
|
||||
static int should_autocreate_reflog(const char *refname)
|
||||
{
|
||||
if (!log_all_ref_updates)
|
||||
return 0;
|
||||
return starts_with(refname, "refs/heads/") ||
|
||||
starts_with(refname, "refs/remotes/") ||
|
||||
starts_with(refname, "refs/notes/") ||
|
||||
!strcmp(refname, "HEAD");
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a reflog for a ref. If force_create = 0, the reflog will
|
||||
* only be created for certain refs (those for which
|
||||
* should_autocreate_reflog returns non-zero. Otherwise, create it
|
||||
* regardless of the ref name. Fill in *err and return -1 on failure.
|
||||
*/
|
||||
static int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct strbuf *err, int force_create)
|
||||
{
|
||||
int logfd, oflags = O_APPEND | O_WRONLY;
|
||||
char *logfile;
|
||||
@ -3123,15 +3148,10 @@ int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
|
||||
logfile = sb_logfile->buf;
|
||||
/* make sure the rest of the function can't change "logfile" */
|
||||
sb_logfile = NULL;
|
||||
if (log_all_ref_updates &&
|
||||
(starts_with(refname, "refs/heads/") ||
|
||||
starts_with(refname, "refs/remotes/") ||
|
||||
starts_with(refname, "refs/notes/") ||
|
||||
!strcmp(refname, "HEAD"))) {
|
||||
if (force_create || should_autocreate_reflog(refname)) {
|
||||
if (safe_create_leading_directories(logfile) < 0) {
|
||||
int save_errno = errno;
|
||||
error("unable to create directory for %s", logfile);
|
||||
errno = save_errno;
|
||||
strbuf_addf(err, "unable to create directory for %s: "
|
||||
"%s", logfile, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
oflags |= O_CREAT;
|
||||
@ -3144,20 +3164,16 @@ int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
|
||||
|
||||
if (errno == EISDIR) {
|
||||
if (remove_empty_directories(logfile)) {
|
||||
int save_errno = errno;
|
||||
error("There are still logs under '%s'",
|
||||
logfile);
|
||||
errno = save_errno;
|
||||
strbuf_addf(err, "There are still logs under "
|
||||
"'%s'", logfile);
|
||||
return -1;
|
||||
}
|
||||
logfd = open(logfile, oflags, 0666);
|
||||
}
|
||||
|
||||
if (logfd < 0) {
|
||||
int save_errno = errno;
|
||||
error("Unable to append to %s: %s", logfile,
|
||||
strerror(errno));
|
||||
errno = save_errno;
|
||||
strbuf_addf(err, "unable to append to %s: %s",
|
||||
logfile, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -3167,6 +3183,17 @@ int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
|
||||
{
|
||||
int ret;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
ret = log_ref_setup(refname, &sb, err, force_create);
|
||||
strbuf_release(&sb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1,
|
||||
const char *committer, const char *msg)
|
||||
@ -3195,7 +3222,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
|
||||
|
||||
static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1, const char *msg,
|
||||
struct strbuf *sb_log_file)
|
||||
struct strbuf *sb_log_file, int flags,
|
||||
struct strbuf *err)
|
||||
{
|
||||
int logfd, result, oflags = O_APPEND | O_WRONLY;
|
||||
char *log_file;
|
||||
@ -3203,7 +3231,8 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
|
||||
if (log_all_ref_updates < 0)
|
||||
log_all_ref_updates = !is_bare_repository();
|
||||
|
||||
result = log_ref_setup(refname, sb_log_file);
|
||||
result = log_ref_setup(refname, sb_log_file, err, flags & REF_FORCE_CREATE_REFLOG);
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
log_file = sb_log_file->buf;
|
||||
@ -3216,26 +3245,26 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
|
||||
result = log_ref_write_fd(logfd, old_sha1, new_sha1,
|
||||
git_committer_info(0), msg);
|
||||
if (result) {
|
||||
int save_errno = errno;
|
||||
strbuf_addf(err, "unable to append to %s: %s", log_file,
|
||||
strerror(errno));
|
||||
close(logfd);
|
||||
error("Unable to append to %s", log_file);
|
||||
errno = save_errno;
|
||||
return -1;
|
||||
}
|
||||
if (close(logfd)) {
|
||||
int save_errno = errno;
|
||||
error("Unable to append to %s", log_file);
|
||||
errno = save_errno;
|
||||
strbuf_addf(err, "unable to append to %s: %s", log_file,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int log_ref_write(const char *refname, const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1, const char *msg)
|
||||
const unsigned char *new_sha1, const char *msg,
|
||||
int flags, struct strbuf *err)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb);
|
||||
int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
|
||||
err);
|
||||
strbuf_release(&sb);
|
||||
return ret;
|
||||
}
|
||||
@ -3247,36 +3276,36 @@ int is_branch(const char *refname)
|
||||
|
||||
/*
|
||||
* Write sha1 into the open lockfile, then close the lockfile. On
|
||||
* errors, rollback the lockfile and set errno to reflect the problem.
|
||||
* errors, rollback the lockfile, fill in *err and
|
||||
* return -1.
|
||||
*/
|
||||
static int write_ref_to_lockfile(struct ref_lock *lock,
|
||||
const unsigned char *sha1)
|
||||
const unsigned char *sha1, struct strbuf *err)
|
||||
{
|
||||
static char term = '\n';
|
||||
struct object *o;
|
||||
|
||||
o = parse_object(sha1);
|
||||
if (!o) {
|
||||
error("Trying to write ref %s with nonexistent object %s",
|
||||
lock->ref_name, sha1_to_hex(sha1));
|
||||
strbuf_addf(err,
|
||||
"Trying to write ref %s with nonexistent object %s",
|
||||
lock->ref_name, sha1_to_hex(sha1));
|
||||
unlock_ref(lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
|
||||
error("Trying to write non-commit object %s to branch %s",
|
||||
sha1_to_hex(sha1), lock->ref_name);
|
||||
strbuf_addf(err,
|
||||
"Trying to write non-commit object %s to branch %s",
|
||||
sha1_to_hex(sha1), lock->ref_name);
|
||||
unlock_ref(lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
|
||||
write_in_full(lock->lk->fd, &term, 1) != 1 ||
|
||||
close_ref(lock) < 0) {
|
||||
int save_errno = errno;
|
||||
error("Couldn't write %s", lock->lk->filename.buf);
|
||||
strbuf_addf(err,
|
||||
"Couldn't write %s", lock->lk->filename.buf);
|
||||
unlock_ref(lock);
|
||||
errno = save_errno;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@ -3288,12 +3317,17 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
|
||||
* necessary, using the specified lockmsg (which can be NULL).
|
||||
*/
|
||||
static int commit_ref_update(struct ref_lock *lock,
|
||||
const unsigned char *sha1, const char *logmsg)
|
||||
const unsigned char *sha1, const char *logmsg,
|
||||
int flags, struct strbuf *err)
|
||||
{
|
||||
clear_loose_ref_cache(&ref_cache);
|
||||
if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 ||
|
||||
if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 ||
|
||||
(strcmp(lock->ref_name, lock->orig_ref_name) &&
|
||||
log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg) < 0)) {
|
||||
log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) {
|
||||
char *old_msg = strbuf_detach(err, NULL);
|
||||
strbuf_addf(err, "Cannot update the ref '%s': %s",
|
||||
lock->ref_name, old_msg);
|
||||
free(old_msg);
|
||||
unlock_ref(lock);
|
||||
return -1;
|
||||
}
|
||||
@ -3316,14 +3350,21 @@ static int commit_ref_update(struct ref_lock *lock,
|
||||
head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
|
||||
head_sha1, &head_flag);
|
||||
if (head_ref && (head_flag & REF_ISSYMREF) &&
|
||||
!strcmp(head_ref, lock->ref_name))
|
||||
log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
|
||||
!strcmp(head_ref, lock->ref_name)) {
|
||||
struct strbuf log_err = STRBUF_INIT;
|
||||
if (log_ref_write("HEAD", lock->old_oid.hash, sha1,
|
||||
logmsg, 0, &log_err)) {
|
||||
error("%s", log_err.buf);
|
||||
strbuf_release(&log_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (commit_ref(lock)) {
|
||||
error("Couldn't set %s", lock->ref_name);
|
||||
unlock_ref(lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unlock_ref(lock);
|
||||
return 0;
|
||||
}
|
||||
@ -3336,6 +3377,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
|
||||
int fd, len, written;
|
||||
char *git_HEAD = git_pathdup("%s", ref_target);
|
||||
unsigned char old_sha1[20], new_sha1[20];
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
|
||||
if (logmsg && read_ref(ref_target, old_sha1))
|
||||
hashclr(old_sha1);
|
||||
@ -3384,8 +3426,11 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
|
||||
#ifndef NO_SYMLINK_HEAD
|
||||
done:
|
||||
#endif
|
||||
if (logmsg && !read_ref(refs_heads_master, new_sha1))
|
||||
log_ref_write(ref_target, old_sha1, new_sha1, logmsg);
|
||||
if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
|
||||
log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
|
||||
error("%s", err.buf);
|
||||
strbuf_release(&err);
|
||||
}
|
||||
|
||||
free(git_HEAD);
|
||||
return 0;
|
||||
@ -4021,14 +4066,19 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
* value, so we don't need to write it.
|
||||
*/
|
||||
} else if (write_ref_to_lockfile(update->lock,
|
||||
update->new_sha1)) {
|
||||
update->new_sha1,
|
||||
err)) {
|
||||
char *write_err = strbuf_detach(err, NULL);
|
||||
|
||||
/*
|
||||
* The lock was freed upon failure of
|
||||
* write_ref_to_lockfile():
|
||||
*/
|
||||
update->lock = NULL;
|
||||
strbuf_addf(err, "cannot update the ref '%s'.",
|
||||
update->refname);
|
||||
strbuf_addf(err,
|
||||
"cannot update the ref '%s': %s",
|
||||
update->refname, write_err);
|
||||
free(write_err);
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto cleanup;
|
||||
} else {
|
||||
@ -4054,11 +4104,10 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
|
||||
if (update->flags & REF_NEEDS_COMMIT) {
|
||||
if (commit_ref_update(update->lock,
|
||||
update->new_sha1, update->msg)) {
|
||||
update->new_sha1, update->msg,
|
||||
update->flags, err)) {
|
||||
/* freed by commit_ref_update(): */
|
||||
update->lock = NULL;
|
||||
strbuf_addf(err, "Cannot update the ref '%s'.",
|
||||
update->refname);
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto cleanup;
|
||||
} else {
|
||||
|
5
refs.h
5
refs.h
@ -224,11 +224,12 @@ int pack_refs(unsigned int flags);
|
||||
* Other flags are reserved for internal use.
|
||||
*/
|
||||
#define REF_NODEREF 0x01
|
||||
#define REF_FORCE_CREATE_REFLOG 0x40
|
||||
|
||||
/*
|
||||
* Setup reflog before using. Set errno to something meaningful on failure.
|
||||
* Setup reflog before using. Fill in err and return -1 on failure.
|
||||
*/
|
||||
int log_ref_setup(const char *refname, struct strbuf *logfile);
|
||||
int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);
|
||||
|
||||
/** Reads log for the value of ref during at_time. **/
|
||||
extern int read_ref_at(const char *refname, unsigned int flags,
|
||||
|
@ -23,6 +23,7 @@ test_expect_success setup '
|
||||
m=refs/heads/master
|
||||
n_dir=refs/heads/gu
|
||||
n=$n_dir/fixes
|
||||
outside=foo
|
||||
|
||||
test_expect_success \
|
||||
"create $m" \
|
||||
@ -74,6 +75,24 @@ test_expect_success "delete $m (by HEAD)" '
|
||||
'
|
||||
rm -f .git/$m
|
||||
|
||||
test_expect_success 'update-ref does not create reflogs by default' '
|
||||
test_when_finished "git update-ref -d $outside" &&
|
||||
git update-ref $outside $A &&
|
||||
git rev-parse $A >expect &&
|
||||
git rev-parse $outside >actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git reflog exists $outside
|
||||
'
|
||||
|
||||
test_expect_success 'update-ref creates reflogs with --create-reflog' '
|
||||
test_when_finished "git update-ref -d $outside" &&
|
||||
git update-ref --create-reflog $outside $A &&
|
||||
git rev-parse $A >expect &&
|
||||
git rev-parse $outside >actual &&
|
||||
test_cmp expect actual &&
|
||||
git reflog exists $outside
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
"create $m (by HEAD)" \
|
||||
"git update-ref HEAD $A &&
|
||||
@ -472,6 +491,25 @@ test_expect_success 'stdin create ref works' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'stdin does not create reflogs by default' '
|
||||
test_when_finished "git update-ref -d $outside" &&
|
||||
echo "create $outside $m" >stdin &&
|
||||
git update-ref --stdin <stdin &&
|
||||
git rev-parse $m >expect &&
|
||||
git rev-parse $outside >actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git reflog exists $outside
|
||||
'
|
||||
|
||||
test_expect_success 'stdin creates reflogs with --create-reflog' '
|
||||
echo "create $outside $m" >stdin &&
|
||||
git update-ref --create-reflog --stdin <stdin &&
|
||||
git rev-parse $m >expect &&
|
||||
git rev-parse $outside >actual &&
|
||||
test_cmp expect actual &&
|
||||
git reflog exists $outside
|
||||
'
|
||||
|
||||
test_expect_success 'stdin succeeds with quoted argument' '
|
||||
git update-ref -d $a &&
|
||||
echo "create $a \"$m\"" >stdin &&
|
||||
|
@ -166,4 +166,9 @@ test_expect_success 'git log -g -p shows diffs vs. parents' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'reflog exists works' '
|
||||
git reflog exists refs/heads/master &&
|
||||
! git reflog exists refs/heads/nonexistent
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -51,7 +51,19 @@ test_expect_success 'creating a tag using default HEAD should succeed' '
|
||||
echo foo >foo &&
|
||||
git add foo &&
|
||||
git commit -m Foo &&
|
||||
git tag mytag
|
||||
git tag mytag &&
|
||||
test_must_fail git reflog exists refs/tags/mytag
|
||||
'
|
||||
|
||||
test_expect_success 'creating a tag with --create-reflog should create reflog' '
|
||||
test_when_finished "git tag -d tag_with_reflog" &&
|
||||
git tag --create-reflog tag_with_reflog &&
|
||||
git reflog exists refs/tags/tag_with_reflog
|
||||
'
|
||||
|
||||
test_expect_success '--create-reflog does not create reflog on failure' '
|
||||
test_must_fail git tag --create-reflog mytag &&
|
||||
test_must_fail git reflog exists refs/tags/mytag
|
||||
'
|
||||
|
||||
test_expect_success 'listing all tags if one exists should succeed' '
|
||||
|
Loading…
Reference in New Issue
Block a user