Skip to content

Commit

Permalink
Merge branch 'jc/fsck-reflog'
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Junio C Hamano committed Dec 27, 2006
2 parents 268b827 + 7dc2692 commit e8b4029
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ git-quiltimport
git-read-tree
git-rebase
git-receive-pack
git-reflog
git-relink
git-repack
git-repo-config
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
21 changes: 1 addition & 20 deletions builtin-branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, "
Expand Down
3 changes: 2 additions & 1 deletion builtin-pack-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions builtin-prune.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
212 changes: 212 additions & 0 deletions builtin-reflog.c
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions commit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
22 changes: 22 additions & 0 deletions fsck-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion git-repack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions git.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
Loading

0 comments on commit e8b4029

Please sign in to comment.