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
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -89,6 +89,7 @@ git-quiltimport
|
|||||||
git-read-tree
|
git-read-tree
|
||||||
git-rebase
|
git-rebase
|
||||||
git-receive-pack
|
git-receive-pack
|
||||||
|
git-reflog
|
||||||
git-relink
|
git-relink
|
||||||
git-repack
|
git-repack
|
||||||
git-repo-config
|
git-repo-config
|
||||||
|
1
Makefile
1
Makefile
@ -287,6 +287,7 @@ BUILTIN_OBJS = \
|
|||||||
builtin-prune-packed.o \
|
builtin-prune-packed.o \
|
||||||
builtin-push.o \
|
builtin-push.o \
|
||||||
builtin-read-tree.o \
|
builtin-read-tree.o \
|
||||||
|
builtin-reflog.o \
|
||||||
builtin-repo-config.o \
|
builtin-repo-config.o \
|
||||||
builtin-rerere.o \
|
builtin-rerere.o \
|
||||||
builtin-rev-list.o \
|
builtin-rev-list.o \
|
||||||
|
@ -74,25 +74,6 @@ const char *branch_get_color(enum color_branch ix)
|
|||||||
return "";
|
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)
|
static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||||
{
|
{
|
||||||
struct commit *rev, *head_rev = head_rev;
|
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 &&
|
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 "
|
error("The branch '%s' is not a strict subset of "
|
||||||
"your current HEAD.\n"
|
"your current HEAD.\n"
|
||||||
"If you are sure you want to delete it, "
|
"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\
|
git-pack-objects [{ -q | --progress | --all-progress }] \n\
|
||||||
[--local] [--incremental] [--window=N] [--depth=N] \n\
|
[--local] [--incremental] [--window=N] [--depth=N] \n\
|
||||||
[--no-reuse-delta] [--delta-base-offset] [--non-empty] \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]";
|
[<ref-list | <object-list]";
|
||||||
|
|
||||||
struct object_entry {
|
struct object_entry {
|
||||||
@ -1575,6 +1575,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
if (!strcmp("--unpacked", arg) ||
|
if (!strcmp("--unpacked", arg) ||
|
||||||
!strncmp("--unpacked=", arg, 11) ||
|
!strncmp("--unpacked=", arg, 11) ||
|
||||||
|
!strcmp("--reflog", arg) ||
|
||||||
!strcmp("--all", arg)) {
|
!strcmp("--all", arg)) {
|
||||||
use_internal_rev_list = 1;
|
use_internal_rev_list = 1;
|
||||||
if (ARRAY_SIZE(rp_av) - 1 <= rp_ac)
|
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)
|
static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
|
||||||
{
|
{
|
||||||
struct object *object = parse_object(sha1);
|
struct object *object = parse_object(sha1);
|
||||||
if (!object)
|
if (!object)
|
||||||
die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
|
die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
|
||||||
add_pending_object(&revs, object, "");
|
add_pending_object(&revs, object, "");
|
||||||
|
|
||||||
|
for_each_reflog_ent(path, add_one_reflog_ent, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
212
builtin-reflog.c
Normal file
212
builtin-reflog.c
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
#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_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_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_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_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_rerere(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
|
extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
|
||||||
|
17
commit.c
17
commit.c
@ -1009,3 +1009,20 @@ struct commit_list *get_merge_bases(struct commit *one,
|
|||||||
free(rslt);
|
free(rslt);
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
1
commit.h
1
commit.h
@ -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);
|
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 */
|
#endif /* COMMIT_H */
|
||||||
|
@ -399,6 +399,25 @@ static void fsck_dir(int i, char *path)
|
|||||||
|
|
||||||
static int default_refs;
|
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)
|
static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||||
{
|
{
|
||||||
struct object *obj;
|
struct object *obj;
|
||||||
@ -416,6 +435,9 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
|
|||||||
default_refs++;
|
default_refs++;
|
||||||
obj->used = 1;
|
obj->used = 1;
|
||||||
mark_reachable(obj, REACHABLE);
|
mark_reachable(obj, REACHABLE);
|
||||||
|
|
||||||
|
for_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ case ",$all_into_one," in
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
args="$args $local $quiet $no_reuse_delta$extra"
|
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
|
exit 1
|
||||||
if [ -z "$name" ]; then
|
if [ -z "$name" ]; then
|
||||||
echo Nothing new to pack.
|
echo Nothing new to pack.
|
||||||
|
1
git.c
1
git.c
@ -246,6 +246,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
|
|||||||
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
|
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
|
||||||
{ "push", cmd_push, RUN_SETUP },
|
{ "push", cmd_push, RUN_SETUP },
|
||||||
{ "read-tree", cmd_read_tree, RUN_SETUP },
|
{ "read-tree", cmd_read_tree, RUN_SETUP },
|
||||||
|
{ "reflog", cmd_reflog, RUN_SETUP },
|
||||||
{ "repo-config", cmd_repo_config },
|
{ "repo-config", cmd_repo_config },
|
||||||
{ "rerere", cmd_rerere, RUN_SETUP },
|
{ "rerere", cmd_rerere, RUN_SETUP },
|
||||||
{ "rev-list", cmd_rev_list, RUN_SETUP },
|
{ "rev-list", cmd_rev_list, RUN_SETUP },
|
||||||
|
37
refs.c
37
refs.c
@ -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;
|
const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
|
||||||
char *tz_c;
|
char *tz_c;
|
||||||
int logfd, tz;
|
int logfd, tz, reccnt = 0;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
unsigned long date;
|
unsigned long date;
|
||||||
unsigned char logged_sha1[20];
|
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;
|
lastrec = NULL;
|
||||||
rec = logend = logdata + st.st_size;
|
rec = logend = logdata + st.st_size;
|
||||||
while (logdata < rec) {
|
while (logdata < rec) {
|
||||||
|
reccnt++;
|
||||||
if (logdata < rec && *(rec-1) == '\n')
|
if (logdata < rec && *(rec-1) == '\n')
|
||||||
rec--;
|
rec--;
|
||||||
lastgt = NULL;
|
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))
|
if (get_sha1_hex(logdata, sha1))
|
||||||
die("Log %s is corrupt.", logfile);
|
die("Log %s is corrupt.", logfile);
|
||||||
munmap((void*)logdata, st.st_size);
|
munmap((void*)logdata, st.st_size);
|
||||||
fprintf(stderr, "warning: Log %s only goes back to %s.\n",
|
if (at_time)
|
||||||
logfile, show_rfc2822_date(date, tz));
|
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;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
4
refs.h
4
refs.h
@ -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. **/
|
/** 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);
|
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. **/
|
/** Returns 0 if target has the right format for a ref. **/
|
||||||
extern int check_ref_format(const char *target);
|
extern int check_ref_format(const char *target);
|
||||||
|
|
||||||
|
66
revision.c
66
revision.c
@ -464,21 +464,69 @@ static void limit_list(struct rev_info *revs)
|
|||||||
revs->commits = newlist;
|
revs->commits = newlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int all_flags;
|
struct all_refs_cb {
|
||||||
static struct rev_info *all_revs;
|
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)
|
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);
|
struct all_refs_cb *cb = cb_data;
|
||||||
add_pending_object(all_revs, object, "");
|
struct object *object = get_reference(cb->all_revs, path, sha1,
|
||||||
|
cb->all_flags);
|
||||||
|
add_pending_object(cb->all_revs, object, "");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_all(struct rev_info *revs, unsigned flags)
|
static void handle_all(struct rev_info *revs, unsigned flags)
|
||||||
{
|
{
|
||||||
all_revs = revs;
|
struct all_refs_cb cb;
|
||||||
all_flags = flags;
|
cb.all_revs = revs;
|
||||||
for_each_ref(handle_one_ref, NULL);
|
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)
|
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);
|
handle_all(revs, flags);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(arg, "--reflog")) {
|
||||||
|
handle_reflog(revs, flags);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp(arg, "--not")) {
|
if (!strcmp(arg, "--not")) {
|
||||||
flags ^= UNINTERESTING;
|
flags ^= UNINTERESTING;
|
||||||
continue;
|
continue;
|
||||||
|
Reference in New Issue
Block a user