Log ref updates to logs/refs/<ref>
If config parameter core.logAllRefUpdates is true or the log file already exists then append a line to ".git/logs/refs/<ref>" whenever git-update-ref <ref> is executed. Each log line contains the following information: oldsha1 <SP> newsha1 <SP> committer <LF> where committer is the current user, date, time and timezone in the standard GIT ident format. If the caller is unable to append to the log file then git-update-ref will fail without updating <ref>. An optional message may be included in the log line with the -m flag. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
5b16b09021
commit
6de08ae688
@ -70,6 +70,14 @@ core.preferSymlinkRefs::
|
||||
This is sometimes needed to work with old scripts that
|
||||
expect HEAD to be a symbolic link.
|
||||
|
||||
core.logAllRefUpdates::
|
||||
If true, `git-update-ref` will append a line to
|
||||
"$GIT_DIR/logs/<ref>" listing the new SHA1 and the date/time
|
||||
of the update. If the file does not exist it will be
|
||||
created automatically. This information can be used to
|
||||
determine what commit was the tip of a branch "2 days ago".
|
||||
This value is false by default (no logging).
|
||||
|
||||
core.repositoryFormatVersion::
|
||||
Internal variable identifying the repository format and layout
|
||||
version.
|
||||
|
@ -49,6 +49,32 @@ 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).
|
||||
|
||||
Logging Updates
|
||||
---------------
|
||||
If config parameter "core.logAllRefUpdates" is true or the file
|
||||
"$GIT_DIR/logs/<ref>" exists then `git-update-ref` will append
|
||||
a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
|
||||
symbolic refs before creating the log name) describing the change
|
||||
in ref value. Log lines are formatted as:
|
||||
|
||||
. oldsha1 SP newsha1 SP committer LF
|
||||
+
|
||||
Where "oldsha1" is the 40 character hexadecimal value previously
|
||||
stored in <ref>, "newsha1" is the 40 character hexadecimal value of
|
||||
<newvalue> and "committer" is the committer's name, email address
|
||||
and date in the standard GIT committer ident format.
|
||||
|
||||
Optionally with -m:
|
||||
|
||||
. oldsha1 SP newsha1 SP committer TAB message LF
|
||||
+
|
||||
Where all fields are as described above and "message" is the
|
||||
value supplied to the -m option.
|
||||
|
||||
An update will fail (without changing <ref>) if the current user is
|
||||
unable to create a new log file, append to the existing log file
|
||||
or does not have committer information available.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>.
|
||||
|
1
cache.h
1
cache.h
@ -170,6 +170,7 @@ extern void rollback_index_file(struct cache_file *);
|
||||
extern int trust_executable_bit;
|
||||
extern int assume_unchanged;
|
||||
extern int prefer_symlink_refs;
|
||||
extern int log_all_ref_updates;
|
||||
extern int warn_ambiguous_refs;
|
||||
extern int diff_rename_limit_default;
|
||||
extern int shared_repository;
|
||||
|
5
config.c
5
config.c
@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.logallrefupdates")) {
|
||||
log_all_ref_updates = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.warnambiguousrefs")) {
|
||||
warn_ambiguous_refs = git_config_bool(var, value);
|
||||
return 0;
|
||||
|
@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME];
|
||||
int trust_executable_bit = 1;
|
||||
int assume_unchanged = 0;
|
||||
int prefer_symlink_refs = 0;
|
||||
int log_all_ref_updates = 0;
|
||||
int warn_ambiguous_refs = 1;
|
||||
int repository_format_version = 0;
|
||||
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
|
||||
|
56
refs.c
56
refs.c
@ -302,6 +302,7 @@ static struct ref_lock* lock_ref_sha1_basic(const char *path,
|
||||
|
||||
lock->ref_file = strdup(path);
|
||||
lock->lock_file = strdup(mkpath("%s.lock", lock->ref_file));
|
||||
lock->log_file = strdup(git_path("logs/%s", lock->ref_file + plen));
|
||||
|
||||
if (safe_create_leading_directories(lock->lock_file))
|
||||
die("unable to create directory for %s", lock->lock_file);
|
||||
@ -343,9 +344,60 @@ void unlock_ref (struct ref_lock *lock)
|
||||
free(lock->ref_file);
|
||||
if (lock->lock_file)
|
||||
free(lock->lock_file);
|
||||
if (lock->log_file)
|
||||
free(lock->log_file);
|
||||
free(lock);
|
||||
}
|
||||
|
||||
static int log_ref_write(struct ref_lock *lock,
|
||||
const unsigned char *sha1, const char *msg)
|
||||
{
|
||||
int logfd, written, oflags = O_APPEND | O_WRONLY;
|
||||
unsigned maxlen, len;
|
||||
char *logrec;
|
||||
const char *comitter;
|
||||
|
||||
if (log_all_ref_updates) {
|
||||
if (safe_create_leading_directories(lock->log_file) < 0)
|
||||
return error("unable to create directory for %s",
|
||||
lock->log_file);
|
||||
oflags |= O_CREAT;
|
||||
}
|
||||
|
||||
logfd = open(lock->log_file, oflags, 0666);
|
||||
if (logfd < 0) {
|
||||
if (!log_all_ref_updates && errno == ENOENT)
|
||||
return 0;
|
||||
return error("Unable to append to %s: %s",
|
||||
lock->log_file, strerror(errno));
|
||||
}
|
||||
|
||||
setup_ident();
|
||||
comitter = git_committer_info(1);
|
||||
if (msg) {
|
||||
maxlen = strlen(comitter) + strlen(msg) + 2*40 + 5;
|
||||
logrec = xmalloc(maxlen);
|
||||
len = snprintf(logrec, maxlen, "%s %s %s\t%s\n",
|
||||
sha1_to_hex(lock->old_sha1),
|
||||
sha1_to_hex(sha1),
|
||||
comitter,
|
||||
msg);
|
||||
} else {
|
||||
maxlen = strlen(comitter) + 2*40 + 4;
|
||||
logrec = xmalloc(maxlen);
|
||||
len = snprintf(logrec, maxlen, "%s %s %s\n",
|
||||
sha1_to_hex(lock->old_sha1),
|
||||
sha1_to_hex(sha1),
|
||||
comitter);
|
||||
}
|
||||
written = len <= maxlen ? write(logfd, logrec, len) : -1;
|
||||
free(logrec);
|
||||
close(logfd);
|
||||
if (written != len)
|
||||
return error("Unable to append to %s", lock->log_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_ref_sha1(struct ref_lock *lock,
|
||||
const unsigned char *sha1, const char *logmsg)
|
||||
{
|
||||
@ -364,6 +416,10 @@ int write_ref_sha1(struct ref_lock *lock,
|
||||
unlock_ref(lock);
|
||||
return -1;
|
||||
}
|
||||
if (log_ref_write(lock, sha1, logmsg) < 0) {
|
||||
unlock_ref(lock);
|
||||
return -1;
|
||||
}
|
||||
if (rename(lock->lock_file, lock->ref_file) < 0) {
|
||||
error("Couldn't set %s", lock->ref_file);
|
||||
unlock_ref(lock);
|
||||
|
1
refs.h
1
refs.h
@ -4,6 +4,7 @@
|
||||
struct ref_lock {
|
||||
char *ref_file;
|
||||
char *lock_file;
|
||||
char *log_file;
|
||||
unsigned char old_sha1[20];
|
||||
int lock_fd;
|
||||
};
|
||||
|
112
t/t1400-update-ref.sh
Normal file
112
t/t1400-update-ref.sh
Normal file
@ -0,0 +1,112 @@
|
||||
#!/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
|
||||
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
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user