From b15af07928cf8aaf405407ec94fca4a9202edbc2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 22:51:49 -0800 Subject: [PATCH 01/19] show-branch --reflog: tighten input validation. Signed-off-by: Junio C Hamano --- builtin-show-branch.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 452c15fd1b..651d27c2c0 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -4,7 +4,9 @@ #include "builtin.h" static const char show_branch_usage[] = -"git-show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...] | --reflog[=n] "; +"git-show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...] | --reflog[=n[,b]] "; +static const char show_branch_usage_reflog[] = +"--reflog is incompatible with --all, --remotes, --independent or --merge-base"; static int default_num; static int default_alloc; @@ -664,12 +666,14 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) */ if (independent || merge_base) usage(show_branch_usage); - if (!!reflog && (0 < extra)) + if (!!reflog && ((0 < extra) || all_heads || all_remotes)) /* * Asking for --more in reflog mode does not - * make sense. + * make sense. --list is Ok. + * + * Also --all and --remotes do not make sense either. */ - usage(show_branch_usage); + usage(show_branch_usage_reflog); } /* If nothing is specified, show all branches by default */ @@ -685,6 +689,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int base = 0; if (ac != 1) die("--reflog option needs one branch name"); + if (MAX_REVS < reflog) + die("Only %d entries can be shown at one time.", + MAX_REVS); if (!dwim_ref(*av, strlen(*av), sha1, &ref)) die("No such ref %s", *av); From da8f070cee6795594e4ac2af9f1e11cf9a7d3649 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 22:21:38 +0100 Subject: [PATCH 02/19] show_date(): fix relative dates We pass a timestamp (i.e. number of seconds elapsed since Jan 1 1970, 00:00:00 GMT) to the function. So there is no need to "fix" the timestamp according to the timezone. Signed-off-by: Johannes Schindelin --- date.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/date.c b/date.c index 7acb8cbd91..542c004c2e 100644 --- a/date.c +++ b/date.c @@ -62,12 +62,11 @@ const char *show_date(unsigned long time, int tz, int relative) if (relative) { unsigned long diff; - time_t t = gm_time_t(time, tz); struct timeval now; gettimeofday(&now, NULL); - if (now.tv_sec < t) + if (now.tv_sec < time) return "in the future"; - diff = now.tv_sec - t; + diff = now.tv_sec - time; if (diff < 90) { snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff); return timebuf; From 16bfefeebcac375ff51652eb8b866b7b65cb4905 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 18:57:06 -0800 Subject: [PATCH 03/19] show-branch --reflog: fix show_date() call Not passing tz to show_date() is not a fix. Signed-off-by: Junio C Hamano --- builtin-show-branch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 651d27c2c0..b54c410e14 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -725,7 +725,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) msg++; m = xmalloc(strlen(msg) + 200); sprintf(m, "(%s) %s", - show_date(timestamp, 0, 1), + show_date(timestamp, tz, 1), msg); reflog_msg[i] = m; free(logmsg); From cd554bb1733feb98ecee647176d0328cab53c2b7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 21 Jan 2007 02:17:19 +0100 Subject: [PATCH 04/19] apply --cached: fix crash in subdirectory The static variable "prefix" was shadowed by an unused parameter of the same name. In case of execution in a subdirectory, the static variable was accessed, leading to a crash. Signed-off-by: Johannes Schindelin --- builtin-apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-apply.c b/builtin-apply.c index 54fd2cb0c7..3fefdacd94 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2589,7 +2589,7 @@ static int git_apply_config(const char *var, const char *value) } -int cmd_apply(int argc, const char **argv, const char *prefix) +int cmd_apply(int argc, const char **argv, const char *unused_prefix) { int i; int read_stdin = 1; From 06f6228a90c50618f0a9793c9e9695be15f93ae2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 23:52:06 -0800 Subject: [PATCH 05/19] Stop ignoring Documentation/README We do not copy this file from elsewhere anymore. Signed-off-by: Junio C Hamano --- Documentation/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/.gitignore b/Documentation/.gitignore index a531be32dc..6a51331b2f 100644 --- a/Documentation/.gitignore +++ b/Documentation/.gitignore @@ -4,5 +4,4 @@ *.7 howto-index.txt doc.dep -README cmds-*.txt From 68025633e352264898de44ec4856552d9a3abece Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 03:09:34 +0100 Subject: [PATCH 06/19] Do not verify filenames in a bare repository For example, it makes no sense to check the presence of a file named "HEAD" when calling "git log HEAD" in a bare repository. Noticed by Han-Wen Nienhuys. Signed-off-by: Johannes Schindelin --- cache.h | 1 + setup.c | 26 ++++++++++++++++++++++++++ t/t1020-subdirectory.sh | 29 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/cache.h b/cache.h index a218db3b30..473197ded8 100644 --- a/cache.h +++ b/cache.h @@ -129,6 +129,7 @@ extern int cache_errno; extern int is_bare_repository_cfg; extern int is_bare_repository(void); +extern int is_inside_git_dir(void); extern const char *get_git_dir(void); extern char *get_object_directory(void); extern char *get_refs_directory(void); diff --git a/setup.c b/setup.c index cc97f9f5c1..e9d3f5aab6 100644 --- a/setup.c +++ b/setup.c @@ -95,6 +95,8 @@ void verify_non_filename(const char *prefix, const char *arg) const char *name; struct stat st; + if (is_inside_git_dir()) + return; if (*arg == '-') return; /* flag */ name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; @@ -168,6 +170,28 @@ static int is_git_directory(const char *suspect) return 1; } +static int inside_git_dir = -1; + +int is_inside_git_dir(void) +{ + if (inside_git_dir < 0) { + char buffer[1024]; + + if (is_bare_repository()) + return (inside_git_dir = 1); + if (getcwd(buffer, sizeof(buffer))) { + const char *git_dir = get_git_dir(), *cwd = buffer; + while (*git_dir && *git_dir == *cwd) { + git_dir++; + cwd++; + } + inside_git_dir = !*git_dir; + } else + inside_git_dir = 0; + } + return inside_git_dir; +} + const char *setup_git_directory_gently(int *nongit_ok) { static char cwd[PATH_MAX+1]; @@ -206,6 +230,7 @@ const char *setup_git_directory_gently(int *nongit_ok) if (chdir(cwd)) die("Cannot come back to cwd"); setenv(GIT_DIR_ENVIRONMENT, cwd, 1); + inside_git_dir = 1; return NULL; } if (nongit_ok) { @@ -226,6 +251,7 @@ const char *setup_git_directory_gently(int *nongit_ok) offset++; cwd[len++] = '/'; cwd[len] = 0; + inside_git_dir = !strncmp(cwd + offset, ".git/", 5); return cwd + offset; } diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index 4409b87f8d..c090c96185 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -106,4 +106,33 @@ test_expect_success 'read-tree' ' cmp ../one ../original.one ' +test_expect_success 'no file/rev ambuguity check inside .git' ' + cd $HERE && + git commit -a -m 1 && + cd $HERE/.git && + git show -s HEAD +' + +test_expect_success 'no file/rev ambuguity check inside a bare repo' ' + cd $HERE && + git clone -s --bare .git foo.git && + cd foo.git && GIT_DIR=. git show -s HEAD +' + +# This still does not work as it should... +: test_expect_success 'no file/rev ambuguity check inside a bare repo' ' + cd $HERE && + git clone -s --bare .git foo.git && + cd foo.git && git show -s HEAD +' + +test_expect_success 'detection should not be fooled by a symlink' ' + cd $HERE && + rm -fr foo.git && + git clone -s .git another && + ln -s another yetanother && + cd yetanother/.git && + git show -s HEAD +' + test_done From 453c1e857534f90b88367f96fc8ca1e7841f9400 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 10:47:41 -0800 Subject: [PATCH 07/19] git-tag -d: allow deleting multiple tags at once. Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 8 ++++---- git-tag.sh | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 8a71ab37df..13c7aefbf3 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -3,14 +3,14 @@ git-tag(1) NAME ---- -git-tag - Create or verify a tag object signed with GPG +git-tag - Create, list, delete or verify a tag object signed with GPG SYNOPSIS -------- [verse] -'git-tag' [-a | -s | -u ] [-f | -d | -v] [-m | -F ] - [] +'git-tag' [-a | -s | -u ] [-f | -v] [-m | -F ] [] +'git-tag' -d ... 'git-tag' -l [] DESCRIPTION @@ -55,7 +55,7 @@ OPTIONS Replace an existing tag with the given name (instead of failing) -d:: - Delete an existing tag with the given name + Delete existing tags with the given names. -v:: Verify the gpg signature of given the tag diff --git a/git-tag.sh b/git-tag.sh index ecb9100e4b..94499c9b36 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -63,12 +63,21 @@ do ;; -d) shift - tag_name="$1" - tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") || - die "Seriously, what tag are you talking about?" - git-update-ref -m 'tag: delete' -d "refs/tags/$tag_name" "$tag" && - echo "Deleted tag $tag_name." - exit $? + had_error=0 + for tag + do + cur=$(git-show-ref --verify --hash -- "refs/tags/$tag") || { + echo >&2 "Seriously, what tag are you talking about?" + had_error=1 + continue + } + git-update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || { + had_error=1 + continue + } + echo "Deleted tag $tag." + done + exit $had_error ;; -v) shift From 11a6ddb2c8a81815ee9d5411638487ee99770a0b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 10:51:37 -0800 Subject: [PATCH 08/19] branch -f: no reason to forbid updating the current branch in a bare repo. Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-branch.c b/builtin-branch.c index c760e188ea..25ffa5491c 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -324,7 +324,7 @@ static void create_branch(const char *name, const char *start_name, if (resolve_ref(ref, sha1, 1, NULL)) { if (!force) die("A branch named '%s' already exists.", name); - else if (!strcmp(head, name)) + else if (!is_bare_repository() && !strcmp(head, name)) die("Cannot force update the current branch."); } From bcf316187699c5e97bf47c1b8a00c844bf809fbc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 19:11:29 -0800 Subject: [PATCH 09/19] git-rebase: allow rebasing a detached HEAD. Signed-off-by: Junio C Hamano --- git-rebase.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index c8bd0f99d1..99cedadda1 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -275,8 +275,12 @@ case "$#" in git-checkout "$2" || usage ;; *) - branch_name=`git symbolic-ref HEAD` || die "No current branch" - branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` + if branch_name=`git symbolic-ref -q HEAD` + then + branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` + else + branch_name=HEAD ;# detached + fi ;; esac branch=$(git-rev-parse --verify "${branch_name}^0") || exit From 8860fd42fcf5a7853f7d7c2198793183320293ff Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 11 Jan 2007 11:47:48 +0100 Subject: [PATCH 10/19] Teach the revision walker to walk by reflogs with --walk-reflogs When called with "--walk-reflogs", as long as there are reflogs available, the walker will take this information into account, rather than the parent information in the commit object. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 2 +- log-tree.c | 3 + reflog-walk.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++ reflog-walk.h | 11 +++ revision.c | 11 +++ revision.h | 2 + 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 reflog-walk.c create mode 100644 reflog-walk.h diff --git a/Makefile b/Makefile index 8432ab8ba1..1a29392a12 100644 --- a/Makefile +++ b/Makefile @@ -254,7 +254,7 @@ LIB_OBJS = \ interpolate.o \ lockfile.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ - reachable.o \ + reachable.o reflog-walk.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ diff --git a/log-tree.c b/log-tree.c index 35be33aaf7..f043ad3723 100644 --- a/log-tree.c +++ b/log-tree.c @@ -2,6 +2,7 @@ #include "diff.h" #include "commit.h" #include "log-tree.h" +#include "reflog-walk.h" static void show_parents(struct commit *commit, int abbrev) { @@ -223,6 +224,8 @@ void show_log(struct rev_info *opt, const char *sep) printf("%s", diff_get_color(opt->diffopt.color_diff, DIFF_RESET)); putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + if (opt->reflog_info) + show_reflog_message(opt->reflog_info); } /* diff --git a/reflog-walk.c b/reflog-walk.c new file mode 100644 index 0000000000..d4b49c7354 --- /dev/null +++ b/reflog-walk.c @@ -0,0 +1,235 @@ +#include "cache.h" +#include "commit.h" +#include "refs.h" +#include "diff.h" +#include "revision.h" +#include "path-list.h" + +struct complete_reflogs { + char *ref; + struct reflog_info { + unsigned char osha1[20], nsha1[20]; + char *email; + unsigned long timestamp; + int tz; + char *message; + } *items; + int nr, alloc; +}; + +static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) +{ + struct complete_reflogs *array = cb_data; + struct reflog_info *item; + + if (array->nr >= array->alloc) { + array->alloc = alloc_nr(array->nr + 1); + array->items = xrealloc(array->items, array->alloc * + sizeof(struct reflog_info)); + } + item = array->items + array->nr; + memcpy(item->osha1, osha1, 20); + memcpy(item->nsha1, nsha1, 20); + item->email = xstrdup(email); + item->timestamp = timestamp; + item->tz = tz; + item->message = xstrdup(message); + array->nr++; + return 0; +} + +static struct complete_reflogs *read_complete_reflog(const char *ref) +{ + struct complete_reflogs *reflogs = + xcalloc(sizeof(struct complete_reflogs), 1); + reflogs->ref = xstrdup(ref); + for_each_reflog_ent(ref, read_one_reflog, reflogs); + if (reflogs->nr == 0) { + unsigned char sha1[20]; + const char *name = resolve_ref(ref, sha1, 1, NULL); + if (name) + for_each_reflog_ent(name, read_one_reflog, reflogs); + } + if (reflogs->nr == 0) { + int len = strlen(ref); + char *refname = xmalloc(len + 12); + sprintf(refname, "refs/%s", ref); + for_each_reflog_ent(refname, read_one_reflog, reflogs); + if (reflogs->nr == 0) { + sprintf(refname, "refs/heads/%s", ref); + for_each_reflog_ent(refname, read_one_reflog, reflogs); + } + free(refname); + } + return reflogs; +} + +static int get_reflog_recno_by_time(struct complete_reflogs *array, + unsigned long timestamp) +{ + int i; + for (i = array->nr - 1; i >= 0; i++) + if (timestamp >= array->items[i].timestamp) + return i; + return -1; +} + +struct commit_info_lifo { + struct commit_info { + struct commit *commit; + void *util; + } *items; + int nr, alloc; +}; + +static struct commit_info *get_commit_info(struct commit *commit, + struct commit_info_lifo *lifo, int pop) +{ + int i; + for (i = 0; i < lifo->nr; i++) + if (lifo->items[i].commit == commit) { + struct commit_info *result = &lifo->items[i]; + if (pop) { + if (i + 1 < lifo->nr) + memmove(lifo->items + i, + lifo->items + i + 1, + (lifo->nr - i) * + sizeof(struct commit_info)); + lifo->nr--; + } + return result; + } + return NULL; +} + +static void add_commit_info(struct commit *commit, void *util, + struct commit_info_lifo *lifo) +{ + struct commit_info *info; + if (lifo->nr >= lifo->alloc) { + lifo->alloc = alloc_nr(lifo->nr + 1); + lifo->items = xrealloc(lifo->items, + lifo->alloc * sizeof(struct commit_info)); + } + info = lifo->items + lifo->nr; + info->commit = commit; + info->util = util; + lifo->nr++; +} + +struct commit_reflog { + int flag, recno; + struct complete_reflogs *reflogs; +}; + +struct reflog_walk_info { + struct commit_info_lifo reflogs; + struct path_list complete_reflogs; + struct commit_reflog *last_commit_reflog; +}; + +void init_reflog_walk(struct reflog_walk_info** info) +{ + *info = xcalloc(sizeof(struct reflog_walk_info), 1); +} + +void add_reflog_for_walk(struct reflog_walk_info *info, + struct commit *commit, const char *name) +{ + unsigned long timestamp = 0; + int recno = -1; + struct path_list_item *item; + struct complete_reflogs *reflogs; + char *branch, *at = strchr(name, '@'); + struct commit_reflog *commit_reflog; + + branch = xstrdup(name); + if (at && at[1] == '{') { + char *ep; + branch[at - name] = '\0'; + recno = strtoul(at + 2, &ep, 10); + if (*ep != '}') { + recno = -1; + timestamp = approxidate(at + 2); + } + } else + recno = 0; + + item = path_list_lookup(branch, &info->complete_reflogs); + if (item) + reflogs = item->util; + else { + reflogs = read_complete_reflog(branch); + if (!reflogs || reflogs->nr == 0) + die("No reflogs found for '%s'", branch); + path_list_insert(branch, &info->complete_reflogs)->util + = reflogs; + } + + commit_reflog = xcalloc(sizeof(struct commit_reflog), 1); + if (recno < 0) { + commit_reflog->flag = 1; + commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); + if (commit_reflog->recno < 0) { + free(branch); + free(commit_reflog); + return; + } + } else + commit_reflog->recno = reflogs->nr - recno - 1; + commit_reflog->reflogs = reflogs; + + add_commit_info(commit, commit_reflog, &info->reflogs); +} + +void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) +{ + struct commit_info *commit_info = + get_commit_info(commit, &info->reflogs, 0); + struct commit_reflog *commit_reflog; + struct reflog_info *reflog; + + info->last_commit_reflog = NULL; + if (!commit_info) + return; + + commit_reflog = commit_info->util; + if (commit_reflog->recno < 0) { + commit->parents = NULL; + return; + } + + reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; + info->last_commit_reflog = commit_reflog; + commit_reflog->recno--; + commit_info->commit = (struct commit *)parse_object(reflog->osha1); + if (!commit_info->commit) { + commit->parents = NULL; + return; + } + + commit->parents = xcalloc(sizeof(struct commit_list), 1); + commit->parents->item = commit_info->commit; + commit->object.flags &= ~(ADDED | SEEN | SHOWN); +} + +void show_reflog_message(struct reflog_walk_info* info) +{ + if (info && info->last_commit_reflog) { + struct commit_reflog *commit_reflog = info->last_commit_reflog; + struct reflog_info *info; + + printf("Reflog: %s@{", commit_reflog->reflogs->ref); + info = &commit_reflog->reflogs->items[commit_reflog->recno + 1]; + if (commit_reflog->flag) + printf("%s", show_rfc2822_date(info->timestamp, + info->tz)); + else + printf("%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + printf("} (%s)\nReflog message: %s", + info->email, info->message); + } +} diff --git a/reflog-walk.h b/reflog-walk.h new file mode 100644 index 0000000000..787996b377 --- /dev/null +++ b/reflog-walk.h @@ -0,0 +1,11 @@ +#ifndef REFLOG_WALK_H +#define REFLOG_WALK_H + +extern void init_reflog_walk(struct reflog_walk_info** info); +extern void add_reflog_for_walk(struct reflog_walk_info *info, + struct commit *commit, const char *name); +extern void fake_reflog_parent(struct reflog_walk_info *info, + struct commit *commit); +extern void show_reflog_message(struct reflog_walk_info* info); + +#endif diff --git a/revision.c b/revision.c index f2ddd95e29..ebd025064c 100644 --- a/revision.c +++ b/revision.c @@ -7,6 +7,7 @@ #include "refs.h" #include "revision.h" #include "grep.h" +#include "reflog-walk.h" static char *path_name(struct name_path *path, const char *name) { @@ -116,6 +117,9 @@ void mark_parents_uninteresting(struct commit *commit) void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) { add_object_array(obj, name, &revs->pending); + if (revs->reflog_info && obj->type == OBJ_COMMIT) + add_reflog_for_walk(revs->reflog_info, + (struct commit *)obj, name); } static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) @@ -864,6 +868,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch handle_reflog(revs, flags); continue; } + if (!strcmp(arg, "--walk-reflogs")) { + init_reflog_walk(&revs->reflog_info); + continue; + } if (!strcmp(arg, "--not")) { flags ^= UNINTERESTING; continue; @@ -1210,6 +1218,9 @@ static struct commit *get_revision_1(struct rev_info *revs) revs->commits = entry->next; free(entry); + if (revs->reflog_info) + fake_reflog_parent(revs->reflog_info, commit); + /* * If we haven't done the list limiting, we need to look at * the parents here. We also need to do the date-based limiting diff --git a/revision.h b/revision.h index 8f7907d7ab..d93481f68f 100644 --- a/revision.h +++ b/revision.h @@ -89,6 +89,8 @@ struct rev_info { topo_sort_set_fn_t topo_setter; topo_sort_get_fn_t topo_getter; + + struct reflog_walk_info *reflog_info; }; #define REV_TREE_SAME 0 From db055e65d20b20f8f32eb85ee1a2417d3b95c1c7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 03:28:19 +0100 Subject: [PATCH 11/19] --walk-reflogs: disallow uninteresting commits Do not allow uninteresting commits with --walk-reflogs, since it is not clear what should be shown in these cases: $ git log --walk-reflogs master..next $ git log --walk-reflogs ^master Signed-off-by: Johannes Schindelin --- reflog-walk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reflog-walk.c b/reflog-walk.c index d4b49c7354..2d974116de 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -145,6 +145,9 @@ void add_reflog_for_walk(struct reflog_walk_info *info, char *branch, *at = strchr(name, '@'); struct commit_reflog *commit_reflog; + if (commit->object.flags & UNINTERESTING) + die ("Cannot walk reflogs for %s", name); + branch = xstrdup(name); if (at && at[1] == '{') { char *ep; From 911cedc95c2990b75f010d0d7bdbbc5911831708 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 23:21:32 -0800 Subject: [PATCH 12/19] log --walk-reflog: documentation Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 86c94e7dfd..fcc540bd3e 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -190,6 +190,21 @@ limiting may be applied. In addition to the '' listed on the command line, read them from the standard input. +--walk-reflogs:: + + Instead of walking the commit ancestry chain, walk + reflog entries from the most recent one to older ones. + When this option is used you cannot specify commits to + exclude (that is, '{caret}commit', 'commit1..commit2', + nor 'commit1...commit2' notations cannot be used). ++ +With '\--pretty' format other than oneline (for obvious reasons), +this causes the output to have two extra lines of information +taken from the reflog. By default, 'commit@{Nth}' notation is +used in the output. When the starting commit is specified as +'commit@{now}', output also uses 'commit@{timestamp}' notation +instead. + --merge:: After a failed merge, show refs that touch files having a From 53645a3a62977d5efd96ca9f3654985802212a9d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 00:47:34 -0800 Subject: [PATCH 13/19] reflog-walk: build fixes Dependency on reflog-walk.h was missing in the Makefile, and reflog-walk.c did not even include it. Signed-off-by: Junio C Hamano --- Makefile | 2 +- reflog-walk.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1a29392a12..07246f3d18 100644 --- a/Makefile +++ b/Makefile @@ -241,7 +241,7 @@ LIB_H = \ diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ - utf8.h + utf8.h reflog-walk.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ diff --git a/reflog-walk.c b/reflog-walk.c index 2d974116de..989a7aee95 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -4,6 +4,7 @@ #include "diff.h" #include "revision.h" #include "path-list.h" +#include "reflog-walk.h" struct complete_reflogs { char *ref; From 4d12a471230625da73be464f5a20b7480a6b8ecb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 00:51:41 -0800 Subject: [PATCH 14/19] Fix --walk-reflog with --pretty=oneline Now, "git log --abbrev-commit --pretty=o --walk-reflogs HEAD" is reasonably pleasant to use. Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 4 +++- log-tree.c | 3 ++- reflog-walk.c | 33 ++++++++++++++++++++++----------- reflog-walk.h | 2 +- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index fcc540bd3e..a996f6cb1e 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -27,6 +27,7 @@ SYNOPSIS [ \--pretty | \--header ] [ \--bisect ] [ \--merge ] + [ \--walk-reflogs ] ... [ \-- ... ] DESCRIPTION @@ -203,7 +204,8 @@ this causes the output to have two extra lines of information taken from the reflog. By default, 'commit@{Nth}' notation is used in the output. When the starting commit is specified as 'commit@{now}', output also uses 'commit@{timestamp}' notation -instead. +instead. Under '\--pretty=oneline', the commit message is +prefixed with this information on the same line. --merge:: diff --git a/log-tree.c b/log-tree.c index f043ad3723..c0fa096327 100644 --- a/log-tree.c +++ b/log-tree.c @@ -225,7 +225,8 @@ void show_log(struct rev_info *opt, const char *sep) diff_get_color(opt->diffopt.color_diff, DIFF_RESET)); putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); if (opt->reflog_info) - show_reflog_message(opt->reflog_info); + show_reflog_message(opt->reflog_info, + opt->commit_format == CMIT_FMT_ONELINE);; } /* diff --git a/reflog-walk.c b/reflog-walk.c index 989a7aee95..8ccbe97760 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -219,21 +219,32 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) commit->object.flags &= ~(ADDED | SEEN | SHOWN); } -void show_reflog_message(struct reflog_walk_info* info) +void show_reflog_message(struct reflog_walk_info* info, int oneline) { if (info && info->last_commit_reflog) { struct commit_reflog *commit_reflog = info->last_commit_reflog; struct reflog_info *info; - printf("Reflog: %s@{", commit_reflog->reflogs->ref); - info = &commit_reflog->reflogs->items[commit_reflog->recno + 1]; - if (commit_reflog->flag) - printf("%s", show_rfc2822_date(info->timestamp, - info->tz)); - else - printf("%d", commit_reflog->reflogs->nr - - 2 - commit_reflog->recno); - printf("} (%s)\nReflog message: %s", - info->email, info->message); + info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; + if (oneline) { + printf("%s@{", commit_reflog->reflogs->ref); + if (commit_reflog->flag) + printf("%s", show_date(info->timestamp, 0, 1)); + else + printf("%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + printf("}: "); + } + else { + printf("Reflog: %s@{", commit_reflog->reflogs->ref); + if (commit_reflog->flag) + printf("%s", show_rfc2822_date(info->timestamp, + info->tz)); + else + printf("%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + printf("} (%s)\nReflog message: %s", + info->email, info->message); + } } } diff --git a/reflog-walk.h b/reflog-walk.h index 787996b377..e63d86778b 100644 --- a/reflog-walk.h +++ b/reflog-walk.h @@ -6,6 +6,6 @@ extern void add_reflog_for_walk(struct reflog_walk_info *info, struct commit *commit, const char *name); extern void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit); -extern void show_reflog_message(struct reflog_walk_info* info); +extern void show_reflog_message(struct reflog_walk_info *info, int); #endif From 40ab7c33cd6dfdf887ed15baee1325ee445eced7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 10:49:15 +0100 Subject: [PATCH 15/19] --walk-reflogs: actually find the right commit by date. Embarassing thinko. Signed-off-by: Johannes Schindelin --- reflog-walk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflog-walk.c b/reflog-walk.c index 8ccbe97760..8e2cd2fcf6 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -71,7 +71,7 @@ static int get_reflog_recno_by_time(struct complete_reflogs *array, unsigned long timestamp) { int i; - for (i = array->nr - 1; i >= 0; i++) + for (i = array->nr - 1; i >= 0; i--) if (timestamp >= array->items[i].timestamp) return i; return -1; From a6c730644b7e1d35bd0d26962dbc978aa47d1863 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 22:28:16 +0100 Subject: [PATCH 16/19] --walk-reflogs: do not crash with cyclic reflog ancestry Since you can reset --hard to any revision you already had, when traversing the reflog ancestry, we may not free() the commit buffer. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index f3cff13edc..13a3f9b25d 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -50,8 +50,11 @@ static int cmd_log_walk(struct rev_info *rev) prepare_revision_walk(rev); while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); - free(commit->buffer); - commit->buffer = NULL; + if (!rev->reflog_info) { + /* we allow cycles in reflog ancestry */ + free(commit->buffer); + commit->buffer = NULL; + } free_commit_list(commit->parents); commit->parents = NULL; } From 9b088c4e394df84232cfd37aea78349a495b09c1 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Fri, 19 Jan 2007 11:49:35 +0100 Subject: [PATCH 17/19] prune: --grace=time This option gives grace period to objects that are unreachable from the refs from getting pruned. The default value is 24 hours and may be changed using gc.prunegrace. Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- Documentation/git-prune.txt | 9 ++++++++- builtin-prune.c | 31 ++++++++++++++++++++++++++++++- t/t1410-reflog.sh | 6 +++--- t/t5400-send-pack.sh | 4 ++-- t/t5700-clone-reference.sh | 4 ++-- t/t5710-info-alternate.sh | 6 +++--- 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index a11e303094..fbd344da40 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -8,7 +8,7 @@ git-prune - Prunes all unreachable objects from the object database SYNOPSIS -------- -'git-prune' [-n] [--] [...] +'git-prune' [-n] [--grace=