diff options
-rw-r--r-- | Documentation/git-annotate.txt | 44 | ||||
-rw-r--r-- | Documentation/git-blame.txt | 42 | ||||
-rw-r--r-- | Documentation/git.txt | 6 | ||||
-rwxr-xr-x | GIT-VERSION-GEN | 2 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | combine-diff.c | 101 | ||||
-rw-r--r-- | commit.c | 6 | ||||
-rw-r--r-- | commit.h | 2 | ||||
-rw-r--r-- | config.c | 39 | ||||
-rw-r--r-- | connect.c | 18 | ||||
-rw-r--r-- | diff-files.c | 27 | ||||
-rw-r--r-- | diff-tree.c | 94 | ||||
-rw-r--r-- | diff.c | 9 | ||||
-rw-r--r-- | diff.h | 8 | ||||
-rw-r--r-- | exec_cmd.c | 4 | ||||
-rwxr-xr-x | git-svnimport.perl | 18 | ||||
-rw-r--r-- | git.c | 162 | ||||
-rwxr-xr-x | gitk | 5 | ||||
-rw-r--r-- | http-push.c | 11 | ||||
-rw-r--r-- | log-tree.c | 211 | ||||
-rw-r--r-- | log-tree.h | 25 | ||||
-rw-r--r-- | quote.c | 2 | ||||
-rw-r--r-- | rev-list.c | 98 | ||||
-rw-r--r-- | revision.c | 202 | ||||
-rw-r--r-- | revision.h | 26 | ||||
-rw-r--r-- | sha1_file.c | 6 | ||||
-rw-r--r-- | t/Makefile | 2 | ||||
-rwxr-xr-x | t/t1200-tutorial.sh | 4 |
28 files changed, 685 insertions, 492 deletions
diff --git a/Documentation/git-annotate.txt b/Documentation/git-annotate.txt new file mode 100644 index 0000000000..7baf73111b --- /dev/null +++ b/Documentation/git-annotate.txt @@ -0,0 +1,44 @@ +git-annotate(1) +=============== + +NAME +---- +git-annotate - Annotate file lines with commit info + +SYNOPSIS +-------- +git-annotate [options] file [revision] + +DESCRIPTION +----------- +Annotates each line in the given file with information from the commit +which introduced the line. Optionally annotate from a given revision. + +OPTIONS +------- +-l, --long:: + Show long rev (Defaults off). + +-t, --time:: + Show raw timestamp (Defaults off). + +-r, --rename:: + Follow renames (Defaults on). + +-S, --rev-file <revs-file>:: + Use revs from revs-file instead of calling git-rev-list. + +-h, --help:: + Show help message. + +SEE ALSO +-------- +gitlink:git-blame[1] + +AUTHOR +------ +Written by Ryan Anderson <ryan@michonline.com>. + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt new file mode 100644 index 0000000000..51898787e6 --- /dev/null +++ b/Documentation/git-blame.txt @@ -0,0 +1,42 @@ +git-blame(1) +============ + +NAME +---- +git-blame - Blame file lines on commits + +SYNOPSIS +-------- +git-blame file [options] file [revision] + +DESCRIPTION +----------- +Annotates each line in the given file with information from the commit +which introduced the line. Start annotation from the given revision. + +OPTIONS +------- +-c, --compability:: + Use the same output mode as git-annotate (Default: off). + +-l, --long:: + Show long rev (Defaults off). + +-S, --rev-file <revs-file>:: + Use revs from revs-file instead of calling git-rev-list. + +-h, --help:: + Show help message. + + +SEE ALSO +-------- +gitlink:git-annotate[1] + +AUTHOR +------ +Written by Fredrik Kuivinen <freku045@student.liu.se>. + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Documentation/git.txt b/Documentation/git.txt index 03d860b191..24ca55da68 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -399,6 +399,12 @@ gitlink:git-update-ref[1]:: Interrogators: +gitlink:git-annotate[1]:: + Annotate file lines with commit info. + +gitlink:git-blame[1]:: + Blame file lines on commits. + gitlink:git-check-ref-format[1]:: Make sure ref name is well formed. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index e88fe5ae7c..7fcefcd7c4 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.3-rc4.GIT +DEF_VER=v1.3.GIT # First try git-describe, then see if there is a version file # (included in release tarballs), then default @@ -653,7 +653,7 @@ rpm: dist clean: rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \ $(LIB_FILE) $(XDIFF_LIB) - rm -f $(ALL_PROGRAMS) git$X + rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags rm -rf $(GIT_TARNAME) rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz @@ -671,7 +671,6 @@ check-docs:: @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \ do \ case "$$v" in \ - git-annotate | git-blame | \ git-merge-octopus | git-merge-ours | git-merge-recursive | \ git-merge-resolve | git-merge-stupid | \ git-ssh-pull | git-ssh-push ) continue ;; \ diff --git a/combine-diff.c b/combine-diff.c index 9bd27f82ec..ca36f5d5e7 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -5,6 +5,7 @@ #include "diffcore.h" #include "quote.h" #include "xdiff-interface.h" +#include "log-tree.h" static int uninteresting(struct diff_filepair *p) { @@ -584,12 +585,22 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt, sline->p_lno[i] = sline->p_lno[j]; } +static void dump_quoted_path(const char *prefix, const char *path) +{ + fputs(prefix, stdout); + if (quote_c_style(path, NULL, NULL, 0)) + quote_c_style(path, NULL, stdout, 0); + else + printf("%s", path); + putchar('\n'); +} + static int show_patch_diff(struct combine_diff_path *elem, int num_parent, - int dense, const char *header, - struct diff_options *opt) + int dense, struct rev_info *rev) { + struct diff_options *opt = &rev->diffopt; unsigned long result_size, cnt, lno; - char *result, *cp, *ep; + char *result, *cp; struct sline *sline; /* survived lines */ int mode_differs = 0; int i, show_hunks, shown_header = 0; @@ -641,7 +652,6 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, cnt++; /* incomplete line */ sline = xcalloc(cnt+2, sizeof(*sline)); - ep = result; sline[0].bol = result; for (lno = 0; lno <= cnt + 1; lno++) { sline[lno].lost_tail = &sline[lno].lost_head; @@ -689,16 +699,9 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, if (show_hunks || mode_differs || working_tree_file) { const char *abb; - if (header) { - shown_header++; - printf("%s%c", header, opt->line_termination); - } - printf("diff --%s ", dense ? "cc" : "combined"); - if (quote_c_style(elem->path, NULL, NULL, 0)) - quote_c_style(elem->path, NULL, stdout, 0); - else - printf("%s", elem->path); - putchar('\n'); + if (rev->loginfo) + show_log(rev, rev->loginfo, "\n"); + dump_quoted_path(dense ? "diff --cc " : "diff --combined ", elem->path); printf("index "); for (i = 0; i < num_parent; i++) { abb = find_unique_abbrev(elem->parent[i].sha1, @@ -729,6 +732,8 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, } putchar('\n'); } + dump_quoted_path("--- a/", elem->path); + dump_quoted_path("+++ b/", elem->path); dump_sline(sline, cnt, num_parent); } free(result); @@ -750,9 +755,10 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, #define COLONS "::::::::::::::::::::::::::::::::" -static void show_raw_diff(struct combine_diff_path *p, int num_parent, const char *header, struct diff_options *opt) +static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev) { - int i, offset, mod_type = 'A'; + struct diff_options *opt = &rev->diffopt; + int i, offset; const char *prefix; int line_termination, inter_name_termination; @@ -761,15 +767,8 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha if (!line_termination) inter_name_termination = 0; - if (header) - printf("%s%c", header, line_termination); - - for (i = 0; i < num_parent; i++) { - if (p->parent[i].mode) - mod_type = 'M'; - } - if (!p->mode) - mod_type = 'D'; + if (rev->loginfo) + show_log(rev, rev->loginfo, "\n"); if (opt->output_format == DIFF_FORMAT_RAW) { offset = strlen(COLONS) - num_parent; @@ -810,40 +809,44 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha } } -int show_combined_diff(struct combine_diff_path *p, +void show_combined_diff(struct combine_diff_path *p, int num_parent, int dense, - const char *header, - struct diff_options *opt) + struct rev_info *rev) { + struct diff_options *opt = &rev->diffopt; if (!p->len) - return 0; + return; switch (opt->output_format) { case DIFF_FORMAT_RAW: case DIFF_FORMAT_NAME_STATUS: case DIFF_FORMAT_NAME: - show_raw_diff(p, num_parent, header, opt); - return 1; - - default: + show_raw_diff(p, num_parent, rev); + return; case DIFF_FORMAT_PATCH: - return show_patch_diff(p, num_parent, dense, header, opt); + show_patch_diff(p, num_parent, dense, rev); + return; + default: + return; } } -const char *diff_tree_combined_merge(const unsigned char *sha1, - const char *header, int dense, - struct diff_options *opt) +void diff_tree_combined_merge(const unsigned char *sha1, + int dense, struct rev_info *rev) { + struct diff_options *opt = &rev->diffopt; struct commit *commit = lookup_commit(sha1); struct diff_options diffopts; struct commit_list *parents; struct combine_diff_path *p, *paths = NULL; int num_parent, i, num_paths; + int do_diffstat; + do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT || + opt->with_stat); diffopts = *opt; - diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; diffopts.with_raw = 0; + diffopts.with_stat = 0; diffopts.recursive = 1; /* count parents */ @@ -857,11 +860,24 @@ const char *diff_tree_combined_merge(const unsigned char *sha1, parents; parents = parents->next, i++) { struct commit *parent = parents->item; + /* show stat against the first parent even + * when doing combined diff. + */ + if (i == 0 && do_diffstat) + diffopts.output_format = DIFF_FORMAT_DIFFSTAT; + else + diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_tree_sha1(parent->object.sha1, commit->object.sha1, "", &diffopts); diffcore_std(&diffopts); paths = intersect_paths(paths, i, num_parent); + + if (do_diffstat && rev->loginfo) + show_log(rev, rev->loginfo, + opt->with_stat ? "---\n" : "\n"); diff_flush(&diffopts); + if (opt->with_stat) + putchar('\n'); } /* find out surviving paths */ @@ -874,17 +890,13 @@ const char *diff_tree_combined_merge(const unsigned char *sha1, int saved_format = opt->output_format; opt->output_format = DIFF_FORMAT_RAW; for (p = paths; p; p = p->next) { - if (show_combined_diff(p, num_parent, dense, - header, opt)) - header = NULL; + show_combined_diff(p, num_parent, dense, rev); } opt->output_format = saved_format; putchar(opt->line_termination); } for (p = paths; p; p = p->next) { - if (show_combined_diff(p, num_parent, dense, - header, opt)) - header = NULL; + show_combined_diff(p, num_parent, dense, rev); } } @@ -894,5 +906,4 @@ const char *diff_tree_combined_merge(const unsigned char *sha1, paths = paths->next; free(tmp); } - return header; } @@ -160,8 +160,8 @@ struct commit_graft *read_graft_line(char *buf, int len) if (buf[len-1] == '\n') buf[--len] = 0; - if (buf[0] == '#') - return 0; + if (buf[0] == '#' || buf[0] == '\0') + return NULL; if ((len + 1) % 41) { bad_graft_data: error("bad graft data: %s", buf); @@ -192,6 +192,8 @@ int read_graft_file(const char *graft_file) /* The format is just "Commit Parent1 Parent2 ...\n" */ int len = strlen(buf); struct commit_graft *graft = read_graft_line(buf, len); + if (!graft) + continue; if (register_commit_graft(graft, 1)) error("duplicate graft data: %s", buf); } @@ -45,6 +45,8 @@ enum cmit_fmt { CMIT_FMT_FULL, CMIT_FMT_FULLER, CMIT_FMT_ONELINE, + + CMIT_FMT_UNSPECIFIED, }; extern enum cmit_fmt get_commit_format(const char *arg); @@ -420,6 +420,7 @@ int git_config_set_multivar(const char* key, const char* value, { int i; int fd, in_fd; + int ret; char* config_filename = strdup(git_path("config")); char* lock_file = strdup(git_path("config.lock")); const char* last_dot = strrchr(key, '.'); @@ -429,9 +430,10 @@ int git_config_set_multivar(const char* key, const char* value, * key name separated by a dot, we have to know where the dot is. */ - if (last_dot == NULL) { + if (last_dot == NULL) { fprintf(stderr, "key does not contain a section: %s\n", key); - return 2; + ret = 2; + goto out_free; } store.baselen = last_dot - key; @@ -447,7 +449,8 @@ int git_config_set_multivar(const char* key, const char* value, (i == store.baselen+1 && !isalpha(key[i])))) { fprintf(stderr, "invalid key: %s\n", key); free(store.key); - return 1; + ret = 1; + goto out_free; } else store.key[i] = tolower(key[i]); store.key[i] = 0; @@ -460,7 +463,8 @@ int git_config_set_multivar(const char* key, const char* value, if (fd < 0) { fprintf(stderr, "could not lock config file\n"); free(store.key); - return -1; + ret = -1; + goto out_free; } /* @@ -475,13 +479,15 @@ int git_config_set_multivar(const char* key, const char* value, strerror(errno)); close(fd); unlink(lock_file); - return 3; /* same as "invalid config file" */ + ret = 3; /* same as "invalid config file" */ + goto out_free; } /* if nothing to unset, error out */ if (value == NULL) { close(fd); unlink(lock_file); - return 5; + ret = 5; + goto out_free; } store.key = (char*)key; @@ -507,7 +513,8 @@ int git_config_set_multivar(const char* key, const char* value, fprintf(stderr, "Invalid pattern: %s\n", value_regex); free(store.value_regex); - return 6; + ret = 6; + goto out_free; } } @@ -528,7 +535,8 @@ int git_config_set_multivar(const char* key, const char* value, regfree(store.value_regex); free(store.value_regex); } - return 3; + ret = 3; + goto out_free; } free(store.key); @@ -542,7 +550,8 @@ int git_config_set_multivar(const char* key, const char* value, (store.seen > 1 && multi_replace == 0)) { close(fd); unlink(lock_file); - return 5; + ret = 5; + goto out_free; } fstat(in_fd, &st); @@ -593,10 +602,18 @@ int git_config_set_multivar(const char* key, const char* value, if (rename(lock_file, config_filename) < 0) { fprintf(stderr, "Could not rename the lock file?\n"); - return 4; + ret = 4; + goto out_free; } - return 0; + ret = 0; + +out_free: + if (config_filename) + free(config_filename); + if (lock_file) + free(lock_file); + return ret; } @@ -74,7 +74,7 @@ int get_ack(int fd, unsigned char *result_sha1) line[--len] = 0; if (!strcmp(line, "NAK")) return 0; - if (!strncmp(line, "ACK ", 3)) { + if (!strncmp(line, "ACK ", 4)) { if (!get_sha1_hex(line+4, result_sha1)) { if (strstr(line+45, "continue")) return 2; @@ -567,6 +567,7 @@ int git_connect(int fd[2], char *url, const char *prog) int pipefd[2][2]; pid_t pid; enum protocol protocol = PROTO_LOCAL; + int free_path = 0; host = strstr(url, "://"); if(host) { @@ -610,16 +611,23 @@ int git_connect(int fd[2], char *url, const char *prog) char *ptr = path; if (path[1] == '~') path++; - else + else { path = strdup(ptr); + free_path = 1; + } *ptr = '\0'; } if (protocol == PROTO_GIT) { + int ret; if (git_use_proxy(host)) - return git_proxy_connect(fd, prog, host, path); - return git_tcp_connect(fd, prog, host, path); + ret = git_proxy_connect(fd, prog, host, path); + else + ret = git_tcp_connect(fd, prog, host, path); + if (free_path) + free(path); + return ret; } if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) @@ -659,6 +667,8 @@ int git_connect(int fd[2], char *url, const char *prog) fd[1] = pipefd[1][1]; close(pipefd[0][1]); close(pipefd[1][0]); + if (free_path) + free(path); return pid; } diff --git a/diff-files.c b/diff-files.c index 3e7f5f105b..ffbef48b2e 100644 --- a/diff-files.c +++ b/diff-files.c @@ -5,12 +5,14 @@ */ #include "cache.h" #include "diff.h" +#include "commit.h" +#include "revision.h" static const char diff_files_usage[] = "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]" COMMON_DIFF_OPTIONS_HELP; -static struct diff_options diff_options; +static struct rev_info rev; static int silent = 0; static int diff_unmerged_stage = 2; static int combine_merges = 0; @@ -18,12 +20,12 @@ static int dense_combined_merges = 0; static void show_unmerge(const char *path) { - diff_unmerge(&diff_options, path); + diff_unmerge(&rev.diffopt, path); } static void show_file(int pfx, struct cache_entry *ce) { - diff_addremove(&diff_options, pfx, ntohl(ce->ce_mode), + diff_addremove(&rev.diffopt, pfx, ntohl(ce->ce_mode), ce->sha1, ce->name, NULL); } @@ -31,7 +33,7 @@ static void show_modified(int oldmode, int mode, const unsigned char *old_sha1, const unsigned char *sha1, char *path) { - diff_change(&diff_options, oldmode, mode, old_sha1, sha1, path, NULL); + diff_change(&rev.diffopt, oldmode, mode, old_sha1, sha1, path, NULL); } int main(int argc, const char **argv) @@ -41,7 +43,7 @@ int main(int argc, const char **argv) int entries, i; git_config(git_diff_config); - diff_setup(&diff_options); + diff_setup(&rev.diffopt); while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "--")) { argv++; @@ -74,7 +76,7 @@ int main(int argc, const char **argv) dense_combined_merges = combine_merges = 1; else { int diff_opt_cnt; - diff_opt_cnt = diff_opt_parse(&diff_options, + diff_opt_cnt = diff_opt_parse(&rev.diffopt, argv+1, argc-1); if (diff_opt_cnt < 0) usage(diff_files_usage); @@ -89,13 +91,13 @@ int main(int argc, const char **argv) argv++; argc--; } if (dense_combined_merges) - diff_options.output_format = DIFF_FORMAT_PATCH; + rev.diffopt.output_format = DIFF_FORMAT_PATCH; /* Find the directory, and set up the pathspec */ pathspec = get_pathspec(prefix, argv + 1); entries = read_cache(); - if (diff_setup_done(&diff_options) < 0) + if (diff_setup_done(&rev.diffopt) < 0) usage(diff_files_usage); /* At this point, if argc == 1, then we are doing everything. @@ -167,8 +169,7 @@ int main(int argc, const char **argv) if (combine_merges && num_compare_stages == 2) { show_combined_diff(&combine.p, 2, dense_combined_merges, - NULL, - &diff_options); + &rev); free(combine.p.path); continue; } @@ -194,7 +195,7 @@ int main(int argc, const char **argv) continue; } changed = ce_match_stat(ce, &st, 0); - if (!changed && !diff_options.find_copies_harder) + if (!changed && !rev.diffopt.find_copies_harder) continue; oldmode = ntohl(ce->ce_mode); @@ -207,7 +208,7 @@ int main(int argc, const char **argv) ce->sha1, (changed ? null_sha1 : ce->sha1), ce->name); } - diffcore_std(&diff_options); - diff_flush(&diff_options); + diffcore_std(&rev.diffopt); + diff_flush(&rev.diffopt); return 0; } diff --git a/diff-tree.c b/diff-tree.c index 7015b06c7f..7207867a74 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -3,7 +3,7 @@ #include "commit.h" #include "log-tree.h" -static struct log_tree_opt log_tree_opt; +static struct rev_info log_tree_opt; static int diff_tree_commit_sha1(const unsigned char *sha1) { @@ -62,47 +62,21 @@ int main(int argc, const char **argv) { int nr_sha1; char line[1000]; - unsigned char sha1[2][20]; - const char *prefix = setup_git_directory(); - static struct log_tree_opt *opt = &log_tree_opt; + struct object *tree1, *tree2; + static struct rev_info *opt = &log_tree_opt; + struct object_list *list; int read_stdin = 0; git_config(git_diff_config); nr_sha1 = 0; - init_log_tree_opt(opt); + init_revisions(opt); + opt->abbrev = 0; + opt->diff = 1; + argc = setup_revisions(argc, argv, opt, NULL); - for (;;) { - int opt_cnt; - const char *arg; + while (--argc > 0) { + const char *arg = *++argv; - argv++; - argc--; - arg = *argv; - if (!arg) - break; - - if (*arg != '-') { - if (nr_sha1 < 2 && !get_sha1(arg, sha1[nr_sha1])) { - nr_sha1++; - continue; - } - break; - } - - opt_cnt = log_tree_opt_parse(opt, argv, argc); - if (opt_cnt < 0) - usage(diff_tree_usage); - else if (opt_cnt) { - argv += opt_cnt - 1; - argc -= opt_cnt - 1; - continue; - } - - if (!strcmp(arg, "--")) { - argv++; - argc--; - break; - } if (!strcmp(arg, "--stdin")) { read_stdin = 1; continue; @@ -110,18 +84,36 @@ int main(int argc, const char **argv) usage(diff_tree_usage); } - if (opt->combine_merges) - opt->ignore_merges = 0; - - /* We can only do dense combined merges with diff output */ - if (opt->dense_combined_merges) - opt->diffopt.output_format = DIFF_FORMAT_PATCH; - - if (opt->diffopt.output_format == DIFF_FORMAT_PATCH) - opt->diffopt.recursive = 1; - - diff_tree_setup_paths(get_pathspec(prefix, argv), &opt->diffopt); - diff_setup_done(&opt->diffopt); + /* + * NOTE! "setup_revisions()" will have inserted the revisions + * it parsed in reverse order. So if you do + * + * git-diff-tree a b + * + * the commit list will be "b" -> "a" -> NULL, so we reverse + * the order of the objects if the first one is not marked + * UNINTERESTING. + */ + nr_sha1 = 0; + list = opt->pending_objects; + if (list) { + nr_sha1++; + tree1 = list->item; + list = list->next; + if (list) { + nr_sha1++; + tree2 = tree1; + tree1 = list->item; + if (list->next) + usage(diff_tree_usage); + /* Switch them around if the second one was uninteresting.. */ + if (tree2->flags & UNINTERESTING) { + struct object *tmp = tree2; + tree2 = tree1; + tree1 = tmp; + } + } + } switch (nr_sha1) { case 0: @@ -129,10 +121,12 @@ int main(int argc, const char **argv) usage(diff_tree_usage); break; case 1: - diff_tree_commit_sha1(sha1[0]); + diff_tree_commit_sha1(tree1->sha1); break; case 2: - diff_tree_sha1(sha1[0], sha1[1], "", &opt->diffopt); + diff_tree_sha1(tree1->sha1, + tree2->sha1, + "", &opt->diffopt); log_tree_diff_flush(opt); break; } @@ -1029,6 +1029,15 @@ int diff_setup_done(struct diff_options *options) options->detect_rename != DIFF_DETECT_COPY) || (0 <= options->rename_limit && !options->detect_rename)) return -1; + + /* + * These cases always need recursive; we do not drop caller-supplied + * recursive bits for other formats here. + */ + if ((options->output_format == DIFF_FORMAT_PATCH) || + (options->output_format == DIFF_FORMAT_DIFFSTAT)) + options->recursive = 1; + if (options->detect_rename && options->rename_limit < 0) options->rename_limit = diff_rename_limit_default; if (options->setup & DIFF_SETUP_USE_CACHE) { @@ -6,6 +6,7 @@ #include "tree-walk.h" +struct rev_info; struct diff_options; typedef void (*change_fn_t)(struct diff_options *options, @@ -70,11 +71,10 @@ struct combine_diff_path { (sizeof(struct combine_diff_path) + \ sizeof(struct combine_diff_parent) * (n) + (l) + 1) -extern int show_combined_diff(struct combine_diff_path *elem, int num_parent, - int dense, const char *header, - struct diff_options *); +extern void show_combined_diff(struct combine_diff_path *elem, int num_parent, + int dense, struct rev_info *); -extern const char *diff_tree_combined_merge(const unsigned char *sha1, const char *, int, struct diff_options *opt); +extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *); extern void diff_addremove(struct diff_options *, int addremove, diff --git a/exec_cmd.c b/exec_cmd.c index 590e738969..44bb2f23de 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -32,7 +32,7 @@ const char *git_exec_path(void) int execv_git_cmd(const char **argv) { char git_command[PATH_MAX + 1]; - int len, err, i; + int len, i; const char *paths[] = { current_exec_path, getenv("GIT_EXEC_PATH"), builtin_exec_path }; @@ -85,8 +85,6 @@ int execv_git_cmd(const char **argv) /* execve() can only ever return if it fails */ execve(git_command, (char **)argv, environ); - err = errno; - argv[0] = tmp; } return -1; diff --git a/git-svnimport.perl b/git-svnimport.perl index 4d5371ca90..60ed7ae3ee 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -98,6 +98,7 @@ package SVNconn; use File::Spec; use File::Temp qw(tempfile); use POSIX qw(strftime dup2); +use Fcntl qw(SEEK_SET); sub new { my($what,$repo) = @_; @@ -143,9 +144,22 @@ sub file { } my $mode; if (exists $properties->{'svn:executable'}) { - $mode = '0755'; + $mode = '100755'; + } elsif (exists $properties->{'svn:special'}) { + my ($special_content, $filesize); + $filesize = tell $fh; + seek $fh, 0, SEEK_SET; + read $fh, $special_content, $filesize; + if ($special_content =~ s/^link //) { + $mode = '120000'; + seek $fh, 0, SEEK_SET; + truncate $fh, 0; + print $fh $special_content; + } else { + die "unexpected svn:special file encountered"; + } } else { - $mode = '0644'; + $mode = '100644'; } close ($fh); @@ -276,130 +276,66 @@ static int cmd_help(int argc, const char **argv, char **envp) return 0; } -#define LOGSIZE (65536) - -static int cmd_log(int argc, const char **argv, char **envp) +static int cmd_log_wc(int argc, const char **argv, char **envp, + struct rev_info *rev) { - struct rev_info rev; struct commit *commit; - char *buf = xmalloc(LOGSIZE); - static enum cmit_fmt commit_format = CMIT_FMT_DEFAULT; - int abbrev = DEFAULT_ABBREV; - int abbrev_commit = 0; - const char *commit_prefix = "commit "; - struct log_tree_opt opt; - int shown = 0; - int do_diff = 0; - int full_diff = 0; - - init_log_tree_opt(&opt); - argc = setup_revisions(argc, argv, &rev, "HEAD"); - while (1 < argc) { - const char *arg = argv[1]; - if (!strncmp(arg, "--pretty", 8)) { - commit_format = get_commit_format(arg + 8); - if (commit_format == CMIT_FMT_ONELINE) - commit_prefix = ""; - } - else if (!strcmp(arg, "--no-abbrev")) { - abbrev = 0; - } - else if (!strcmp(arg, "--abbrev")) { - abbrev = DEFAULT_ABBREV; - } - else if (!strcmp(arg, "--abbrev-commit")) { - abbrev_commit = 1; - } - else if (!strncmp(arg, "--abbrev=", 9)) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev && abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (40 < abbrev) - abbrev = 40; - } - else if (!strcmp(arg, "--full-diff")) { - do_diff = 1; - full_diff = 1; - } - else { - int cnt = log_tree_opt_parse(&opt, argv+1, argc-1); - if (0 < cnt) { - do_diff = 1; - argv += cnt; - argc -= cnt; - continue; - } - die("unrecognized argument: %s", arg); - } - argc--; argv++; - } + rev->abbrev = DEFAULT_ABBREV; + rev->commit_format = CMIT_FMT_DEFAULT; + rev->verbose_header = 1; + argc = setup_revisions(argc, argv, rev, "HEAD"); - if (do_diff) { - opt.diffopt.abbrev = abbrev; - opt.verbose_header = 0; - opt.always_show_header = 0; - opt.no_commit_id = 1; - if (opt.combine_merges) - opt.ignore_merges = 0; - if (opt.dense_combined_merges) - opt.diffopt.output_format = DIFF_FORMAT_PATCH; - if (opt.diffopt.output_format == DIFF_FORMAT_PATCH) - opt.diffopt.recursive = 1; - if (!full_diff && rev.prune_data) - diff_tree_setup_paths(rev.prune_data, &opt.diffopt); - diff_setup_done(&opt.diffopt); - } + if (argc > 1) + die("unrecognized argument: %s", argv[1]); - prepare_revision_walk(&rev); + prepare_revision_walk(rev); setup_pager(); - while ((commit = get_revision(&rev)) != NULL) { - if (shown && do_diff && commit_format != CMIT_FMT_ONELINE) - putchar('\n'); - fputs(commit_prefix, stdout); - if (abbrev_commit && abbrev) - fputs(find_unique_abbrev(commit->object.sha1, abbrev), - stdout); - else - fputs(sha1_to_hex(commit->object.sha1), stdout); - if (rev.parents) { - struct commit_list *parents = commit->parents; - while (parents) { - struct object *o = &(parents->item->object); - parents = parents->next; - if (o->flags & TMP_MARK) - continue; - printf(" %s", sha1_to_hex(o->sha1)); - o->flags |= TMP_MARK; - } - /* TMP_MARK is a general purpose flag that can - * be used locally, but the user should clean - * things up after it is done with them. - */ - for (parents = commit->parents; - parents; - parents = parents->next) - parents->item->object.flags &= ~TMP_MARK; - } - if (commit_format == CMIT_FMT_ONELINE) - putchar(' '); - else - putchar('\n'); - pretty_print_commit(commit_format, commit, ~0, buf, - LOGSIZE, abbrev); - printf("%s\n", buf); - if (do_diff) { - printf("---\n"); - log_tree_commit(&opt, commit); - } - shown = 1; + while ((commit = get_revision(rev)) != NULL) { + log_tree_commit(rev, commit); free(commit->buffer); commit->buffer = NULL; } - free(buf); return 0; } +static int cmd_wc(int argc, const char **argv, char **envp) +{ + struct rev_info rev; + + init_revisions(&rev); + rev.diff = 1; + rev.diffopt.recursive = 1; + return cmd_log_wc(argc, argv, envp, &rev); +} + +static int cmd_show(int argc, const char **argv, char **envp) +{ + struct rev_info rev; + + init_revisions(&rev); + rev.diff = 1; + rev.diffopt.recursive = 1; + rev.combine_merges = 1; + rev.dense_combined_merges = 1; + rev.always_show_header = 1; + rev.ignore_merges = 0; + rev.no_walk = 1; + return cmd_log_wc(argc, argv, envp, &rev); +} + +static int cmd_log(int argc, const char **argv, char **envp) +{ + struct rev_info rev; + + init_revisions(&rev); + rev.always_show_header = 1; + rev.diffopt.recursive = 1; + rev.combine_merges = 1; + rev.ignore_merges = 0; + return cmd_log_wc(argc, argv, envp, &rev); +} + static void handle_internal_command(int argc, const char **argv, char **envp) { const char *cmd = argv[0]; @@ -410,6 +346,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "version", cmd_version }, { "help", cmd_help }, { "log", cmd_log }, + { "whatchanged", cmd_wc }, + { "show", cmd_show }, }; int i; @@ -1116,11 +1116,12 @@ proc layoutrows {row endrow last} { proc addextraid {id row} { global displayorder commitrow commitinfo - global commitidx + global commitidx commitlisted global parentlist childlist children incr commitidx lappend displayorder $id + lappend commitlisted 0 lappend parentlist {} set commitrow($id) $row readcommit $id @@ -1500,7 +1501,7 @@ proc drawcmittext {id row col rmx} { proc drawcmitrow {row} { global displayorder rowidlist global idrowranges idrangedrawn iddrawn - global commitinfo commitlisted parentlist numcommits + global commitinfo parentlist numcommits if {$row >= $numcommits} return foreach id [lindex $rowidlist $row] { diff --git a/http-push.c b/http-push.c index 19a0f772e7..b4327d9243 100644 --- a/http-push.c +++ b/http-push.c @@ -60,12 +60,12 @@ enum XML_Status { #define LOCK_TIME 600 #define LOCK_REFRESH 30 -/* bits #0-6 in revision.h */ +/* bits #0-15 in revision.h */ -#define LOCAL (1u << 7) -#define REMOTE (1u << 8) -#define FETCHING (1u << 9) -#define PUSHING (1u << 10) +#define LOCAL (1u<<16) +#define REMOTE (1u<<17) +#define FETCHING (1u<<18) +#define PUSHING (1u<<19) /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 @@ -2498,6 +2498,7 @@ int main(int argc, char **argv) commit_argv[3] = old_sha1_hex; commit_argc++; } + init_revisions(&revs); setup_revisions(commit_argc, commit_argv, &revs, NULL); free(new_sha1_hex); if (old_sha1_hex) { diff --git a/log-tree.c b/log-tree.c index 3d404824a1..9634c4677f 100644 --- a/log-tree.c +++ b/log-tree.c @@ -3,59 +3,58 @@ #include "commit.h" #include "log-tree.h" -void init_log_tree_opt(struct log_tree_opt *opt) +void show_log(struct rev_info *opt, struct log_info *log, const char *sep) { - memset(opt, 0, sizeof *opt); - opt->ignore_merges = 1; - opt->header_prefix = ""; - opt->commit_format = CMIT_FMT_RAW; - diff_setup(&opt->diffopt); -} - -int log_tree_opt_parse(struct log_tree_opt *opt, const char **av, int ac) -{ - const char *arg; - int cnt = diff_opt_parse(&opt->diffopt, av, ac); - if (0 < cnt) - return cnt; - arg = *av; - if (!strcmp(arg, "-r")) - opt->diffopt.recursive = 1; - else if (!strcmp(arg, "-t")) { - opt->diffopt.recursive = 1; - opt->diffopt.tree_in_recursive = 1; - } - else if (!strcmp(arg, "-m")) - opt->ignore_merges = 0; - else if (!strcmp(arg, "-c")) - opt->combine_merges = 1; - else if (!strcmp(arg, "--cc")) { - opt->dense_combined_merges = 1; - opt->combine_merges = 1; - } - else if (!strcmp(arg, "-v")) { - opt->verbose_header = 1; - opt->header_prefix = "diff-tree "; - } - else if (!strncmp(arg, "--pretty", 8)) { - opt->verbose_header = 1; - opt->header_prefix = "diff-tree "; - opt->commit_format = get_commit_format(arg+8); + static char this_header[16384]; + struct commit *commit = log->commit, *parent = log->parent; + int abbrev = opt->diffopt.abbrev; + int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; + const char *extra; + int len; + + opt->loginfo = NULL; + if (!opt->verbose_header) { + puts(sha1_to_hex(commit->object.sha1)); + return; } - else if (!strcmp(arg, "--root")) - opt->show_root_diff = 1; - else if (!strcmp(arg, "--no-commit-id")) - opt->no_commit_id = 1; - else if (!strcmp(arg, "--always")) - opt->always_show_header = 1; - else - return 0; - return 1; + + /* + * The "oneline" format has several special cases: + * - The pretty-printed commit lacks a newline at the end + * of the buffer, but we do want to make sure that we + * have a newline there. If the separator isn't already + * a newline, add an extra one. + * - unlike other log messages, the one-line format does + * not have an empty line between entries. + */ + extra = ""; + if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE) + extra = "\n"; + if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE) + putchar('\n'); + opt->shown_one = 1; + + /* + * Print header line of header.. + */ + printf("%s%s", + opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", + diff_unique_abbrev(commit->object.sha1, abbrev_commit)); + if (parent) + printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit)); + putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + + /* + * And then the pretty-printed message itself + */ + len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev); + printf("%s%s%s", this_header, extra, sep); } -int log_tree_diff_flush(struct log_tree_opt *opt) +int log_tree_diff_flush(struct rev_info *opt) { diffcore_std(&opt->diffopt); + if (diff_queue_is_empty()) { int saved_fmt = opt->diffopt.output_format; opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT; @@ -63,17 +62,14 @@ int log_tree_diff_flush(struct log_tree_opt *opt) opt->diffopt.output_format = saved_fmt; return 0; } - if (opt->header) { - if (!opt->no_commit_id) - printf("%s%c", opt->header, - opt->diffopt.line_termination); - opt->header = NULL; - } + + if (opt->loginfo && !opt->no_commit_id) + show_log(opt, opt->loginfo, opt->diffopt.with_stat ? "---\n" : "\n"); diff_flush(&opt->diffopt); return 1; } -static int diff_root_tree(struct log_tree_opt *opt, +static int diff_root_tree(struct rev_info *opt, const unsigned char *new, const char *base) { int retval; @@ -93,83 +89,78 @@ static int diff_root_tree(struct log_tree_opt *opt, return retval; } -static const char *generate_header(struct log_tree_opt *opt, - const unsigned char *commit_sha1, - const unsigned char *parent_sha1, - const struct commit *commit) -{ - static char this_header[16384]; - int offset; - unsigned long len; - int abbrev = opt->diffopt.abbrev; - const char *msg = commit->buffer; - - if (!opt->verbose_header) - return sha1_to_hex(commit_sha1); - - len = strlen(msg); - - offset = sprintf(this_header, "%s%s ", - opt->header_prefix, - diff_unique_abbrev(commit_sha1, abbrev)); - if (commit_sha1 != parent_sha1) - offset += sprintf(this_header + offset, "(from %s)\n", - parent_sha1 - ? diff_unique_abbrev(parent_sha1, abbrev) - : "root"); - else - offset += sprintf(this_header + offset, "(from parents)\n"); - offset += pretty_print_commit(opt->commit_format, commit, len, - this_header + offset, - sizeof(this_header) - offset, abbrev); - if (opt->always_show_header) { - puts(this_header); - return NULL; - } - return this_header; -} - -static int do_diff_combined(struct log_tree_opt *opt, struct commit *commit) +static int do_diff_combined(struct rev_info *opt, struct commit *commit) { unsigned const char *sha1 = commit->object.sha1; - opt->header = generate_header(opt, sha1, sha1, commit); - opt->header = diff_tree_combined_merge(sha1, opt->header, - opt->dense_combined_merges, - &opt->diffopt); - if (!opt->header && opt->verbose_header) - opt->header_prefix = "\ndiff-tree "; - return 0; + diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt); + return !opt->loginfo; } -int log_tree_commit(struct log_tree_opt *opt, struct commit *commit) +/* + * Show the diff of a commit. + * + * Return true if we printed any log info messages + */ +static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log) { + int showed_log; struct commit_list *parents; unsigned const char *sha1 = commit->object.sha1; + if (!opt->diff) + return 0; + /* Root commit? */ - if (opt->show_root_diff && !commit->parents) { - opt->header = generate_header(opt, sha1, NULL, commit); - diff_root_tree(opt, sha1, ""); + parents = commit->parents; + if (!parents) { + if (opt->show_root_diff) + diff_root_tree(opt, sha1, ""); + return !opt->loginfo; } /* More than one parent? */ - if (commit->parents && commit->parents->next) { + if (parents && parents->next) { if (opt->ignore_merges) return 0; else if (opt->combine_merges) return do_diff_combined(opt, commit); + + /* If we show individual diffs, show the parent info */ + log->parent = parents->item; } - for (parents = commit->parents; parents; parents = parents->next) { + showed_log = 0; + for (;;) { struct commit *parent = parents->item; - unsigned const char *psha1 = parent->object.sha1; - opt->header = generate_header(opt, sha1, psha1, commit); - diff_tree_sha1(psha1, sha1, "", &opt->diffopt); - log_tree_diff_flush(opt); - if (!opt->header && opt->verbose_header) - opt->header_prefix = "\ndiff-tree "; + diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt); + log_tree_diff_flush(opt); + + showed_log |= !opt->loginfo; + + /* Set up the log info for the next parent, if any.. */ + parents = parents->next; + if (!parents) + break; + log->parent = parents->item; + opt->loginfo = log; + } + return showed_log; +} + +int log_tree_commit(struct rev_info *opt, struct commit *commit) +{ + struct log_info log; + + log.commit = commit; + log.parent = NULL; + opt->loginfo = &log; + + if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) { + log.parent = NULL; + show_log(opt, opt->loginfo, ""); } + opt->loginfo = NULL; return 0; } diff --git a/log-tree.h b/log-tree.h index da166c6f2c..a26e4841ff 100644 --- a/log-tree.h +++ b/log-tree.h @@ -1,23 +1,16 @@ #ifndef LOG_TREE_H #define LOG_TREE_H -struct log_tree_opt { - struct diff_options diffopt; - int show_root_diff; - int no_commit_id; - int verbose_header; - int ignore_merges; - int combine_merges; - int dense_combined_merges; - int always_show_header; - const char *header_prefix; - const char *header; - enum cmit_fmt commit_format; +#include "revision.h" + +struct log_info { + struct commit *commit, *parent; }; -void init_log_tree_opt(struct log_tree_opt *); -int log_tree_diff_flush(struct log_tree_opt *); -int log_tree_commit(struct log_tree_opt *, struct commit *); -int log_tree_opt_parse(struct log_tree_opt *, const char **, int); +void init_log_tree_opt(struct rev_info *); +int log_tree_diff_flush(struct rev_info *); +int log_tree_commit(struct rev_info *, struct commit *); +int log_tree_opt_parse(struct rev_info *, const char **, int); +void show_log(struct rev_info *opt, struct log_info *log, const char *sep); #endif @@ -144,8 +144,6 @@ static int quote_c_style_counted(const char *name, int namelen, case '\\': /* fallthru */ case '"': EMITQ(); break; - case ' ': - break; default: /* octal */ EMITQ(); diff --git a/rev-list.c b/rev-list.c index 963707a495..8b0ec388fa 100644 --- a/rev-list.c +++ b/rev-list.c @@ -8,9 +8,9 @@ #include "diff.h" #include "revision.h" -/* bits #0-6 in revision.h */ +/* bits #0-15 in revision.h */ -#define COUNTED (1u<<7) +#define COUNTED (1u<<16) static const char rev_list_usage[] = "git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n" @@ -39,24 +39,21 @@ static const char rev_list_usage[] = struct rev_info revs; static int bisect_list = 0; -static int verbose_header = 0; -static int abbrev = DEFAULT_ABBREV; -static int abbrev_commit = 0; static int show_timestamp = 0; static int hdr_termination = 0; -static const char *commit_prefix = ""; -static enum cmit_fmt commit_format = CMIT_FMT_RAW; +static const char *header_prefix; static void show_commit(struct commit *commit) { if (show_timestamp) printf("%lu ", commit->date); - if (commit_prefix[0]) - fputs(commit_prefix, stdout); + if (header_prefix) + fputs(header_prefix, stdout); if (commit->object.flags & BOUNDARY) putchar('-'); - if (abbrev_commit && abbrev) - fputs(find_unique_abbrev(commit->object.sha1, abbrev), stdout); + if (revs.abbrev_commit && revs.abbrev) + fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev), + stdout); else fputs(sha1_to_hex(commit->object.sha1), stdout); if (revs.parents) { @@ -78,14 +75,16 @@ static void show_commit(struct commit *commit) parents = parents->next) parents->item->object.flags &= ~TMP_MARK; } - if (commit_format == CMIT_FMT_ONELINE) + if (revs.commit_format == CMIT_FMT_ONELINE) putchar(' '); else putchar('\n'); - if (verbose_header) { + if (revs.verbose_header) { static char pretty_header[16384]; - pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev); + pretty_print_commit(revs.commit_format, commit, ~0, + pretty_header, sizeof(pretty_header), + revs.abbrev); printf("%s%c", pretty_header, hdr_termination); } fflush(stdout); @@ -297,58 +296,16 @@ int main(int argc, const char **argv) struct commit_list *list; int i; + init_revisions(&revs); + revs.abbrev = 0; + revs.commit_format = CMIT_FMT_UNSPECIFIED; argc = setup_revisions(argc, argv, &revs, NULL); for (i = 1 ; i < argc; i++) { const char *arg = argv[i]; - /* accept -<digit>, like traditilnal "head" */ - if ((*arg == '-') && isdigit(arg[1])) { - revs.max_count = atoi(arg + 1); - continue; - } - if (!strcmp(arg, "-n")) { - if (++i >= argc) - die("-n requires an argument"); - revs.max_count = atoi(argv[i]); - continue; - } - if (!strncmp(arg,"-n",2)) { - revs.max_count = atoi(arg + 2); - continue; - } if (!strcmp(arg, "--header")) { - verbose_header = 1; - continue; - } - if (!strcmp(arg, "--no-abbrev")) { - abbrev = 0; - continue; - } - if (!strcmp(arg, "--abbrev")) { - abbrev = DEFAULT_ABBREV; - continue; - } - if (!strcmp(arg, "--abbrev-commit")) { - abbrev_commit = 1; - continue; - } - if (!strncmp(arg, "--abbrev=", 9)) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev && abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (40 < abbrev) - abbrev = 40; - continue; - } - if (!strncmp(arg, "--pretty", 8)) { - commit_format = get_commit_format(arg+8); - verbose_header = 1; - hdr_termination = '\n'; - if (commit_format == CMIT_FMT_ONELINE) - commit_prefix = ""; - else - commit_prefix = "commit "; + revs.verbose_header = 1; continue; } if (!strcmp(arg, "--timestamp")) { @@ -362,15 +319,30 @@ int main(int argc, const char **argv) usage(rev_list_usage); } + if (revs.commit_format != CMIT_FMT_UNSPECIFIED) { + /* The command line has a --pretty */ + hdr_termination = '\n'; + if (revs.commit_format == CMIT_FMT_ONELINE) + header_prefix = ""; + else + header_prefix = "commit "; + } + else if (revs.verbose_header) + /* Only --header was specified */ + revs.commit_format = CMIT_FMT_RAW; list = revs.commits; - if (!list && - (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects)) + if ((!list && + (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && + !revs.pending_objects)) || + revs.diff) usage(rev_list_usage); - save_commit_buffer = verbose_header; + save_commit_buffer = revs.verbose_header; track_object_refs = 0; + if (bisect_list) + revs.limited = 1; prepare_revision_walk(&revs); if (revs.tree_objects) diff --git a/revision.c b/revision.c index 0505f3f455..dbd54da5ba 100644 --- a/revision.c +++ b/revision.c @@ -116,21 +116,27 @@ static void add_pending_object(struct rev_info *revs, struct object *obj, const add_object(obj, &revs->pending_objects, NULL, name); } -static struct commit *get_commit_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) +static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) { struct object *object; object = parse_object(sha1); if (!object) die("bad object %s", name); + object->flags |= flags; + return object; +} + +static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name) +{ + unsigned long flags = object->flags; /* * Tag object? Look what it points to.. */ while (object->type == tag_type) { struct tag *tag = (struct tag *) object; - object->flags |= flags; - if (revs->tag_objects && !(object->flags & UNINTERESTING)) + if (revs->tag_objects && !(flags & UNINTERESTING)) add_pending_object(revs, object, tag->tag); object = parse_object(tag->tagged->sha1); if (!object) @@ -143,7 +149,6 @@ static struct commit *get_commit_reference(struct rev_info *revs, const char *na */ if (object->type == commit_type) { struct commit *commit = (struct commit *)object; - object->flags |= flags; if (parse_commit(commit) < 0) die("unable to parse commit %s", name); if (flags & UNINTERESTING) { @@ -241,7 +246,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2) return REV_TREE_DIFFERENT; tree_difference = REV_TREE_SAME; if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", - &revs->diffopt) < 0) + &revs->pruning) < 0) return REV_TREE_DIFFERENT; return tree_difference; } @@ -264,7 +269,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1) empty.size = 0; tree_difference = 0; - retval = diff_tree(&empty, &real, "", &revs->diffopt); + retval = diff_tree(&empty, &real, "", &revs->pruning); free(tree); return retval >= 0 && !tree_difference; @@ -375,6 +380,9 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st if (revs->prune_fn) revs->prune_fn(revs, commit); + if (revs->no_walk) + return; + parent = commit->parents; while (parent) { struct commit *p = parent->item; @@ -451,21 +459,13 @@ static void limit_list(struct rev_info *revs) revs->commits = newlist; } -static void add_one_commit(struct commit *commit, struct rev_info *revs) -{ - if (!commit || (commit->object.flags & SEEN)) - return; - commit->object.flags |= SEEN; - commit_list_insert(commit, &revs->commits); -} - static int all_flags; static struct rev_info *all_revs; static int handle_one_ref(const char *path, const unsigned char *sha1) { - struct commit *commit = get_commit_reference(all_revs, path, sha1, all_flags); - add_one_commit(commit, all_revs); + struct object *object = get_reference(all_revs, path, sha1, all_flags); + add_pending_object(all_revs, object, ""); return 0; } @@ -479,9 +479,12 @@ static void handle_all(struct rev_info *revs, unsigned flags) void init_revisions(struct rev_info *revs) { memset(revs, 0, sizeof(*revs)); - revs->diffopt.recursive = 1; - revs->diffopt.add_remove = file_add_remove; - revs->diffopt.change = file_change; + + revs->abbrev = DEFAULT_ABBREV; + revs->ignore_merges = 1; + revs->pruning.recursive = 1; + revs->pruning.add_remove = file_add_remove; + revs->pruning.change = file_change; revs->lifo = 1; revs->dense = 1; revs->prefix = setup_git_directory(); @@ -494,6 +497,10 @@ void init_revisions(struct rev_info *revs) revs->topo_setter = topo_sort_default_setter; revs->topo_getter = topo_sort_default_getter; + + revs->commit_format = CMIT_FMT_DEFAULT; + + diff_setup(&revs->diffopt); } /* @@ -509,8 +516,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch const char **unrecognized = argv + 1; int left = 1; - init_revisions(revs); - /* First, search for "--" */ seen_dashdash = 0; for (i = 1; i < argc; i++) { @@ -526,13 +531,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch flags = 0; for (i = 1; i < argc; i++) { - struct commit *commit; + struct object *object; const char *arg = argv[i]; unsigned char sha1[20]; char *dotdot; int local_flags; if (*arg == '-') { + int opts; if (!strncmp(arg, "--max-count=", 12)) { revs->max_count = atoi(arg + 12); continue; @@ -640,6 +646,76 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->unpacked = 1; continue; } + if (!strcmp(arg, "-r")) { + revs->diff = 1; + revs->diffopt.recursive = 1; + continue; + } + if (!strcmp(arg, "-t")) { + revs->diff = 1; + revs->diffopt.recursive = 1; + revs->diffopt.tree_in_recursive = 1; + continue; + } + if (!strcmp(arg, "-m")) { + revs->ignore_merges = 0; + continue; + } + if (!strcmp(arg, "-c")) { + revs->diff = 1; + revs->combine_merges = 1; + continue; + } + if (!strcmp(arg, "--cc")) { + revs->diff = 1; + revs->dense_combined_merges = 1; + revs->combine_merges = 1; + continue; + } + if (!strcmp(arg, "-v")) { + revs->verbose_header = 1; + continue; + } + if (!strncmp(arg, "--pretty", 8)) { + revs->verbose_header = 1; + revs->commit_format = get_commit_format(arg+8); + continue; + } + if (!strcmp(arg, "--root")) { + revs->show_root_diff = 1; + continue; + } + if (!strcmp(arg, "--no-commit-id")) { + revs->no_commit_id = 1; + continue; + } + if (!strcmp(arg, "--always")) { + revs->always_show_header = 1; + continue; + } + if (!strcmp(arg, "--no-abbrev")) { + revs->abbrev = 0; + continue; + } + if (!strcmp(arg, "--abbrev")) { + revs->abbrev = DEFAULT_ABBREV; + continue; + } + if (!strcmp(arg, "--abbrev-commit")) { + revs->abbrev_commit = 1; + continue; + } + if (!strcmp(arg, "--full-diff")) { + revs->diff = 1; + revs->full_diff = 1; + continue; + } + opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); + if (opts > 0) { + revs->diff = 1; + i += opts - 1; + continue; + } *unrecognized++ = arg; left++; continue; @@ -656,15 +732,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch this = "HEAD"; if (!get_sha1(this, from_sha1) && !get_sha1(next, sha1)) { - struct commit *exclude; - struct commit *include; + struct object *exclude; + struct object *include; - exclude = get_commit_reference(revs, this, from_sha1, flags ^ UNINTERESTING); - include = get_commit_reference(revs, next, sha1, flags); + exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING); + include = get_reference(revs, next, sha1, flags); if (!exclude || !include) die("Invalid revision range %s..%s", arg, next); - add_one_commit(exclude, revs); - add_one_commit(include, revs); + add_pending_object(revs, exclude, this); + add_pending_object(revs, include, next); continue; } *dotdot = '.'; @@ -689,32 +765,57 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->prune_data = get_pathspec(revs->prefix, argv + i); break; } - commit = get_commit_reference(revs, arg, sha1, flags ^ local_flags); - add_one_commit(commit, revs); + object = get_reference(revs, arg, sha1, flags ^ local_flags); + add_pending_object(revs, object, arg); } - if (def && !revs->commits) { + if (def && !revs->pending_objects) { unsigned char sha1[20]; - struct commit *commit; + struct object *object; if (get_sha1(def, sha1) < 0) die("bad default revision '%s'", def); - commit = get_commit_reference(revs, def, sha1, 0); - add_one_commit(commit, revs); + object = get_reference(revs, def, sha1, 0); + add_pending_object(revs, object, def); } if (revs->topo_order || revs->unpacked) revs->limited = 1; if (revs->prune_data) { - diff_tree_setup_paths(revs->prune_data, &revs->diffopt); + diff_tree_setup_paths(revs->prune_data, &revs->pruning); revs->prune_fn = try_to_simplify_commit; + if (!revs->full_diff) + diff_tree_setup_paths(revs->prune_data, &revs->diffopt); + } + if (revs->combine_merges) { + revs->ignore_merges = 0; + if (revs->dense_combined_merges) + revs->diffopt.output_format = DIFF_FORMAT_PATCH; } + revs->diffopt.abbrev = revs->abbrev; + diff_setup_done(&revs->diffopt); return left; } void prepare_revision_walk(struct rev_info *revs) { - sort_by_date(&revs->commits); + struct object_list *list; + + list = revs->pending_objects; + revs->pending_objects = NULL; + while (list) { + struct commit *commit = handle_commit(revs, list->item, list->name); + if (commit) { + if (!(commit->object.flags & SEEN)) { + commit->object.flags |= SEEN; + insert_by_date(commit, &revs->commits); + } + } + list = list->next; + } + + if (revs->no_walk) + return; if (revs->limited) limit_list(revs); if (revs->topo_order) @@ -750,6 +851,17 @@ static void rewrite_parents(struct rev_info *revs, struct commit *commit) } } +static void mark_boundary_to_show(struct commit *commit) +{ + struct commit_list *p = commit->parents; + while (p) { + commit = p->item; + p = p->next; + if (commit->object.flags & BOUNDARY) + commit->object.flags |= BOUNDARY_SHOW; + } +} + struct commit *get_revision(struct rev_info *revs) { struct commit_list *list = revs->commits; @@ -787,8 +899,20 @@ struct commit *get_revision(struct rev_info *revs) } if (commit->object.flags & SHOWN) continue; - if (!(commit->object.flags & BOUNDARY) && - (commit->object.flags & UNINTERESTING)) + + /* We want to show boundary commits only when their + * children are shown. When path-limiter is in effect, + * rewrite_parents() drops some commits from getting shown, + * and there is no point showing boundary parents that + * are not shown. After rewrite_parents() rewrites the + * parents of a commit that is shown, we mark the boundary + * parents with BOUNDARY_SHOW. + */ + if (commit->object.flags & BOUNDARY_SHOW) { + commit->object.flags |= SHOWN; + return commit; + } + if (commit->object.flags & UNINTERESTING) continue; if (revs->min_age != -1 && (commit->date > revs->min_age)) continue; @@ -801,6 +925,8 @@ struct commit *get_revision(struct rev_info *revs) if (revs->parents) rewrite_parents(revs, commit); } + if (revs->boundary) + mark_boundary_to_show(commit); commit->object.flags |= SHOWN; return commit; } while (revs->commits); diff --git a/revision.h b/revision.h index 8970b57e3c..48d7b4ca94 100644 --- a/revision.h +++ b/revision.h @@ -7,9 +7,11 @@ #define SHOWN (1u<<3) #define TMP_MARK (1u<<4) /* for isolated cases; clean after use */ #define BOUNDARY (1u<<5) -#define ADDED (1u<<6) /* Parents already parsed and added? */ +#define BOUNDARY_SHOW (1u<<6) +#define ADDED (1u<<7) /* Parents already parsed and added? */ struct rev_info; +struct log_info; typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit); @@ -26,6 +28,7 @@ struct rev_info { /* Traversal flags */ unsigned int dense:1, no_merges:1, + no_walk:1, remove_empty_trees:1, lifo:1, topo_order:1, @@ -38,13 +41,32 @@ struct rev_info { boundary:1, parents:1; + /* Diff flags */ + unsigned int diff:1, + full_diff:1, + show_root_diff:1, + no_commit_id:1, + verbose_header:1, + ignore_merges:1, + combine_merges:1, + dense_combined_merges:1, + always_show_header:1; + + /* Format info */ + unsigned int shown_one:1, + abbrev_commit:1; + unsigned int abbrev; + enum cmit_fmt commit_format; + struct log_info *loginfo; + /* special limits */ int max_count; unsigned long max_age; unsigned long min_age; - /* paths limiting */ + /* diff info for patches and for paths limiting */ struct diff_options diffopt; + struct diff_options pruning; topo_sort_set_fn_t topo_setter; topo_sort_get_fn_t topo_getter; diff --git a/sha1_file.c b/sha1_file.c index e3d011309a..f2d33afb27 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -874,17 +874,19 @@ void packed_object_info_detail(struct pack_entry *e, unsigned char *base_sha1) { struct packed_git *p = e->p; - unsigned long offset, left; + unsigned long offset; unsigned char *pack; enum object_type kind; offset = unpack_object_header(p, e->offset, &kind, size); pack = p->pack_base + offset; - left = p->pack_size - offset; if (kind != OBJ_DELTA) *delta_chain_length = 0; else { unsigned int chain_length = 0; + if (p->pack_size <= offset + 20) + die("pack file %s records an incomplete delta base", + p->pack_name); memcpy(base_sha1, pack, 20); do { struct pack_entry base_ent; diff --git a/t/Makefile b/t/Makefile index fe65f53c5f..549598575b 100644 --- a/t/Makefile +++ b/t/Makefile @@ -25,5 +25,5 @@ clean: rm -fr trash .PHONY: $(T) clean -.NOPARALLEL: +.NOTPARALLEL: diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh index 10024133e3..16b3ea9157 100755 --- a/t/t1200-tutorial.sh +++ b/t/t1200-tutorial.sh @@ -49,7 +49,7 @@ test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output' #test_expect_success 'git-read-tree --reset HEAD' "git-read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git-update-index --refresh)\"" cat > whatchanged.expect << EOF -diff-tree VARIABLE (from root) +commit VARIABLE Author: VARIABLE Date: VARIABLE @@ -72,7 +72,7 @@ index 0000000..557db03 EOF git-whatchanged -p --root | \ - sed -e "1s/^\(.\{10\}\).\{40\}/\1VARIABLE/" \ + sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \ -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \ > whatchanged.output test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output' |