提交 e8b4029f 编写于 作者: J Junio C Hamano

Merge branch 'jc/fsck-reflog'

* jc/fsck-reflog:
  Add git-reflog to .gitignore
  reflog expire: do not punt on tags that point at non commits.
  reflog expire: prune commits that are not incomplete
  Don't crash during repack of a reflog with pruned commits.
  git reflog expire
  Move in_merge_bases() to commit.c
  reflog: fix warning message.
  Teach git-repack to preserve objects referred to by reflog entries.
  Protect commits recorded in reflog from pruning.
  add for_each_reflog_ent() iterator
......@@ -89,6 +89,7 @@ git-quiltimport
git-read-tree
git-rebase
git-receive-pack
git-reflog
git-relink
git-repack
git-repo-config
......
......@@ -287,6 +287,7 @@ BUILTIN_OBJS = \
builtin-prune-packed.o \
builtin-push.o \
builtin-read-tree.o \
builtin-reflog.o \
builtin-repo-config.o \
builtin-rerere.o \
builtin-rev-list.o \
......
......@@ -74,25 +74,6 @@ const char *branch_get_color(enum color_branch ix)
return "";
}
static int in_merge_bases(const unsigned char *sha1,
struct commit *rev1,
struct commit *rev2)
{
struct commit_list *bases, *b;
int ret = 0;
bases = get_merge_bases(rev1, rev2, 1);
for (b = bases; b; b = b->next) {
if (!hashcmp(sha1, b->item->object.sha1)) {
ret = 1;
break;
}
}
free_commit_list(bases);
return ret;
}
static int delete_branches(int argc, const char **argv, int force, int kinds)
{
struct commit *rev, *head_rev = head_rev;
......@@ -153,7 +134,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
*/
if (!force &&
!in_merge_bases(sha1, rev, head_rev)) {
!in_merge_bases(rev, head_rev)) {
error("The branch '%s' is not a strict subset of "
"your current HEAD.\n"
"If you are sure you want to delete it, "
......
......@@ -17,7 +17,7 @@ static const char pack_usage[] = "\
git-pack-objects [{ -q | --progress | --all-progress }] \n\
[--local] [--incremental] [--window=N] [--depth=N] \n\
[--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\
[--revs [--unpacked | --all]*] [--stdout | base-name] \n\
[--revs [--unpacked | --all]*] [--reflog] [--stdout | base-name] \n\
[<ref-list | <object-list]";
struct object_entry {
......@@ -1575,6 +1575,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
if (!strcmp("--unpacked", arg) ||
!strncmp("--unpacked=", arg, 11) ||
!strcmp("--reflog", arg) ||
!strcmp("--all", arg)) {
use_internal_rev_list = 1;
if (ARRAY_SIZE(rp_av) - 1 <= rp_ac)
......
......@@ -181,12 +181,28 @@ static void walk_commit_list(struct rev_info *revs)
}
}
static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data)
{
struct object *object;
object = parse_object(osha1);
if (object)
add_pending_object(&revs, object, "");
object = parse_object(nsha1);
if (object)
add_pending_object(&revs, object, "");
return 0;
}
static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = parse_object(sha1);
if (!object)
die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
add_pending_object(&revs, object, "");
for_each_reflog_ent(path, add_one_reflog_ent, NULL);
return 0;
}
......
#include "cache.h"
#include "builtin.h"
#include "commit.h"
#include "refs.h"
#include "dir.h"
#include "tree-walk.h"
struct expire_reflog_cb {
FILE *newlog;
const char *ref;
struct commit *ref_commit;
unsigned long expire_total;
unsigned long expire_unreachable;
};
static int tree_is_complete(const unsigned char *sha1)
{
struct tree_desc desc;
void *buf;
char type[20];
buf = read_sha1_file(sha1, type, &desc.size);
if (!buf)
return 0;
desc.buf = buf;
while (desc.size) {
const unsigned char *elem;
const char *name;
unsigned mode;
elem = tree_entry_extract(&desc, &name, &mode);
if (!has_sha1_file(elem) ||
(S_ISDIR(mode) && !tree_is_complete(elem))) {
free(buf);
return 0;
}
update_tree_entry(&desc);
}
free(buf);
return 1;
}
static int keep_entry(struct commit **it, unsigned char *sha1)
{
struct commit *commit;
*it = NULL;
if (is_null_sha1(sha1))
return 1;
commit = lookup_commit_reference_gently(sha1, 1);
if (!commit)
return 0;
/* Make sure everything in this commit exists. */
parse_object(commit->object.sha1);
if (!tree_is_complete(commit->tree->object.sha1))
return 0;
*it = commit;
return 1;
}
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
char *data, void *cb_data)
{
struct expire_reflog_cb *cb = cb_data;
unsigned long timestamp;
char *cp, *ep;
struct commit *old, *new;
cp = strchr(data, '>');
if (!cp || *++cp != ' ')
goto prune;
timestamp = strtoul(cp, &ep, 10);
if (*ep != ' ')
goto prune;
if (timestamp < cb->expire_total)
goto prune;
if (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1))
goto prune;
if ((timestamp < cb->expire_unreachable) &&
(!cb->ref_commit ||
(old && !in_merge_bases(old, cb->ref_commit)) ||
(new && !in_merge_bases(new, cb->ref_commit))))
goto prune;
if (cb->newlog)
fprintf(cb->newlog, "%s %s %s",
sha1_to_hex(osha1), sha1_to_hex(nsha1), data);
return 0;
prune:
if (!cb->newlog)
fprintf(stderr, "would prune %s", data);
return 0;
}
struct cmd_reflog_expire_cb {
int dry_run;
unsigned long expire_total;
unsigned long expire_unreachable;
};
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
{
struct cmd_reflog_expire_cb *cmd = cb_data;
struct expire_reflog_cb cb;
struct ref_lock *lock;
char *newlog_path = NULL;
int status = 0;
if (strncmp(ref, "refs/", 5))
return error("not a ref '%s'", ref);
memset(&cb, 0, sizeof(cb));
/* we take the lock for the ref itself to prevent it from
* getting updated.
*/
lock = lock_ref_sha1(ref + 5, sha1);
if (!lock)
return error("cannot lock ref '%s'", ref);
if (!file_exists(lock->log_file))
goto finish;
if (!cmd->dry_run) {
newlog_path = xstrdup(git_path("logs/%s.lock", ref));
cb.newlog = fopen(newlog_path, "w");
}
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
if (!cb.ref_commit)
fprintf(stderr,
"warning: ref '%s' does not point at a commit\n", ref);
cb.ref = ref;
cb.expire_total = cmd->expire_total;
cb.expire_unreachable = cmd->expire_unreachable;
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
finish:
if (cb.newlog) {
if (fclose(cb.newlog))
status |= error("%s: %s", strerror(errno),
newlog_path);
if (rename(newlog_path, lock->log_file)) {
status |= error("cannot rename %s to %s",
newlog_path, lock->log_file);
unlink(newlog_path);
}
}
free(newlog_path);
unlock_ref(lock);
return status;
}
static const char reflog_expire_usage[] =
"git-reflog expire [--dry-run] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
struct cmd_reflog_expire_cb cb;
unsigned long now = time(NULL);
int i, status, do_all;
save_commit_buffer = 0;
do_all = status = 0;
memset(&cb, 0, sizeof(cb));
cb.expire_total = now - 90 * 24 * 3600;
cb.expire_unreachable = now - 30 * 24 * 3600;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
cb.dry_run = 1;
else if (!strncmp(arg, "--expire=", 9))
cb.expire_total = approxidate(arg + 9);
else if (!strncmp(arg, "--expire-unreachable=", 21))
cb.expire_unreachable = approxidate(arg + 21);
else if (!strcmp(arg, "--all"))
do_all = 1;
else if (!strcmp(arg, "--")) {
i++;
break;
}
else if (arg[0] == '-')
usage(reflog_expire_usage);
else
break;
}
if (do_all)
status |= for_each_ref(expire_reflog, &cb);
while (i < argc) {
const char *ref = argv[i++];
unsigned char sha1[20];
if (!resolve_ref(ref, sha1, 1, NULL)) {
status |= error("%s points nowhere!", ref);
continue;
}
status |= expire_reflog(ref, sha1, 0, &cb);
}
return status;
}
static const char reflog_usage[] =
"git-reflog (expire | ...)";
int cmd_reflog(int argc, const char **argv, const char *prefix)
{
if (argc < 2)
usage(reflog_usage);
else if (!strcmp(argv[1], "expire"))
return cmd_reflog_expire(argc - 1, argv + 1, prefix);
else
usage(reflog_usage);
}
......@@ -51,6 +51,7 @@ extern int cmd_prune(int argc, const char **argv, const char *prefix);
extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
extern int cmd_push(int argc, const char **argv, const char *prefix);
extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
extern int cmd_reflog(int argc, const char **argv, const char *prefix);
extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
extern int cmd_rerere(int argc, const char **argv, const char *prefix);
extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
......
......@@ -1009,3 +1009,20 @@ struct commit_list *get_merge_bases(struct commit *one,
free(rslt);
return result;
}
int in_merge_bases(struct commit *rev1, struct commit *rev2)
{
struct commit_list *bases, *b;
int ret = 0;
bases = get_merge_bases(rev1, rev2, 1);
for (b = bases; b; b = b->next) {
if (!hashcmp(rev1->object.sha1, b->item->object.sha1)) {
ret = 1;
break;
}
}
free_commit_list(bases);
return ret;
}
......@@ -107,4 +107,5 @@ int read_graft_file(const char *graft_file);
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
int in_merge_bases(struct commit *rev1, struct commit *rev2);
#endif /* COMMIT_H */
......@@ -399,6 +399,25 @@ static void fsck_dir(int i, char *path)
static int default_refs;
static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data)
{
struct object *obj;
if (!is_null_sha1(osha1)) {
obj = lookup_object(osha1);
if (obj) {
obj->used = 1;
mark_reachable(obj, REACHABLE);
}
}
obj = lookup_object(nsha1);
if (obj) {
obj->used = 1;
mark_reachable(obj, REACHABLE);
}
return 0;
}
static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *obj;
......@@ -416,6 +435,9 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
default_refs++;
obj->used = 1;
mark_reachable(obj, REACHABLE);
for_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);
return 0;
}
......
......@@ -62,7 +62,7 @@ case ",$all_into_one," in
esac
args="$args $local $quiet $no_reuse_delta$extra"
name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
name=$(git-pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
exit 1
if [ -z "$name" ]; then
echo Nothing new to pack.
......
......@@ -246,6 +246,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
{ "push", cmd_push, RUN_SETUP },
{ "read-tree", cmd_read_tree, RUN_SETUP },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "repo-config", cmd_repo_config },
{ "rerere", cmd_rerere, RUN_SETUP },
{ "rev-list", cmd_rev_list, RUN_SETUP },
......
......@@ -1013,7 +1013,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
{
const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
char *tz_c;
int logfd, tz;
int logfd, tz, reccnt = 0;
struct stat st;
unsigned long date;
unsigned char logged_sha1[20];
......@@ -1031,6 +1031,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
lastrec = NULL;
rec = logend = logdata + st.st_size;
while (logdata < rec) {
reccnt++;
if (logdata < rec && *(rec-1) == '\n')
rec--;
lastgt = NULL;
......@@ -1087,7 +1088,37 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
if (get_sha1_hex(logdata, sha1))
die("Log %s is corrupt.", logfile);
munmap((void*)logdata, st.st_size);
fprintf(stderr, "warning: Log %s only goes back to %s.\n",
logfile, show_rfc2822_date(date, tz));
if (at_time)
fprintf(stderr, "warning: Log %s only goes back to %s.\n",
logfile, show_rfc2822_date(date, tz));
else
fprintf(stderr, "warning: Log %s only has %d entries.\n",
logfile, reccnt);
return 0;
}
void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
{
const char *logfile;
FILE *logfp;
char buf[1024];
logfile = git_path("logs/%s", ref);
logfp = fopen(logfile, "r");
if (!logfp)
return;
while (fgets(buf, sizeof(buf), logfp)) {
unsigned char osha1[20], nsha1[20];
int len;
/* old SP new SP name <email> SP time TAB msg LF */
len = strlen(buf);
if (len < 83 || buf[len-1] != '\n' ||
get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ')
continue; /* corrupt? */
fn(osha1, nsha1, buf+82, cb_data);
}
fclose(logfp);
}
......@@ -44,6 +44,10 @@ extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, cons
/** Reads log for the value of ref during at_time. **/
extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1);
/* iterate over reflog entries */
typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, char *, void *);
void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
/** Returns 0 if target has the right format for a ref. **/
extern int check_ref_format(const char *target);
......
......@@ -464,21 +464,69 @@ static void limit_list(struct rev_info *revs)
revs->commits = newlist;
}
static int all_flags;
static struct rev_info *all_revs;
struct all_refs_cb {
int all_flags;
int warned_bad_reflog;
struct rev_info *all_revs;
const char *name_for_errormsg;
};
static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = get_reference(all_revs, path, sha1, all_flags);
add_pending_object(all_revs, object, "");
struct all_refs_cb *cb = cb_data;
struct object *object = get_reference(cb->all_revs, path, sha1,
cb->all_flags);
add_pending_object(cb->all_revs, object, "");
return 0;
}
static void handle_all(struct rev_info *revs, unsigned flags)
{
all_revs = revs;
all_flags = flags;
for_each_ref(handle_one_ref, NULL);
struct all_refs_cb cb;
cb.all_revs = revs;
cb.all_flags = flags;
for_each_ref(handle_one_ref, &cb);
}
static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
if (!is_null_sha1(sha1)) {
struct object *o = parse_object(sha1);
if (o) {
o->flags |= cb->all_flags;
add_pending_object(cb->all_revs, o, "");
}
else if (!cb->warned_bad_reflog) {
warn("reflog of '%s' references pruned commits",
cb->name_for_errormsg);
cb->warned_bad_reflog = 1;
}
}
}
static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *detail, void *cb_data)
{
handle_one_reflog_commit(osha1, cb_data);
handle_one_reflog_commit(nsha1, cb_data);
return 0;
}
static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
cb->warned_bad_reflog = 0;
cb->name_for_errormsg = path;
for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
return 0;
}
static void handle_reflog(struct rev_info *revs, unsigned flags)
{
struct all_refs_cb cb;
cb.all_revs = revs;
cb.all_flags = flags;
for_each_ref(handle_one_reflog, &cb);
}
static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
......@@ -810,6 +858,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
handle_all(revs, flags);
continue;
}
if (!strcmp(arg, "--reflog")) {
handle_reflog(revs, flags);
continue;
}
if (!strcmp(arg, "--not")) {
flags ^= UNINTERESTING;
continue;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册