Browse Source
* sp/reflog: Enable ref log creation in git checkout -b. Create/delete branch ref logs. Include ref log detail in commit, reset, etc. Change order of -m option to update-ref. Correct force_write bug in refs.c Change 'master@noon' syntax to 'master@{noon}'. Log ref updates made by fetch. Force writing ref if it doesn't exist. Added logs/ directory to repository layout. General ref log reading improvements. Fix ref log parsing so it works properly. Support 'master@2 hours ago' syntax Log ref updates to logs/refs/<ref> Convert update-ref to use ref_lock API. Improve abstraction of ref lock/write.maint
Junio C Hamano
19 years ago
25 changed files with 704 additions and 248 deletions
@ -0,0 +1,199 @@
@@ -0,0 +1,199 @@
|
||||
#!/bin/sh |
||||
# |
||||
# Copyright (c) 2006 Shawn Pearce |
||||
# |
||||
|
||||
test_description='Test git-update-ref and basic ref logging' |
||||
. ./test-lib.sh |
||||
|
||||
Z=0000000000000000000000000000000000000000 |
||||
A=1111111111111111111111111111111111111111 |
||||
B=2222222222222222222222222222222222222222 |
||||
C=3333333333333333333333333333333333333333 |
||||
D=4444444444444444444444444444444444444444 |
||||
E=5555555555555555555555555555555555555555 |
||||
F=6666666666666666666666666666666666666666 |
||||
m=refs/heads/master |
||||
|
||||
test_expect_success \ |
||||
"create $m" \ |
||||
'git-update-ref $m $A && |
||||
test $A = $(cat .git/$m)' |
||||
test_expect_success \ |
||||
"create $m" \ |
||||
'git-update-ref $m $B $A && |
||||
test $B = $(cat .git/$m)' |
||||
rm -f .git/$m |
||||
|
||||
test_expect_success \ |
||||
"create $m (by HEAD)" \ |
||||
'git-update-ref HEAD $A && |
||||
test $A = $(cat .git/$m)' |
||||
test_expect_success \ |
||||
"create $m (by HEAD)" \ |
||||
'git-update-ref HEAD $B $A && |
||||
test $B = $(cat .git/$m)' |
||||
rm -f .git/$m |
||||
|
||||
test_expect_failure \ |
||||
'(not) create HEAD with old sha1' \ |
||||
'git-update-ref HEAD $A $B' |
||||
test_expect_failure \ |
||||
"(not) prior created .git/$m" \ |
||||
'test -f .git/$m' |
||||
rm -f .git/$m |
||||
|
||||
test_expect_success \ |
||||
"create HEAD" \ |
||||
'git-update-ref HEAD $A' |
||||
test_expect_failure \ |
||||
'(not) change HEAD with wrong SHA1' \ |
||||
'git-update-ref HEAD $B $Z' |
||||
test_expect_failure \ |
||||
"(not) changed .git/$m" \ |
||||
'test $B = $(cat .git/$m)' |
||||
rm -f .git/$m |
||||
|
||||
mkdir -p .git/logs/refs/heads |
||||
touch .git/logs/refs/heads/master |
||||
test_expect_success \ |
||||
"create $m (logged by touch)" \ |
||||
'GIT_COMMITTER_DATE="2005-05-26 23:30" \ |
||||
git-update-ref HEAD $A -m "Initial Creation" && |
||||
test $A = $(cat .git/$m)' |
||||
test_expect_success \ |
||||
"update $m (logged by touch)" \ |
||||
'GIT_COMMITTER_DATE="2005-05-26 23:31" \ |
||||
git-update-ref HEAD $B $A -m "Switch" && |
||||
test $B = $(cat .git/$m)' |
||||
test_expect_success \ |
||||
"set $m (logged by touch)" \ |
||||
'GIT_COMMITTER_DATE="2005-05-26 23:41" \ |
||||
git-update-ref HEAD $A && |
||||
test $A = $(cat .git/$m)' |
||||
|
||||
cat >expect <<EOF |
||||
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation |
||||
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch |
||||
$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 |
||||
EOF |
||||
test_expect_success \ |
||||
"verifying $m's log" \ |
||||
'diff expect .git/logs/$m' |
||||
rm -rf .git/$m .git/logs expect |
||||
|
||||
test_expect_success \ |
||||
'enable core.logAllRefUpdates' \ |
||||
'git-repo-config core.logAllRefUpdates true && |
||||
test true = $(git-repo-config --bool --get core.logAllRefUpdates)' |
||||
|
||||
test_expect_success \ |
||||
"create $m (logged by config)" \ |
||||
'GIT_COMMITTER_DATE="2005-05-26 23:32" \ |
||||
git-update-ref HEAD $A -m "Initial Creation" && |
||||
test $A = $(cat .git/$m)' |
||||
test_expect_success \ |
||||
"update $m (logged by config)" \ |
||||
'GIT_COMMITTER_DATE="2005-05-26 23:33" \ |
||||
git-update-ref HEAD $B $A -m "Switch" && |
||||
test $B = $(cat .git/$m)' |
||||
test_expect_success \ |
||||
"set $m (logged by config)" \ |
||||
'GIT_COMMITTER_DATE="2005-05-26 23:43" \ |
||||
git-update-ref HEAD $A && |
||||
test $A = $(cat .git/$m)' |
||||
|
||||
cat >expect <<EOF |
||||
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000 Initial Creation |
||||
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch |
||||
$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000 |
||||
EOF |
||||
test_expect_success \ |
||||
"verifying $m's log" \ |
||||
'diff expect .git/logs/$m' |
||||
rm -f .git/$m .git/logs/$m expect |
||||
|
||||
git-update-ref $m $D |
||||
cat >.git/logs/$m <<EOF |
||||
$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500 |
||||
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500 |
||||
$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500 |
||||
$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500 |
||||
EOF |
||||
|
||||
ed="Thu, 26 May 2005 18:32:00 -0500" |
||||
gd="Thu, 26 May 2005 18:33:00 -0500" |
||||
ld="Thu, 26 May 2005 18:43:00 -0500" |
||||
test_expect_success \ |
||||
'Query "master@{May 25 2005}" (before history)' \ |
||||
'rm -f o e |
||||
git-rev-parse --verify "master@{May 25 2005}" >o 2>e && |
||||
test $C = $(cat o) && |
||||
test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"' |
||||
test_expect_success \ |
||||
"Query master@{2005-05-25} (before history)" \ |
||||
'rm -f o e |
||||
git-rev-parse --verify master@{2005-05-25} >o 2>e && |
||||
test $C = $(cat o) && |
||||
echo test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"' |
||||
test_expect_success \ |
||||
'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \ |
||||
'rm -f o e |
||||
git-rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e && |
||||
test $C = $(cat o) && |
||||
test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"' |
||||
test_expect_success \ |
||||
'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \ |
||||
'rm -f o e |
||||
git-rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e && |
||||
test $A = $(cat o) && |
||||
test "" = "$(cat e)"' |
||||
test_expect_success \ |
||||
'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \ |
||||
'rm -f o e |
||||
git-rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e && |
||||
test $B = $(cat o) && |
||||
test "warning: Log .git/logs/$m has gap after $gd." = "$(cat e)"' |
||||
test_expect_success \ |
||||
'Query "master@{2005-05-26 23:38:00}" (middle of history)' \ |
||||
'rm -f o e |
||||
git-rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e && |
||||
test $Z = $(cat o) && |
||||
test "" = "$(cat e)"' |
||||
test_expect_success \ |
||||
'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \ |
||||
'rm -f o e |
||||
git-rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e && |
||||
test $E = $(cat o) && |
||||
test "" = "$(cat e)"' |
||||
test_expect_success \ |
||||
'Query "master@{2005-05-28}" (past end of history)' \ |
||||
'rm -f o e |
||||
git-rev-parse --verify "master@{2005-05-28}" >o 2>e && |
||||
test $D = $(cat o) && |
||||
test "warning: Log .git/logs/$m unexpectedly ended on $ld." = "$(cat e)"' |
||||
|
||||
|
||||
rm -f .git/$m .git/logs/$m expect |
||||
|
||||
test_expect_success \ |
||||
'creating initial files' \ |
||||
'cp ../../COPYING COPYING && |
||||
git-add COPYING && |
||||
GIT_COMMITTER_DATE="2005-05-26 23:30" git-commit -m add -a && |
||||
cp ../../Makefile COPYING && |
||||
GIT_COMMITTER_DATE="2005-05-26 23:41" git-commit -m change -a' |
||||
|
||||
test_expect_success \ |
||||
'git-cat-file blob master:COPYING (expect Makefile)' \ |
||||
'git-cat-file blob master:COPYING | diff - ../../Makefile' |
||||
test_expect_success \ |
||||
'git-cat-file blob master@{2005-05-26 23:30}:COPYING (expect COPYING)' \ |
||||
'git-cat-file blob "master@{2005-05-26 23:30}:COPYING" \ |
||||
| diff - ../../COPYING' |
||||
test_expect_success \ |
||||
'git-cat-file blob master@{2005-05-26 23:42}:COPYING (expect Makefile)' \ |
||||
'git-cat-file blob "master@{2005-05-26 23:42}:COPYING" \ |
||||
| diff - ../../Makefile' |
||||
|
||||
test_done |
@ -1,85 +1,56 @@
@@ -1,85 +1,56 @@
|
||||
#include "cache.h" |
||||
#include "refs.h" |
||||
|
||||
static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]"; |
||||
|
||||
static int re_verify(const char *path, unsigned char *oldsha1, unsigned char *currsha1) |
||||
{ |
||||
char buf[40]; |
||||
int fd = open(path, O_RDONLY), nr; |
||||
if (fd < 0) |
||||
return -1; |
||||
nr = read(fd, buf, 40); |
||||
close(fd); |
||||
if (nr != 40 || get_sha1_hex(buf, currsha1) < 0) |
||||
return -1; |
||||
return memcmp(oldsha1, currsha1, 20) ? -1 : 0; |
||||
} |
||||
static const char git_update_ref_usage[] = |
||||
"git-update-ref <refname> <value> [<oldval>] [-m <reason>]"; |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
char *hex; |
||||
const char *refname, *value, *oldval, *path; |
||||
char *lockpath; |
||||
unsigned char sha1[20], oldsha1[20], currsha1[20]; |
||||
int fd, written; |
||||
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; |
||||
struct ref_lock *lock; |
||||
unsigned char sha1[20], oldsha1[20]; |
||||
int i; |
||||
|
||||
setup_git_directory(); |
||||
git_config(git_default_config); |
||||
if (argc < 3 || argc > 4) |
||||
|
||||
for (i = 1; i < argc; i++) { |
||||
if (!strcmp("-m", argv[i])) { |
||||
if (i+1 >= argc) |
||||
usage(git_update_ref_usage); |
||||
msg = argv[++i]; |
||||
if (!*msg) |
||||
die("Refusing to perform update with empty message."); |
||||
if (strchr(msg, '\n')) |
||||
die("Refusing to perform update with \\n in message."); |
||||
continue; |
||||
} |
||||
if (!refname) { |
||||
refname = argv[i]; |
||||
continue; |
||||
} |
||||
if (!value) { |
||||
value = argv[i]; |
||||
continue; |
||||
} |
||||
if (!oldval) { |
||||
oldval = argv[i]; |
||||
continue; |
||||
} |
||||
} |
||||
if (!refname || !value) |
||||
usage(git_update_ref_usage); |
||||
|
||||
refname = argv[1]; |
||||
value = argv[2]; |
||||
oldval = argv[3]; |
||||
if (get_sha1(value, sha1)) |
||||
die("%s: not a valid SHA1", value); |
||||
memset(oldsha1, 0, 20); |
||||
if (oldval && get_sha1(oldval, oldsha1)) |
||||
die("%s: not a valid old SHA1", oldval); |
||||
|
||||
path = resolve_ref(git_path("%s", refname), currsha1, !!oldval); |
||||
if (!path) |
||||
die("No such ref: %s", refname); |
||||
|
||||
if (oldval) { |
||||
if (memcmp(currsha1, oldsha1, 20)) |
||||
die("Ref %s is at %s but expected %s", refname, sha1_to_hex(currsha1), sha1_to_hex(oldsha1)); |
||||
/* Nothing to do? */ |
||||
if (!memcmp(oldsha1, sha1, 20)) |
||||
exit(0); |
||||
} |
||||
path = strdup(path); |
||||
lockpath = mkpath("%s.lock", path); |
||||
if (safe_create_leading_directories(lockpath) < 0) |
||||
die("Unable to create all of %s", lockpath); |
||||
|
||||
fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); |
||||
if (fd < 0) |
||||
die("Unable to create %s", lockpath); |
||||
hex = sha1_to_hex(sha1); |
||||
hex[40] = '\n'; |
||||
written = write(fd, hex, 41); |
||||
close(fd); |
||||
if (written != 41) { |
||||
unlink(lockpath); |
||||
die("Unable to write to %s", lockpath); |
||||
} |
||||
|
||||
/* |
||||
* Re-read the ref after getting the lock to verify |
||||
*/ |
||||
if (oldval && re_verify(path, oldsha1, currsha1) < 0) { |
||||
unlink(lockpath); |
||||
die("Ref lock failed"); |
||||
} |
||||
|
||||
/* |
||||
* Finally, replace the old ref with the new one |
||||
*/ |
||||
if (rename(lockpath, path) < 0) { |
||||
unlink(lockpath); |
||||
die("Unable to create %s", path); |
||||
} |
||||
lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0); |
||||
if (!lock) |
||||
return 1; |
||||
if (write_ref_sha1(lock, sha1, msg) < 0) |
||||
return 1; |
||||
return 0; |
||||
} |
||||
|
Loading…
Reference in new issue