зеркало из https://github.com/microsoft/git.git
Merge branch 'mh/split-under-lock'
Further preparatory work on the refs API before the pluggable backend series can land. * mh/split-under-lock: (33 commits) lock_ref_sha1_basic(): only handle REF_NODEREF mode commit_ref_update(): remove the flags parameter lock_ref_for_update(): don't resolve symrefs lock_ref_for_update(): don't re-read non-symbolic references refs: resolve symbolic refs first ref_transaction_update(): check refname_is_safe() at a minimum unlock_ref(): move definition higher in the file lock_ref_for_update(): new function add_update(): initialize the whole ref_update verify_refname_available(): adjust constness in declaration refs: don't dereference on rename refs: allow log-only updates delete_branches(): use resolve_refdup() ref_transaction_commit(): correctly report close_ref() failure ref_transaction_create(): disallow recursive pruning refs: make error messages more consistent lock_ref_sha1_basic(): remove unneeded local variable read_raw_ref(): move docstring to header file read_raw_ref(): improve docstring read_raw_ref(): rename symref argument to referent ...
This commit is contained in:
Коммит
6b34ce90a7
|
@ -212,7 +212,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
|
|||
die(_("Couldn't look up commit object for HEAD"));
|
||||
}
|
||||
for (i = 0; i < argc; i++, strbuf_release(&bname)) {
|
||||
const char *target;
|
||||
char *target = NULL;
|
||||
int flags = 0;
|
||||
|
||||
strbuf_branchname(&bname, argv[i]);
|
||||
|
@ -231,11 +231,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
|
|||
}
|
||||
}
|
||||
|
||||
target = resolve_ref_unsafe(name,
|
||||
RESOLVE_REF_READING
|
||||
| RESOLVE_REF_NO_RECURSE
|
||||
| RESOLVE_REF_ALLOW_BAD_NAME,
|
||||
sha1, &flags);
|
||||
target = resolve_refdup(name,
|
||||
RESOLVE_REF_READING
|
||||
| RESOLVE_REF_NO_RECURSE
|
||||
| RESOLVE_REF_ALLOW_BAD_NAME,
|
||||
sha1, &flags);
|
||||
if (!target) {
|
||||
error(remote_branch
|
||||
? _("remote-tracking branch '%s' not found.")
|
||||
|
@ -248,7 +248,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
|
|||
check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
|
||||
force)) {
|
||||
ret = 1;
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
|
||||
|
@ -258,7 +258,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
|
|||
: _("Error deleting branch '%s'"),
|
||||
bname.buf);
|
||||
ret = 1;
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
if (!quiet) {
|
||||
printf(remote_branch
|
||||
|
@ -270,6 +270,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
|
|||
: find_unique_abbrev(sha1, DEFAULT_ABBREV));
|
||||
}
|
||||
delete_branch_config(bname.buf);
|
||||
|
||||
next:
|
||||
free(target);
|
||||
}
|
||||
|
||||
free(name);
|
||||
|
|
5
cache.h
5
cache.h
|
@ -1003,6 +1003,11 @@ int adjust_shared_perm(const char *path);
|
|||
* directory while we were working. To be robust against this kind of
|
||||
* race, callers might want to try invoking the function again when it
|
||||
* returns SCLD_VANISHED.
|
||||
*
|
||||
* safe_create_leading_directories() temporarily changes path while it
|
||||
* is working but restores it before returning.
|
||||
* safe_create_leading_directories_const() doesn't modify path, even
|
||||
* temporarily.
|
||||
*/
|
||||
enum scld_error {
|
||||
SCLD_OK = 0,
|
||||
|
|
23
dir.h
23
dir.h
|
@ -262,9 +262,32 @@ extern int is_empty_dir(const char *dir);
|
|||
|
||||
extern void setup_standard_excludes(struct dir_struct *dir);
|
||||
|
||||
|
||||
/* Constants for remove_dir_recursively: */
|
||||
|
||||
/*
|
||||
* If a non-directory is found within path, stop and return an error.
|
||||
* (In this case some empty directories might already have been
|
||||
* removed.)
|
||||
*/
|
||||
#define REMOVE_DIR_EMPTY_ONLY 01
|
||||
|
||||
/*
|
||||
* If any Git work trees are found within path, skip them without
|
||||
* considering it an error.
|
||||
*/
|
||||
#define REMOVE_DIR_KEEP_NESTED_GIT 02
|
||||
|
||||
/* Remove the contents of path, but leave path itself. */
|
||||
#define REMOVE_DIR_KEEP_TOPLEVEL 04
|
||||
|
||||
/*
|
||||
* Remove path and its contents, recursively. flags is a combination
|
||||
* of the above REMOVE_DIR_* constants. Return 0 on success.
|
||||
*
|
||||
* This function uses path as temporary scratch space, but restores it
|
||||
* before returning.
|
||||
*/
|
||||
extern int remove_dir_recursively(struct strbuf *path, int flag);
|
||||
|
||||
/* tries to remove the path with empty directories along it, ignores ENOENT */
|
||||
|
|
76
refs.c
76
refs.c
|
@ -120,25 +120,33 @@ int check_refname_format(const char *refname, int flags)
|
|||
|
||||
int refname_is_safe(const char *refname)
|
||||
{
|
||||
if (starts_with(refname, "refs/")) {
|
||||
const char *rest;
|
||||
|
||||
if (skip_prefix(refname, "refs/", &rest)) {
|
||||
char *buf;
|
||||
int result;
|
||||
size_t restlen = strlen(rest);
|
||||
|
||||
/* rest must not be empty, or start or end with "/" */
|
||||
if (!restlen || *rest == '/' || rest[restlen - 1] == '/')
|
||||
return 0;
|
||||
|
||||
buf = xmallocz(strlen(refname));
|
||||
/*
|
||||
* Does the refname try to escape refs/?
|
||||
* For example: refs/foo/../bar is safe but refs/foo/../../bar
|
||||
* is not.
|
||||
*/
|
||||
result = !normalize_path_copy(buf, refname + strlen("refs/"));
|
||||
buf = xmallocz(restlen);
|
||||
result = !normalize_path_copy(buf, rest) && !strcmp(buf, rest);
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
while (*refname) {
|
||||
|
||||
do {
|
||||
if (!isupper(*refname) && *refname != '_')
|
||||
return 0;
|
||||
refname++;
|
||||
}
|
||||
} while (*refname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -496,7 +504,7 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
|
|||
filename = git_path("%s", pseudoref);
|
||||
fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
|
||||
if (fd < 0) {
|
||||
strbuf_addf(err, "Could not open '%s' for writing: %s",
|
||||
strbuf_addf(err, "could not open '%s' for writing: %s",
|
||||
filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
@ -507,14 +515,14 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
|
|||
if (read_ref(pseudoref, actual_old_sha1))
|
||||
die("could not read ref '%s'", pseudoref);
|
||||
if (hashcmp(actual_old_sha1, old_sha1)) {
|
||||
strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref);
|
||||
strbuf_addf(err, "unexpected sha1 when writing '%s'", pseudoref);
|
||||
rollback_lock_file(&lock);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
|
||||
strbuf_addf(err, "Could not write to '%s'", filename);
|
||||
strbuf_addf(err, "could not write to '%s'", filename);
|
||||
rollback_lock_file(&lock);
|
||||
goto done;
|
||||
}
|
||||
|
@ -758,13 +766,33 @@ void ref_transaction_free(struct ref_transaction *transaction)
|
|||
free(transaction);
|
||||
}
|
||||
|
||||
static struct ref_update *add_update(struct ref_transaction *transaction,
|
||||
const char *refname)
|
||||
struct ref_update *ref_transaction_add_update(
|
||||
struct ref_transaction *transaction,
|
||||
const char *refname, unsigned int flags,
|
||||
const unsigned char *new_sha1,
|
||||
const unsigned char *old_sha1,
|
||||
const char *msg)
|
||||
{
|
||||
struct ref_update *update;
|
||||
|
||||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
die("BUG: update called for transaction that is not open");
|
||||
|
||||
if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF))
|
||||
die("BUG: REF_ISPRUNING set without REF_NODEREF");
|
||||
|
||||
FLEX_ALLOC_STR(update, refname, refname);
|
||||
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
|
||||
transaction->updates[transaction->nr++] = update;
|
||||
|
||||
update->flags = flags;
|
||||
|
||||
if (flags & REF_HAVE_NEW)
|
||||
hashcpy(update->new_sha1, new_sha1);
|
||||
if (flags & REF_HAVE_OLD)
|
||||
hashcpy(update->old_sha1, old_sha1);
|
||||
if (msg)
|
||||
update->msg = xstrdup(msg);
|
||||
return update;
|
||||
}
|
||||
|
||||
|
@ -775,32 +803,20 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
|||
unsigned int flags, const char *msg,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_update *update;
|
||||
|
||||
assert(err);
|
||||
|
||||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
die("BUG: update called for transaction that is not open");
|
||||
|
||||
if (new_sha1 && !is_null_sha1(new_sha1) &&
|
||||
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
|
||||
strbuf_addf(err, "refusing to update ref with bad name %s",
|
||||
if ((new_sha1 && !is_null_sha1(new_sha1)) ?
|
||||
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
|
||||
!refname_is_safe(refname)) {
|
||||
strbuf_addf(err, "refusing to update ref with bad name '%s'",
|
||||
refname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
update = add_update(transaction, refname);
|
||||
if (new_sha1) {
|
||||
hashcpy(update->new_sha1, new_sha1);
|
||||
flags |= REF_HAVE_NEW;
|
||||
}
|
||||
if (old_sha1) {
|
||||
hashcpy(update->old_sha1, old_sha1);
|
||||
flags |= REF_HAVE_OLD;
|
||||
}
|
||||
update->flags = flags;
|
||||
if (msg)
|
||||
update->msg = xstrdup(msg);
|
||||
flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0);
|
||||
|
||||
ref_transaction_add_update(transaction, refname, flags,
|
||||
new_sha1, old_sha1, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -15,7 +15,7 @@
|
|||
|
||||
/*
|
||||
* Used as a flag in ref_update::flags when a loose ref is being
|
||||
* pruned.
|
||||
* pruned. This flag must only be used when REF_NODEREF is set.
|
||||
*/
|
||||
#define REF_ISPRUNING 0x04
|
||||
|
||||
|
@ -42,6 +42,19 @@
|
|||
* value to ref_update::flags
|
||||
*/
|
||||
|
||||
/*
|
||||
* Used as a flag in ref_update::flags when we want to log a ref
|
||||
* update but not actually perform it. This is used when a symbolic
|
||||
* ref update is split up.
|
||||
*/
|
||||
#define REF_LOG_ONLY 0x80
|
||||
|
||||
/*
|
||||
* Internal flag, meaning that the containing ref_update was via an
|
||||
* update to HEAD.
|
||||
*/
|
||||
#define REF_UPDATE_VIA_HEAD 0x100
|
||||
|
||||
/*
|
||||
* Return true iff refname is minimally safe. "Safe" here means that
|
||||
* deleting a loose reference by this name will not do any damage, for
|
||||
|
@ -109,8 +122,8 @@ enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);
|
|||
* extras and skip must be sorted.
|
||||
*/
|
||||
int verify_refname_available(const char *newname,
|
||||
struct string_list *extras,
|
||||
struct string_list *skip,
|
||||
const struct string_list *extras,
|
||||
const struct string_list *skip,
|
||||
struct strbuf *err);
|
||||
|
||||
/*
|
||||
|
@ -130,26 +143,58 @@ int should_autocreate_reflog(const char *refname);
|
|||
* not exist before update.
|
||||
*/
|
||||
struct ref_update {
|
||||
|
||||
/*
|
||||
* If (flags & REF_HAVE_NEW), set the reference to this value:
|
||||
*/
|
||||
unsigned char new_sha1[20];
|
||||
|
||||
/*
|
||||
* If (flags & REF_HAVE_OLD), check that the reference
|
||||
* previously had this value:
|
||||
*/
|
||||
unsigned char old_sha1[20];
|
||||
|
||||
/*
|
||||
* One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
|
||||
* REF_DELETING, and REF_ISPRUNING:
|
||||
* REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY, and
|
||||
* REF_UPDATE_VIA_HEAD:
|
||||
*/
|
||||
unsigned int flags;
|
||||
|
||||
struct ref_lock *lock;
|
||||
int type;
|
||||
unsigned int type;
|
||||
char *msg;
|
||||
|
||||
/*
|
||||
* If this ref_update was split off of a symref update via
|
||||
* split_symref_update(), then this member points at that
|
||||
* update. This is used for two purposes:
|
||||
* 1. When reporting errors, we report the refname under which
|
||||
* the update was originally requested.
|
||||
* 2. When we read the old value of this reference, we
|
||||
* propagate it back to its parent update for recording in
|
||||
* the latter's reflog.
|
||||
*/
|
||||
struct ref_update *parent_update;
|
||||
|
||||
const char refname[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
/*
|
||||
* Add a ref_update with the specified properties to transaction, and
|
||||
* return a pointer to the new object. This function does not verify
|
||||
* that refname is well-formed. new_sha1 and old_sha1 are only
|
||||
* dereferenced if the REF_HAVE_NEW and REF_HAVE_OLD bits,
|
||||
* respectively, are set in flags.
|
||||
*/
|
||||
struct ref_update *ref_transaction_add_update(
|
||||
struct ref_transaction *transaction,
|
||||
const char *refname, unsigned int flags,
|
||||
const unsigned char *new_sha1,
|
||||
const unsigned char *old_sha1,
|
||||
const char *msg);
|
||||
|
||||
/*
|
||||
* Transaction states.
|
||||
* OPEN: The transaction is in a valid state and can accept new updates.
|
||||
|
@ -209,7 +254,45 @@ int rename_ref_available(const char *oldname, const char *newname);
|
|||
int do_for_each_ref(const char *submodule, const char *base,
|
||||
each_ref_fn fn, int trim, int flags, void *cb_data);
|
||||
|
||||
/*
|
||||
* Read the specified reference from the filesystem or packed refs
|
||||
* file, non-recursively. Set type to describe the reference, and:
|
||||
*
|
||||
* - If refname is the name of a normal reference, fill in sha1
|
||||
* (leaving referent unchanged).
|
||||
*
|
||||
* - If refname is the name of a symbolic reference, write the full
|
||||
* name of the reference to which it refers (e.g.
|
||||
* "refs/heads/master") to referent and set the REF_ISSYMREF bit in
|
||||
* type (leaving sha1 unchanged). The caller is responsible for
|
||||
* validating that referent is a valid reference name.
|
||||
*
|
||||
* WARNING: refname might be used as part of a filename, so it is
|
||||
* important from a security standpoint that it be safe in the sense
|
||||
* of refname_is_safe(). Moreover, for symrefs this function sets
|
||||
* referent to whatever the repository says, which might not be a
|
||||
* properly-formatted or even safe reference name. NEITHER INPUT NOR
|
||||
* OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION.
|
||||
*
|
||||
* Return 0 on success. If the ref doesn't exist, set errno to ENOENT
|
||||
* and return -1. If the ref exists but is neither a symbolic ref nor
|
||||
* a sha1, it is broken; set REF_ISBROKEN in type, set errno to
|
||||
* EINVAL, and return -1. If there is another error reading the ref,
|
||||
* set errno appropriately and return -1.
|
||||
*
|
||||
* Backend-specific flags might be set in type as well, regardless of
|
||||
* outcome.
|
||||
*
|
||||
* It is OK for refname to point into referent. If so:
|
||||
*
|
||||
* - if the function succeeds with REF_ISSYMREF, referent will be
|
||||
* overwritten and the memory formerly pointed to by it might be
|
||||
* changed or even freed.
|
||||
*
|
||||
* - in all other cases, referent will be untouched, and therefore
|
||||
* refname will still be valid and unchanged.
|
||||
*/
|
||||
int read_raw_ref(const char *refname, unsigned char *sha1,
|
||||
struct strbuf *symref, unsigned int *flags);
|
||||
struct strbuf *referent, unsigned int *type);
|
||||
|
||||
#endif /* REFS_REFS_INTERNAL_H */
|
||||
|
|
|
@ -23,7 +23,7 @@ test_expect_success setup '
|
|||
m=refs/heads/master
|
||||
n_dir=refs/heads/gu
|
||||
n=$n_dir/fixes
|
||||
outside=foo
|
||||
outside=refs/foo
|
||||
|
||||
test_expect_success \
|
||||
"create $m" \
|
||||
|
@ -479,7 +479,7 @@ test_expect_success 'stdin fails with duplicate refs' '
|
|||
create $a $m
|
||||
EOF
|
||||
test_must_fail git update-ref --stdin <stdin 2>err &&
|
||||
grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
|
||||
grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err
|
||||
'
|
||||
|
||||
test_expect_success 'stdin create ref works' '
|
||||
|
@ -880,7 +880,7 @@ test_expect_success 'stdin -z fails option with unknown name' '
|
|||
test_expect_success 'stdin -z fails with duplicate refs' '
|
||||
printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
|
||||
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
||||
grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
|
||||
grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err
|
||||
'
|
||||
|
||||
test_expect_success 'stdin -z create ref works' '
|
||||
|
@ -1102,6 +1102,41 @@ test_expect_success 'stdin -z delete refs works with packed and loose refs' '
|
|||
test_must_fail git rev-parse --verify -q $c
|
||||
'
|
||||
|
||||
test_expect_success 'fails with duplicate HEAD update' '
|
||||
git branch target1 $A &&
|
||||
git checkout target1 &&
|
||||
cat >stdin <<-EOF &&
|
||||
update refs/heads/target1 $C
|
||||
option no-deref
|
||||
update HEAD $B
|
||||
EOF
|
||||
test_must_fail git update-ref --stdin <stdin 2>err &&
|
||||
grep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
|
||||
echo "refs/heads/target1" >expect &&
|
||||
git symbolic-ref HEAD >actual &&
|
||||
test_cmp expect actual &&
|
||||
echo "$A" >expect &&
|
||||
git rev-parse refs/heads/target1 >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'fails with duplicate ref update via symref' '
|
||||
git branch target2 $A &&
|
||||
git symbolic-ref refs/heads/symref2 refs/heads/target2 &&
|
||||
cat >stdin <<-EOF &&
|
||||
update refs/heads/target2 $C
|
||||
update refs/heads/symref2 $B
|
||||
EOF
|
||||
test_must_fail git update-ref --stdin <stdin 2>err &&
|
||||
grep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
|
||||
echo "refs/heads/target2" >expect &&
|
||||
git symbolic-ref refs/heads/symref2 >actual &&
|
||||
test_cmp expect actual &&
|
||||
echo "$A" >expect &&
|
||||
git rev-parse refs/heads/target2 >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
run_with_limited_open_files () {
|
||||
(ulimit -n 32 && "$@")
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ Q="'"
|
|||
test_expect_success 'setup' '
|
||||
|
||||
git commit --allow-empty -m Initial &&
|
||||
C=$(git rev-parse HEAD)
|
||||
C=$(git rev-parse HEAD) &&
|
||||
git commit --allow-empty -m Second &&
|
||||
D=$(git rev-parse HEAD)
|
||||
|
||||
'
|
||||
|
||||
|
@ -104,4 +106,76 @@ test_expect_success 'one new ref is a simple prefix of another' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool rev-parse' '
|
||||
prefix=refs/e-rev-parse &&
|
||||
git update-ref $prefix/foo $C &&
|
||||
git pack-refs --all &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
echo "$C" >expected &&
|
||||
git rev-parse $prefix/foo >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool for-each-ref' '
|
||||
prefix=refs/e-for-each-ref &&
|
||||
git update-ref $prefix/foo $C &&
|
||||
git for-each-ref $prefix >expected &&
|
||||
git pack-refs --all &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
git for-each-ref $prefix >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool create' '
|
||||
prefix=refs/e-create &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
printf "create %s $C\n" $prefix/foo |
|
||||
git update-ref --stdin
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool verify' '
|
||||
prefix=refs/e-verify &&
|
||||
git update-ref $prefix/foo $C &&
|
||||
git pack-refs --all &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
printf "verify %s $C\n" $prefix/foo |
|
||||
git update-ref --stdin
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool 1-arg update' '
|
||||
prefix=refs/e-update-1 &&
|
||||
git update-ref $prefix/foo $C &&
|
||||
git pack-refs --all &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
printf "update %s $D\n" $prefix/foo |
|
||||
git update-ref --stdin
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool 2-arg update' '
|
||||
prefix=refs/e-update-2 &&
|
||||
git update-ref $prefix/foo $C &&
|
||||
git pack-refs --all &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
printf "update %s $D $C\n" $prefix/foo |
|
||||
git update-ref --stdin
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool 0-arg delete' '
|
||||
prefix=refs/e-delete-0 &&
|
||||
git update-ref $prefix/foo $C &&
|
||||
git pack-refs --all &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
printf "delete %s\n" $prefix/foo |
|
||||
git update-ref --stdin
|
||||
'
|
||||
|
||||
test_expect_success 'empty directory should not fool 1-arg delete' '
|
||||
prefix=refs/e-delete-1 &&
|
||||
git update-ref $prefix/foo $C &&
|
||||
git pack-refs --all &&
|
||||
mkdir -p .git/$prefix/foo/bar/baz &&
|
||||
printf "delete %s $C\n" $prefix/foo |
|
||||
git update-ref --stdin
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -285,7 +285,7 @@ test_expect_success 'update-ref -d cannot delete non-ref in .git dir' '
|
|||
echo precious >expect &&
|
||||
test_must_fail git update-ref -d my-private-file >output 2>error &&
|
||||
test_must_be_empty output &&
|
||||
test_i18ngrep -e "cannot lock .*: unable to resolve reference" error &&
|
||||
test_i18ngrep -e "refusing to update ref with bad name" error &&
|
||||
test_cmp expect .git/my-private-file
|
||||
'
|
||||
|
||||
|
|
|
@ -79,6 +79,15 @@ test_expect_success 'git branch -m dumps usage' '
|
|||
test_i18ngrep "branch name required" err
|
||||
'
|
||||
|
||||
test_expect_success 'git branch -m m broken_symref should work' '
|
||||
test_when_finished "git branch -D broken_symref" &&
|
||||
git branch -l m &&
|
||||
git symbolic-ref refs/heads/broken_symref refs/heads/i_am_broken &&
|
||||
git branch -m m broken_symref &&
|
||||
git reflog exists refs/heads/broken_symref &&
|
||||
test_must_fail git reflog exists refs/heads/i_am_broken
|
||||
'
|
||||
|
||||
test_expect_success 'git branch -m m m/m should work' '
|
||||
git branch -l m &&
|
||||
git branch -m m m/m &&
|
||||
|
|
Загрузка…
Ссылка в новой задаче