Merge branch 'jc/ref-locking' into next

* jc/ref-locking:
  Teach receive-pack about ref-log
  update a few Porcelain-ish for ref lock safety.
  update-ref: -d flag and ref creation safety.
  Clean-up lock-ref implementation
This commit is contained in:
Junio C Hamano
2006-09-27 02:49:05 -07:00
11 changed files with 89 additions and 99 deletions

View File

@@ -7,7 +7,7 @@ git-update-ref - update the object name stored in a ref safely
SYNOPSIS
--------
'git-update-ref' [-m <reason>] <ref> <newvalue> [<oldvalue>]
'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>])
DESCRIPTION
-----------
@@ -20,7 +20,9 @@ possibly dereferencing the symbolic refs, after verifying that
the current value of the <ref> matches <oldvalue>.
E.g. `git-update-ref refs/heads/master <newvalue> <oldvalue>`
updates the master branch head to <newvalue> only if its current
value is <oldvalue>.
value is <oldvalue>. You can specify 40 "0" or an empty string
as <oldvalue> to make sure that the ref you are creating does
not exist.
It also allows a "ref" file to be a symbolic pointer to another
ref file by starting with the four-byte header sequence of
@@ -49,6 +51,10 @@ for reading but not for writing (so we'll never write through a
ref symlink to some other tree, if you have copied a whole
archive by creating a symlink tree).
With `-d` flag, it deletes the named <ref> after verifying it
still contains <oldvalue>.
Logging Updates
---------------
If config parameter "core.logAllRefUpdates" is true or the file

View File

@@ -53,7 +53,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
/* make sure nobody touched the ref, and unlink */
static void prune_ref(struct ref_to_prune *r)
{
struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1);
struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
if (lock) {
unlink(git_path("%s", r->name));

View File

@@ -3,15 +3,16 @@
#include "builtin.h"
static const char git_update_ref_usage[] =
"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])";
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
struct ref_lock *lock;
unsigned char sha1[20], oldsha1[20];
int i;
int i, delete;
delete = 0;
setup_ident();
git_config(git_default_config);
@@ -26,6 +27,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("Refusing to perform update with \\n in message.");
continue;
}
if (!strcmp("-d", argv[i])) {
delete = 1;
continue;
}
if (!refname) {
refname = argv[i];
continue;
@@ -44,11 +49,18 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (get_sha1(value, sha1))
die("%s: not a valid SHA1", value);
if (delete) {
if (oldval)
usage(git_update_ref_usage);
return delete_ref(refname, sha1);
}
hashclr(oldsha1);
if (oldval && get_sha1(oldval, oldsha1))
if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0);
lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);
if (!lock)
return 1;
if (write_ref_sha1(lock, sha1, msg) < 0)

View File

@@ -179,6 +179,7 @@ struct lock_file {
extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
extern int commit_lock_file(struct lock_file *);
extern void rollback_lock_file(struct lock_file *);
extern int delete_ref(const char *, unsigned char *sha1);
/* Environment bits from configuration mechanism */
extern int use_legacy_headers;

View File

@@ -266,7 +266,7 @@ int pull(int targets, char **target, const char **write_ref,
if (!write_ref || !write_ref[i])
continue;
lock[i] = lock_ref_sha1(write_ref[i], NULL, 0);
lock[i] = lock_ref_sha1(write_ref[i], NULL);
if (!lock[i]) {
error("Can't lock ref %s", write_ref[i]);
goto unlock_and_fail;

View File

@@ -42,8 +42,7 @@ If you are sure you want to delete it, run 'git branch -D $branch_name'."
esac
;;
esac
rm -f "$GIT_DIR/logs/refs/heads/$branch_name"
rm -f "$GIT_DIR/refs/heads/$branch_name"
git update-ref -d "refs/heads/$branch_name" "$branch"
echo "Deleted branch $branch_name."
done
exit 0
@@ -122,6 +121,7 @@ then
done
fi
prev=''
if git-show-ref --verify --quiet -- "refs/heads/$branchname"
then
if test '' = "$force"
@@ -131,10 +131,11 @@ then
then
die "cannot force-update the current branch."
fi
prev=`git rev-parse --verify "refs/heads/$branchname"`
fi
if test "$create_log" = 'yes'
then
mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$branchname")
touch "$GIT_DIR/logs/refs/heads/$branchname"
fi
git update-ref -m "branch: Created from $head" "refs/heads/$branchname" $rev
git update-ref -m "branch: Created from $head" "refs/heads/$branchname" "$rev" "$prev"

View File

@@ -547,15 +547,15 @@ then
PARENTS=$(git-cat-file commit HEAD |
sed -n -e '/^$/q' -e 's/^parent /-p /p')
fi
current=$(git-rev-parse --verify HEAD)
current="$(git-rev-parse --verify HEAD)"
else
if [ -z "$(git-ls-files)" ]; then
echo >&2 Nothing to commit
exit 1
fi
PARENTS=""
current=
rloga='commit (initial)'
current=''
fi
if test -z "$no_edit"
@@ -631,7 +631,7 @@ then
fi &&
commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
git-update-ref -m "$rloga: $rlogm" HEAD $commit $current &&
git-update-ref -m "$rloga: $rlogm" HEAD $commit "$current" &&
rm -f -- "$GIT_DIR/MERGE_HEAD" &&
if test -f "$NEXT_INDEX"
then

View File

@@ -63,8 +63,11 @@ done
name="$1"
[ "$name" ] || usage
if [ -e "$GIT_DIR/refs/tags/$name" -a -z "$force" ]; then
die "tag '$name' already exists"
prev=0000000000000000000000000000000000000000
if test -e "$GIT_DIR/refs/tags/$name"
then
test -n "$force" || die "tag '$name' already exists"
prev=`git rev-parse "refs/tags/$name"`
fi
shift
git-check-ref-format "tags/$name" ||
@@ -109,4 +112,4 @@ fi
leading=`expr "refs/tags/$name" : '\(.*\)/'` &&
mkdir -p "$GIT_DIR/$leading" &&
echo $object > "$GIT_DIR/refs/tags/$name"
GIT_DIR="$GIT_DIR" git update-ref "refs/tags/$name" "$object" "$prev"

View File

@@ -43,34 +43,6 @@ struct command {
static struct command *commands;
static int is_all_zeroes(const char *hex)
{
int i;
for (i = 0; i < 40; i++)
if (*hex++ != '0')
return 0;
return 1;
}
static int verify_old_ref(const char *name, char *hex_contents)
{
int fd, ret;
char buffer[60];
if (is_all_zeroes(hex_contents))
return 0;
fd = open(name, O_RDONLY);
if (fd < 0)
return -1;
ret = read(fd, buffer, 40);
close(fd);
if (ret != 40)
return -1;
if (memcmp(buffer, hex_contents, 40))
return -1;
return 0;
}
static char update_hook[] = "hooks/update";
static int run_update_hook(const char *refname,
@@ -107,8 +79,8 @@ static int update(struct command *cmd)
const char *name = cmd->ref_name;
unsigned char *old_sha1 = cmd->old_sha1;
unsigned char *new_sha1 = cmd->new_sha1;
char new_hex[60], *old_hex, *lock_name;
int newfd, namelen, written;
char new_hex[41], old_hex[41];
struct ref_lock *lock;
cmd->error_string = NULL;
if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) {
@@ -117,13 +89,8 @@ static int update(struct command *cmd)
name);
}
namelen = strlen(name);
lock_name = xmalloc(namelen + 10);
memcpy(lock_name, name, namelen);
memcpy(lock_name + namelen, ".lock", 6);
strcpy(new_hex, sha1_to_hex(new_sha1));
old_hex = sha1_to_hex(old_sha1);
strcpy(old_hex, sha1_to_hex(old_sha1));
if (!has_sha1_file(new_sha1)) {
cmd->error_string = "bad pack";
return error("unpack should have generated %s, "
@@ -144,47 +111,20 @@ static int update(struct command *cmd)
return error("denying non-fast forward;"
" you should pull first");
}
safe_create_leading_directories(lock_name);
newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (newfd < 0) {
cmd->error_string = "can't lock";
return error("unable to create %s (%s)",
lock_name, strerror(errno));
}
/* Write the ref with an ending '\n' */
new_hex[40] = '\n';
new_hex[41] = 0;
written = write(newfd, new_hex, 41);
/* Remove the '\n' again */
new_hex[40] = 0;
close(newfd);
if (written != 41) {
unlink(lock_name);
cmd->error_string = "can't write";
return error("unable to write %s", lock_name);
}
if (verify_old_ref(name, old_hex) < 0) {
unlink(lock_name);
cmd->error_string = "raced";
return error("%s changed during push", name);
}
if (run_update_hook(name, old_hex, new_hex)) {
unlink(lock_name);
cmd->error_string = "hook declined";
return error("hook declined to update %s", name);
}
else if (rename(lock_name, name) < 0) {
unlink(lock_name);
cmd->error_string = "can't rename";
return error("unable to replace %s", name);
}
else {
fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
return 0;
lock = lock_any_ref_for_update(name, old_sha1);
if (!lock) {
cmd->error_string = "failed to lock";
return error("failed to lock %s", name);
}
write_ref_sha1(lock, new_sha1, "push");
fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
return 0;
}
static char update_post_hook[] = "hooks/post-update";
@@ -335,9 +275,11 @@ int main(int argc, char **argv)
if (!dir)
usage(receive_pack_usage);
if(!enter_repo(dir, 0))
if (!enter_repo(dir, 0))
die("'%s': unable to chdir or not a git archive", dir);
git_config(git_default_config);
write_head_info();
/* EOF */

39
refs.c
View File

@@ -378,6 +378,32 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
return read_ref(mkpath("refs/%s", ref), sha1);
}
int delete_ref(const char *refname, unsigned char *sha1)
{
struct ref_lock *lock;
int err, i, ret = 0;
lock = lock_any_ref_for_update(refname, sha1);
if (!lock)
return 1;
i = strlen(lock->lk->filename) - 5; /* .lock */
lock->lk->filename[i] = 0;
err = unlink(lock->lk->filename);
if (err) {
ret = 1;
error("unlink(%s) failed: %s",
lock->lk->filename, strerror(errno));
}
lock->lk->filename[i] = '.';
err = unlink(lock->log_file);
if (err && errno != ENOENT)
fprintf(stderr, "warning: unlink(%s) failed: %s",
lock->log_file, strerror(errno));
return ret;
}
/*
* Make sure "ref" is something reasonable to have under ".git/refs/";
* We do not like it if:
@@ -447,12 +473,13 @@ static struct ref_lock *verify_lock(struct ref_lock *lock,
return lock;
}
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int mustexist)
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)
{
char *ref_file;
const char *orig_ref = ref;
struct ref_lock *lock;
struct stat st;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
@@ -480,20 +507,18 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
}
struct ref_lock *lock_ref_sha1(const char *ref,
const unsigned char *old_sha1, int mustexist)
struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
{
char refpath[PATH_MAX];
if (check_ref_format(ref))
return NULL;
strcpy(refpath, mkpath("refs/%s", ref));
return lock_ref_sha1_basic(refpath, old_sha1, mustexist);
return lock_ref_sha1_basic(refpath, old_sha1);
}
struct ref_lock *lock_any_ref_for_update(const char *ref,
const unsigned char *old_sha1, int mustexist)
struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
{
return lock_ref_sha1_basic(ref, old_sha1, mustexist);
return lock_ref_sha1_basic(ref, old_sha1);
}
void unlock_ref(struct ref_lock *lock)

4
refs.h
View File

@@ -27,10 +27,10 @@ extern int for_each_remote_ref(each_ref_fn, void *);
extern int get_ref_sha1(const char *ref, unsigned char *sha1);
/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist);
extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
/** Locks any ref (for 'HEAD' type refs). */
extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist);
extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1);
/** Release any lock taken but not written. **/
extern void unlock_ref(struct ref_lock *lock);