From: Shawn O. Pearce Date: Mon, 29 Sep 2008 17:52:34 +0000 (-0700) Subject: Merge branch 'maint' into bc/master-diff-hunk-header-fix X-Git-Tag: v1.6.1-rc1~194^2 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/5a139ba483efea47e103fef47afe87157e3bba77?ds=inline;hp=-c Merge branch 'maint' into bc/master-diff-hunk-header-fix * maint: (41 commits) Clarify commit error message for unmerged files Use strchrnul() instead of strchr() plus manual workaround Use remove_path from dir.c instead of own implementation Add remove_path: a function to remove as much as possible of a path git-submodule: Fix "Unable to checkout" for the initial 'update' Clarify how the user can satisfy stash's 'dirty state' check. Remove empty directories in recursive merge Documentation: clarify the details of overriding LESS via core.pager Update release notes for 1.6.0.3 checkout: Do not show local changes when in quiet mode for-each-ref: Fix --format=%(subject) for log message without newlines git-stash.sh: don't default to refs/stash if invalid ref supplied maint: check return of split_cmdline to avoid bad config strings builtin-prune.c: prune temporary packs in /pack directory Do not perform cross-directory renames when creating packs Use dashless git commands in setgitperms.perl git-remote: do not use user input in a printf format string make "git remote" report multiple URLs Start draft release notes for 1.6.0.3 git-repack uses --no-repack-object, not --no-repack-delta. ... Conflicts: RelNotes --- 5a139ba483efea47e103fef47afe87157e3bba77 diff --combined Documentation/config.txt index ed3285f899,87b028fbc1..5dda70de96 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -363,8 -363,17 +363,17 @@@ core.pager: variable. Note that git sets the `LESS` environment variable to `FRSX` if it is unset when it runs the pager. One can change these settings by setting the - `LESS` variable to some other value or by giving the - `core.pager` option a value such as "`less -+FRSX`". + `LESS` variable to some other value. Alternately, + these settings can be overridden on a project or + global basis by setting the `core.pager` option. + Setting `core.pager` has no affect on the `LESS` + environment variable behaviour above, so if you want + to override git's default settings this way, you need + to be explicit. For example, to disable the S option + in a backward compatible manner, set `core.pager` + to "`less -+$LESS -FRX`". This will be passed to the + shell by git, which will translate the final command to + "`LESS=FRSX less -+FRSX -FRX`". core.whitespace:: A comma separated list of common whitespace problems to @@@ -572,10 -581,6 +581,10 @@@ diff.autorefreshindex: affects only 'git-diff' Porcelain, and not lower level 'diff' commands, such as 'git-diff-files'. +diff.suppress-blank-empty:: + A boolean to inhibit the standard behavior of printing a space + before each empty output line. Defaults to false. + diff.external:: If this config variable is set, diff generation is not performed using the internal diff machinery, but using the diff --combined Documentation/gitattributes.txt index 9a752570f3,53da9b4f6b..2ae771f2fb --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@@ -270,27 -270,27 +270,27 @@@ See linkgit:git[1] for details Defining a custom hunk-header ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Each group of changes (called "hunk") in the textual diff output + Each group of changes (called a "hunk") in the textual diff output is prefixed with a line of the form: @@ -k,l +n,m @@ TEXT - The text is called 'hunk header', and by default a line that - begins with an alphabet, an underscore or a dollar sign is used, - which matches what GNU 'diff -p' output uses. This default - selection however is not suited for some contents, and you can - use customized pattern to make a selection. + This is called a 'hunk header'. The "TEXT" portion is by default a line + that begins with an alphabet, an underscore or a dollar sign; this + matches what GNU 'diff -p' output uses. This default selection however + is not suited for some contents, and you can use a customized pattern + to make a selection. - First in .gitattributes, you would assign the `diff` attribute + First, in .gitattributes, you would assign the `diff` attribute for paths. ------------------------ *.tex diff=tex ------------------------ - Then, you would define "diff.tex.xfuncname" configuration to + Then, you would define a "diff.tex.xfuncname" configuration to specify a regular expression that matches a line that you would - want to appear as the hunk header, like this: + want to appear as the hunk header "TEXT", like this: ------------------------ [diff "tex"] @@@ -311,16 -311,10 +311,16 @@@ patterns are available - `bibtex` suitable for files with BibTeX coded references. +- `html` suitable for HTML/XHTML documents. + - `java` suitable for source code in the Java language. - `pascal` suitable for source code in the Pascal/Delphi language. +- `php` suitable for source code in the PHP language. + +- `python` suitable for source code in the Python language. + - `ruby` suitable for source code in the Ruby language. - `tex` suitable for source code for LaTeX documents. diff --combined builtin-checkout.c index d986ac7abb,c4fc2b2c56..4ca76a6f48 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@@ -184,7 -184,7 +184,7 @@@ struct checkout_opts int force; int writeout_error; - char *new_branch; + const char *new_branch; int new_branch_log; enum branch_track track; }; @@@ -269,6 -269,8 +269,8 @@@ static int merge_working_tree(struct ch } /* 2-way merge to the new branch */ + topts.initial_checkout = (!active_nr && + (old->commit == new->commit)); topts.update = 1; topts.merge = 1; topts.gently = opts->merge; @@@ -326,7 -328,7 +328,7 @@@ commit_locked_index(lock_file)) die("unable to write new index file"); - if (!opts->force) + if (!opts->force && !opts->quiet) show_local_changes(&new->commit->object); return 0; @@@ -462,28 -464,13 +464,28 @@@ int cmd_checkout(int argc, const char * git_config(git_default_config, NULL); - opts.track = git_branch_track; + opts.track = BRANCH_TRACK_UNSPECIFIED; argc = parse_options(argc, argv, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - if (!opts.new_branch && (opts.track != git_branch_track)) - die("git checkout: --track and --no-track require -b"); + /* --track without -b should DWIM */ + if (0 < opts.track && !opts.new_branch) { + const char *argv0 = argv[0]; + if (!argc || !strcmp(argv0, "--")) + die ("--track needs a branch name"); + if (!prefixcmp(argv0, "refs/")) + argv0 += 5; + if (!prefixcmp(argv0, "remotes/")) + argv0 += 8; + argv0 = strchr(argv0, '/'); + if (!argv0 || !argv0[1]) + die ("Missing branch name; try -b"); + opts.new_branch = argv0 + 1; + } + + if (opts.track == BRANCH_TRACK_UNSPECIFIED) + opts.track = git_branch_track; if (opts.force && opts.merge) die("git checkout: -f and -m are incompatible"); diff --combined builtin-commit.c index 8165bb3d31,e2a7e48b1c..6b23143cd7 --- a/builtin-commit.c +++ b/builtin-commit.c @@@ -320,7 -320,7 +320,7 @@@ static char *prepare_index(int argc, co die("unable to write new_index file"); fd = hold_lock_file_for_update(&false_lock, - git_path("next-index-%d", getpid()), 1); + git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), 1); create_base_index(); add_remove_files(&partial); @@@ -639,7 -639,7 +639,7 @@@ static int prepare_to_commit(const cha active_cache_tree = cache_tree(); if (cache_tree_update(active_cache_tree, active_cache, active_nr, 0, 0) < 0) { - error("Error building trees"); + error("Error building trees; the index is unmerged?"); return 0; } @@@ -710,31 -710,6 +710,31 @@@ static int message_is_empty(struct strb return 1; } +static const char *find_author_by_nickname(const char *name) +{ + struct rev_info revs; + struct commit *commit; + struct strbuf buf = STRBUF_INIT; + const char *av[20]; + int ac = 0; + + init_revisions(&revs, NULL); + strbuf_addf(&buf, "--author=%s", name); + av[++ac] = "--all"; + av[++ac] = "-i"; + av[++ac] = buf.buf; + av[++ac] = NULL; + setup_revisions(ac, av, &revs, NULL); + prepare_revision_walk(&revs); + commit = get_revision(&revs); + if (commit) { + strbuf_release(&buf); + format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL); + return strbuf_detach(&buf, NULL); + } + die("No existing author found with '%s'", name); +} + static int parse_and_validate_options(int argc, const char *argv[], const char * const usage[], const char *prefix) @@@ -745,9 -720,6 +745,9 @@@ logfile = parse_options_fix_filename(prefix, logfile); template_file = parse_options_fix_filename(prefix, template_file); + if (force_author && !strchr(force_author, '>')) + force_author = find_author_by_nickname(force_author); + if (logfile || message.len || use_message) use_editor = 0; if (edit_flag) diff --combined builtin-log.c index 1d3c5cbf58,2efe593734..d4aaddc9db --- a/builtin-log.c +++ b/builtin-log.c @@@ -217,11 -217,6 +217,11 @@@ static int cmd_log_walk(struct rev_inf if (rev->early_output) finish_early_output(rev); + /* + * For --check and --exit-code, the exit code is based on CHECK_FAILED + * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to + * retain that state information if replacing rev->diffopt in this loop + */ while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); if (!rev->reflog_info) { @@@ -232,11 -227,7 +232,11 @@@ free_commit_list(commit->parents); commit->parents = NULL; } - return 0; + if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && + DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { + return 02; + } + return diff_result_code(&rev->diffopt, 0); } static int git_log_config(const char *var, const char *value, void *cb) @@@ -844,7 -835,7 +844,7 @@@ int cmd_format_patch(int argc, const ch committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) - die("bogos committer info %s\n", committer); + die("bogus committer info %s\n", committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } else if (!strcmp(argv[i], "--attach")) { @@@ -932,8 -923,7 +932,8 @@@ if (argc > 1) die ("unrecognized argument: %s", argv[1]); - if (!rev.diffopt.output_format) + if (!rev.diffopt.output_format + || rev.diffopt.output_format == DIFF_FORMAT_PATCH) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) diff --combined builtin-merge-recursive.c index dfb363ee2f,b9738655ad..36aa798459 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@@ -18,6 -18,7 +18,7 @@@ #include "ll-merge.h" #include "interpolate.h" #include "attr.h" + #include "dir.h" #include "merge-recursive.h" static int subtree_merge; @@@ -416,24 -417,6 +417,6 @@@ static int update_stages(const char *pa return 0; } - static int remove_path(const char *name) - { - int ret; - char *slash, *dirs; - - ret = unlink(name); - if (ret) - return ret; - dirs = xstrdup(name); - while ((slash = strrchr(name, '/'))) { - *slash = '\0'; - if (rmdir(name) != 0) - break; - } - free(dirs); - return ret; - } - static int remove_file(int clean, const char *path, int no_wd) { int update_cache = index_only || clean; @@@ -444,10 -427,8 +427,8 @@@ return -1; } if (update_working_directory) { - unlink(path); - if (errno != ENOENT || errno != EISDIR) + if (remove_path(path)) return -1; - remove_path(path); } return 0; } @@@ -729,13 -710,13 +710,13 @@@ static void conflict_rename_rename(stru const char *dst_name2 = ren2_dst; if (string_list_has_string(¤t_directory_set, ren1_dst)) { dst_name1 = del[delp++] = unique_path(ren1_dst, branch1); - output(1, "%s is a directory in %s added as %s instead", + output(1, "%s is a directory in %s adding as %s instead", ren1_dst, branch2, dst_name1); remove_file(0, ren1_dst, 0); } if (string_list_has_string(¤t_directory_set, ren2_dst)) { dst_name2 = del[delp++] = unique_path(ren2_dst, branch2); - output(1, "%s is a directory in %s added as %s instead", + output(1, "%s is a directory in %s adding as %s instead", ren2_dst, branch1, dst_name2); remove_file(0, ren2_dst, 0); } @@@ -760,7 -741,7 +741,7 @@@ static void conflict_rename_dir(struct const char *branch1) { char *new_path = unique_path(ren1->pair->two->path, branch1); - output(1, "Renamed %s to %s instead", ren1->pair->one->path, new_path); + output(1, "Renaming %s to %s instead", ren1->pair->one->path, new_path); remove_file(0, ren1->pair->two->path, 0); update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path); free(new_path); @@@ -773,7 -754,7 +754,7 @@@ static void conflict_rename_rename_2(st { char *new_path1 = unique_path(ren1->pair->two->path, branch1); char *new_path2 = unique_path(ren2->pair->two->path, branch2); - output(1, "Renamed %s to %s and %s to %s instead", + output(1, "Renaming %s to %s and %s to %s instead", ren1->pair->one->path, new_path1, ren2->pair->one->path, new_path2); remove_file(0, ren1->pair->two->path, 0); @@@ -887,10 -868,10 +868,10 @@@ static int process_renames(struct strin branch1, branch2); if (mfi.merge || !mfi.clean) - output(1, "Renamed %s->%s", src, ren1_dst); + output(1, "Renaming %s->%s", src, ren1_dst); if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); + output(2, "Auto-merging %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (content): merge conflict in %s", @@@ -924,14 -905,14 +905,14 @@@ if (string_list_has_string(¤t_directory_set, ren1_dst)) { clean_merge = 0; - output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s " + output(1, "CONFLICT (rename/directory): Rename %s->%s in %s " " directory %s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); conflict_rename_dir(ren1, branch1); } else if (sha_eq(src_other.sha1, null_sha1)) { clean_merge = 0; - output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s " + output(1, "CONFLICT (rename/delete): Rename %s->%s in %s " "and deleted in %s", ren1_src, ren1_dst, branch1, branch2); @@@ -940,19 -921,19 +921,19 @@@ const char *new_path; clean_merge = 0; try_merge = 1; - output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. " + output(1, "CONFLICT (rename/add): Rename %s->%s in %s. " "%s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); new_path = unique_path(ren1_dst, branch2); - output(1, "Added as %s instead", new_path); + output(1, "Adding as %s instead", new_path); update_file(0, dst_other.sha1, dst_other.mode, new_path); } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) { ren2 = item->util; clean_merge = 0; ren2->processed = 1; - output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. " - "Renamed %s->%s in %s", + output(1, "CONFLICT (rename/rename): Rename %s->%s in %s. " + "Rename %s->%s in %s", ren1_src, ren1_dst, branch1, ren2->pair->one->path, ren2->pair->two->path, branch2); conflict_rename_rename_2(ren1, branch1, ren2, branch2); @@@ -986,9 -967,9 +967,9 @@@ output(3, "Skipped %s (merged same as existing)", ren1_dst); else { if (mfi.merge || !mfi.clean) - output(1, "Renamed %s => %s", ren1_src, ren1_dst); + output(1, "Renaming %s => %s", ren1_src, ren1_dst); if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); + output(2, "Auto-merging %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (rename/modify): Merge conflict in %s", ren1_dst); @@@ -1039,7 -1020,7 +1020,7 @@@ static int process_entry(const char *pa /* Deleted in both or deleted in one and * unchanged in the other */ if (a_sha) - output(2, "Removed %s", path); + output(2, "Removing %s", path); /* do not touch working file if it did not exist */ remove_file(1, path, !a_sha); } else { @@@ -1086,12 -1067,12 +1067,12 @@@ const char *new_path = unique_path(path, add_branch); clean_merge = 0; output(1, "CONFLICT (%s): There is a directory with name %s in %s. " - "Added %s as %s", + "Adding %s as %s", conf, path, other_branch, path, new_path); remove_file(0, path, 0); update_file(0, sha, mode, new_path); } else { - output(2, "Added %s", path); + output(2, "Adding %s", path); update_file(1, sha, mode, path); } } else if (a_sha && b_sha) { @@@ -1105,7 -1086,7 +1086,7 @@@ reason = "add/add"; o_sha = (unsigned char *)null_sha1; } - output(2, "Auto-merged %s", path); + output(2, "Auto-merging %s", path); o.path = a.path = b.path = (char *)path; hashcpy(o.sha1, o_sha); o.mode = o_mode; diff --combined builtin-merge.c index 9ad9791068,dcaf3681dc..1094c5fd58 --- a/builtin-merge.c +++ b/builtin-merge.c @@@ -22,7 -22,6 +22,7 @@@ #include "log-tree.h" #include "color.h" #include "rerere.h" +#include "help.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@@ -78,9 -77,7 +78,9 @@@ static int option_parse_message(const s static struct strategy *get_strategy(const char *name) { int i; - struct strbuf err; + struct strategy *ret; + static struct cmdnames main_cmds, other_cmds; + static int loaded; if (!name) return NULL; @@@ -89,43 -86,12 +89,43 @@@ if (!strcmp(name, all_strategy[i].name)) return &all_strategy[i]; - strbuf_init(&err, 0); - for (i = 0; i < ARRAY_SIZE(all_strategy); i++) - strbuf_addf(&err, " %s", all_strategy[i].name); - fprintf(stderr, "Could not find merge strategy '%s'.\n", name); - fprintf(stderr, "Available strategies are:%s.\n", err.buf); - exit(1); + if (!loaded) { + struct cmdnames not_strategies; + loaded = 1; + + memset(¬_strategies, 0, sizeof(struct cmdnames)); + load_command_list("git-merge-", &main_cmds, &other_cmds); + for (i = 0; i < main_cmds.cnt; i++) { + int j, found = 0; + struct cmdname *ent = main_cmds.names[i]; + for (j = 0; j < ARRAY_SIZE(all_strategy); j++) + if (!strncmp(ent->name, all_strategy[j].name, ent->len) + && !all_strategy[j].name[ent->len]) + found = 1; + if (!found) + add_cmdname(¬_strategies, ent->name, ent->len); + exclude_cmds(&main_cmds, ¬_strategies); + } + } + if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) { + fprintf(stderr, "Could not find merge strategy '%s'.\n", name); + fprintf(stderr, "Available strategies are:"); + for (i = 0; i < main_cmds.cnt; i++) + fprintf(stderr, " %s", main_cmds.names[i]->name); + fprintf(stderr, ".\n"); + if (other_cmds.cnt) { + fprintf(stderr, "Available custom strategies are:"); + for (i = 0; i < other_cmds.cnt; i++) + fprintf(stderr, " %s", other_cmds.names[i]->name); + fprintf(stderr, ".\n"); + } + exit(1); + } + + ret = xmalloc(sizeof(struct strategy)); + memset(ret, 0, sizeof(struct strategy)); + ret->name = xstrdup(name); + return ret; } static void append_strategy(struct strategy *s) @@@ -476,6 -442,8 +476,8 @@@ static int git_merge_config(const char buf = xstrdup(v); argc = split_cmdline(buf, &argv); + if (argc < 0) + die("Bad branch.%s.mergeoptions string", branch); argv = xrealloc(argv, sizeof(*argv) * (argc + 2)); memmove(argv + 1, argv, sizeof(*argv) * (argc + 1)); argc++; @@@ -868,11 -836,6 +870,11 @@@ int cmd_merge(int argc, const char **ar if (argc != 1) die("Can merge only exactly one commit into " "empty head"); + if (squash) + die("Squash commit into empty head not supported yet"); + if (!allow_fast_forward) + die("Non-fast-forward commit does not make sense into " + "an empty head"); remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT); if (!remote_head) die("%s - not something we can merge", argv[0]); diff --combined contrib/completion/git-completion.bash index d3fb6ae507,3bc45f6b47..2d8d1c3d76 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@@ -750,7 -750,7 +750,7 @@@ _git_commit ( --*) __gitcomp " --all --author= --signoff --verify --no-verify - --edit --amend --include --only + --edit --amend --include --only --interactive " return esac @@@ -1493,7 -1493,7 +1493,7 @@@ _git_submodule ( { __git_has_doubledash && return - local subcommands="add status init update" + local subcommands="add status init update summary foreach sync" if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in diff --combined diff.c index 05dd8f0b56,781fa15ac1..a3793f65e7 --- a/diff.c +++ b/diff.c @@@ -20,7 -20,6 +20,7 @@@ static int diff_detect_rename_default; static int diff_rename_limit_default = 200; +static int diff_suppress_blank_empty; int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@@ -182,12 -181,6 +182,12 @@@ int git_diff_basic_config(const char *v return 0; } + /* like GNU diff's --suppress-blank-empty option */ + if (!strcmp(var, "diff.suppress-blank-empty")) { + diff_suppress_blank_empty = git_config_bool(var, value); + return 0; + } + if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); if (ep != var + 4) { @@@ -387,6 -380,7 +387,6 @@@ static void diff_words_append(char *lin } struct diff_words_data { - struct xdiff_emit_state xm; struct diff_words_buffer minus, plus; FILE *file; }; @@@ -476,8 -470,11 +476,8 @@@ static void diff_words_show(struct diff xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; - ecb.outf = xdiff_outf; - ecb.priv = diff_words; - diff_words->xm.consume = fn_out_diff_words_aux; - xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); - + xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, + &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); diff_words->minus.text.size = diff_words->plus.text.size = 0; @@@ -491,6 -488,7 +491,6 @@@ typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len); struct emit_callback { - struct xdiff_emit_state xm; int nparents, color_diff; unsigned ws_rule; sane_truncate_fn truncate; @@@ -600,12 -598,6 +600,12 @@@ static void fn_out_consume(void *priv, ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } + if (diff_suppress_blank_empty + && len == 2 && line[0] == ' ' && line[1] == '\n') { + line[0] = '\n'; + len = 1; + } + /* This is not really necessary for now because * this codepath only deals with two-way diffs. */ @@@ -734,6 -726,8 +734,6 @@@ static char *pprint_rename(const char * } struct diffstat_t { - struct xdiff_emit_state xm; - int nr; int alloc; struct diffstat_file { @@@ -1163,6 -1157,7 +1163,6 @@@ static void free_diffstat_info(struct d } struct checkdiff_t { - struct xdiff_emit_state xm; const char *filename; int lineno; struct diff_options *o; @@@ -1404,9 -1399,6 +1404,9 @@@ static const struct funcname_pattern_en } static const struct funcname_pattern_entry builtin_funcname_pattern[] = { + { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + REG_EXTENDED }, + { "html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", REG_EXTENDED }, { "java", "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n" "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$", @@@ -1414,18 -1406,16 +1414,18 @@@ { "pascal", "^((procedure|function|constructor|destructor|interface|" "implementation|initialization|finalization)[ \t]*.*)$" - "|" + "\n" "^(.*=[ \t]*(class|record).*)$", REG_EXTENDED }, + { "php", "^[\t ]*((function|class).*)", REG_EXTENDED }, + { "python", "^[ \t]*((class|def)[ \t].*)$", REG_EXTENDED }, + { "ruby", "^[ \t]*((class|module|def)[ \t].*)$", + REG_EXTENDED }, { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", REG_EXTENDED }, { "tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", REG_EXTENDED }, - { "ruby", "^[ \t]*((class|module|def)[ \t].*)$", - REG_EXTENDED }, }; static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one) @@@ -1558,13 -1548,15 +1558,13 @@@ static void builtin_diff(const char *na xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); else if (!prefixcmp(diffopts, "-u")) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); - ecb.outf = xdiff_outf; - ecb.priv = &ecbdata; - ecbdata.xm.consume = fn_out_consume; if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) { ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); ecbdata.diff_words->file = o->file; } - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, + &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@@ -1615,8 -1607,9 +1615,8 @@@ static void builtin_diffstat(const cha memset(&xecfg, 0, sizeof(xecfg)); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; - ecb.outf = xdiff_outf; - ecb.priv = diffstat; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat, + &xpp, &xecfg, &ecb); } free_and_return: @@@ -1637,6 -1630,7 +1637,6 @@@ static void builtin_checkdiff(const cha return; memset(&data, 0, sizeof(data)); - data.xm.consume = checkdiff_consume; data.filename = name_b ? name_b : name_a; data.lineno = 0; data.o = o; @@@ -1662,8 -1656,9 +1662,8 @@@ memset(&xecfg, 0, sizeof(xecfg)); xecfg.ctxlen = 1; /* at least one context line */ xpp.flags = XDF_NEED_MINIMAL; - ecb.outf = xdiff_outf; - ecb.priv = &data; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data, + &xpp, &xecfg, &ecb); if ((data.ws_rule & WS_TRAILING_SPACE) && data.trailing_blanks_start) { @@@ -2417,13 -2412,6 +2417,6 @@@ int diff_setup_done(struct diff_option DIFF_OPT_SET(options, EXIT_WITH_STATUS); } - /* - * If we postprocess in diffcore, we cannot simply return - * upon the first hit. We need to run diff as usual. - */ - if (options->pickaxe || options->filter) - DIFF_OPT_CLR(options, QUIET); - return 0; } @@@ -3054,6 -3042,7 +3047,6 @@@ static void diff_summary(FILE *file, st } struct patch_id_t { - struct xdiff_emit_state xm; SHA_CTX *ctx; int patchlen; }; @@@ -3098,6 -3087,7 +3091,6 @@@ static int diff_get_patch_id(struct dif SHA1_Init(&ctx); memset(&data, 0, sizeof(struct patch_id_t)); data.ctx = &ctx; - data.xm.consume = patch_id_consume; for (i = 0; i < q->nr; i++) { xpparam_t xpp; @@@ -3162,8 -3152,9 +3155,8 @@@ xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = 3; xecfg.flags = XDL_EMIT_FUNCNAMES; - ecb.outf = xdiff_outf; - ecb.priv = &data; - xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data, + &xpp, &xecfg, &ecb); } SHA1_Final(sha1, &ctx); @@@ -3240,6 -3231,7 +3233,6 @@@ void diff_flush(struct diff_options *op struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); - diffstat.xm.consume = diffstat_consume; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (check_pair_status(p)) @@@ -3411,10 -3403,7 +3404,7 @@@ static void diffcore_skip_stat_unmatch( void diffcore_std(struct diff_options *options) { - if (DIFF_OPT_TST(options, QUIET)) - return; - - if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER)) + if (options->skip_stat_unmatch) diffcore_skip_stat_unmatch(options); if (options->break_opt != -1) diffcore_break(options->break_opt); diff --combined dir.c index acf1001c4f,cfaa28ff23..1c3c501524 --- a/dir.c +++ b/dir.c @@@ -52,6 -52,11 +52,6 @@@ int common_prefix(const char **pathspec return prefix; } -static inline int special_char(unsigned char c1) -{ - return !c1 || c1 == '*' || c1 == '[' || c1 == '?' || c1 == '\\'; -} - /* * Does 'match' matches the given name? * A match is found if @@@ -75,7 -80,7 +75,7 @@@ static int match_one(const char *match for (;;) { unsigned char c1 = *match; unsigned char c2 = *name; - if (special_char(c1)) + if (isspecial(c1)) break; if (c1 != c2) return 0; @@@ -675,12 -680,17 +675,12 @@@ static int cmp_name(const void *p1, con */ static int simple_length(const char *match) { - const char special[256] = { - [0] = 1, ['?'] = 1, - ['\\'] = 1, ['*'] = 1, - ['['] = 1 - }; int len = -1; for (;;) { unsigned char c = *match++; len++; - if (special[c]) + if (isspecial(c)) return len; } } @@@ -717,12 -727,8 +717,12 @@@ static void free_simplify(struct path_s int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec) { - struct path_simplify *simplify = create_simplify(pathspec); + struct path_simplify *simplify; + + if (has_symlink_leading_path(strlen(path), path)) + return dir->nr; + simplify = create_simplify(pathspec); read_directory_recursive(dir, path, base, baselen, 0, simplify); free_simplify(simplify); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); @@@ -831,3 -837,23 +831,23 @@@ void setup_standard_excludes(struct dir if (excludes_file && !access(excludes_file, R_OK)) add_excludes_from_file(dir, excludes_file); } + + int remove_path(const char *name) + { + char *slash; + + if (unlink(name) && errno != ENOENT) + return -1; + + slash = strrchr(name, '/'); + if (slash) { + char *dirs = xstrdup(name); + slash = dirs + (slash - name); + do { + *slash = '\0'; + } while (rmdir(dirs) && (slash = strrchr(dirs, '/'))); + free(dirs); + } + return 0; + } + diff --combined fast-import.c index ccdf2e57b0,5473cd4d62..ab6689a64d --- a/fast-import.c +++ b/fast-import.c @@@ -376,7 -376,7 +376,7 @@@ static void dump_marks_helper(FILE *, u static void write_crash_report(const char *err) { - char *loc = git_path("fast_import_crash_%d", getpid()); + char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid()); FILE *rpt = fopen(loc, "w"); struct branch *b; unsigned long lu; @@@ -390,8 -390,8 +390,8 @@@ fprintf(stderr, "fast-import: dumping crash report to %s\n", loc); fprintf(rpt, "fast-import crash report:\n"); - fprintf(rpt, " fast-import process: %d\n", getpid()); - fprintf(rpt, " parent process : %d\n", getppid()); + fprintf(rpt, " fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid()); + fprintf(rpt, " parent process : %"PRIuMAX"\n", (uintmax_t) getppid()); fprintf(rpt, " at %s\n", show_date(time(NULL), 0, DATE_LOCAL)); fputc('\n', rpt); @@@ -816,7 -816,7 +816,7 @@@ static void start_packfile(void int pack_fd; snprintf(tmpfile, sizeof(tmpfile), - "%s/tmp_pack_XXXXXX", get_object_directory()); + "%s/pack/tmp_pack_XXXXXX", get_object_directory()); pack_fd = xmkstemp(tmpfile); p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2); strcpy(p->pack_name, tmpfile); @@@ -878,7 -878,7 +878,7 @@@ static char *create_index(void } snprintf(tmpfile, sizeof(tmpfile), - "%s/tmp_idx_XXXXXX", get_object_directory()); + "%s/pack/tmp_idx_XXXXXX", get_object_directory()); idx_fd = xmkstemp(tmpfile); f = sha1fd(idx_fd, tmpfile); sha1write(f, array, 256 * sizeof(int)); diff --combined git-submodule.sh index 1c39b593a6,5888735e4f..c3d0d24bc7 --- a/git-submodule.sh +++ b/git-submodule.sh @@@ -6,10 -6,9 +6,10 @@@ USAGE="[--quiet] [--cached] \ [add [-b branch] ]|[status|init|update [-i|--init]|summary [-n|--summary-limit ] []] \ -[--] [...]" +[--] [...]|[foreach ]|[sync [--] [...]]" OPTIONS_SPEC= . git-sh-setup +. git-parse-remote require_work_tree command= @@@ -31,11 -30,12 +31,11 @@@ say( # Resolve relative url by appending to parent's url resolve_relative_url () { - branch="$(git symbolic-ref HEAD 2>/dev/null)" - remote="$(git config branch.${branch#refs/heads/}.remote)" - remote="${remote:-origin}" + remote=$(get_default_remote) remoteurl=$(git config "remote.$remote.url") || die "remote ($remote) does not have a url defined in .git/config" url="$1" + remoteurl=${remoteurl%/} while test -n "$url" do case "$url" in @@@ -50,16 -50,7 +50,16 @@@ break;; esac done - echo "$remoteurl/$url" + echo "$remoteurl/${url%/}" +} + +# +# Get submodule info for registered submodules +# $@ = path to limit submodule list +# +module_list() +{ + git ls-files --stage -- "$@" | grep '^160000 ' } # @@@ -194,7 -185,7 +194,7 @@@ cmd_add( else module_clone "$path" "$realrepo" || exit - (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) || + (unset GIT_DIR; cd "$path" && git checkout -f -q ${branch:+-b "$branch" "origin/$branch"}) || die "Unable to checkout submodule '$path'" fi @@@ -207,26 -198,6 +207,26 @@@ die "Failed to register submodule '$path'" } +# +# Execute an arbitrary command sequence in each checked out +# submodule +# +# $@ = command to execute +# +cmd_foreach() +{ + module_list | + while read mode sha1 stage path + do + if test -e "$path"/.git + then + say "Entering '$path'" + (cd "$path" && eval "$@") || + die "Stopping at '$path'; script returned non-zero status." + fi + done +} + # # Register submodules in .git/config # @@@ -255,7 -226,7 +255,7 @@@ cmd_init( shift done - git ls-files --stage -- "$@" | grep '^160000 ' | + module_list "$@" | while read mode sha1 stage path do # Skip already registered paths @@@ -313,7 -284,7 +313,7 @@@ cmd_update( esac done - git ls-files --stage -- "$@" | grep '^160000 ' | + module_list "$@" | while read mode sha1 stage path do name=$(module_name "$path") || exit @@@ -340,8 -311,13 +340,13 @@@ if test "$subsha1" != "$sha1" then + force= + if test -z "$subsha1" + then + force="-f" + fi (unset GIT_DIR; cd "$path" && git-fetch && - git-checkout -q "$sha1") || + git-checkout $force -q "$sha1") || die "Unable to checkout '$sha1' in submodule path '$path'" say "Submodule path '$path': checked out '$sha1'" @@@ -578,7 -554,7 +583,7 @@@ cmd_status( shift done - git ls-files --stage -- "$@" | grep '^160000 ' | + module_list "$@" | while read mode sha1 stage path do name=$(module_name "$path") || exit @@@ -602,50 -578,6 +607,50 @@@ fi done } +# +# Sync remote urls for submodules +# This makes the value for remote.$remote.url match the value +# specified in .gitmodules. +# +cmd_sync() +{ + while test $# -ne 0 + do + case "$1" in + -q|--quiet) + quiet=1 + shift + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + done + cd_to_toplevel + module_list "$@" | + while read mode sha1 stage path + do + name=$(module_name "$path") + url=$(git config -f .gitmodules --get submodule."$name".url) + if test -e "$path"/.git + then + ( + unset GIT_DIR + cd "$path" + remote=$(get_default_remote) + say "Synchronizing submodule url for '$name'" + git config remote."$remote".url "$url" + ) + fi + done +} # This loop parses the command line arguments to find the # subcommand name to dispatch. Parsing of the subcommand specific @@@ -656,7 -588,7 +661,7 @@@ while test $# != 0 && test -z "$command" do case "$1" in - add | init | update | status | summary) + add | foreach | init | update | status | summary | sync) command=$1 ;; -q|--quiet) diff --combined git-svn.perl index 2fe7bd3c46,7c7fc39483..af8279acaf --- a/git-svn.perl +++ b/git-svn.perl @@@ -421,15 -421,15 +421,15 @@@ sub cmd_dcommit $head ||= 'HEAD'; my @refs; my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs); + unless ($gs) { + die "Unable to determine upstream SVN information from ", + "$head history.\nPerhaps the repository is empty."; + } $url = defined $_commit_url ? $_commit_url : $gs->full_url; my $last_rev = $_revision if defined $_revision; if ($url) { print "Committing to $url ...\n"; } - unless ($gs) { - die "Unable to determine upstream SVN information from ", - "$head history.\nPerhaps the repository is empty."; - } my ($linear_refs, $parents) = linearize_history($gs, \@refs); if ($_no_rebase && scalar(@$linear_refs) > 1) { warn "Attempting to commit more than one change while ", @@@ -803,28 -803,8 +803,28 @@@ sub cmd_commit_diff } } +sub escape_uri_only { + my ($uri) = @_; + my @tmp; + foreach (split m{/}, $uri) { + s/([^\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; + push @tmp, $_; + } + join('/', @tmp); +} + +sub escape_url { + my ($url) = @_; + if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) { + my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3)); + $url = "$scheme://$domain$uri"; + } + $url; +} + sub cmd_info { my $path = canonicalize_path(defined($_[0]) ? $_[0] : "."); + my $fullpath = canonicalize_path($cmd_dir_prefix . $path); if (exists $_[1]) { die "Too many arguments specified\n"; } @@@ -832,8 -812,8 +832,8 @@@ my ($file_type, $diff_status) = find_file_type_and_diff_status($path); if (!$file_type && !$diff_status) { - print STDERR "$path: (Not a versioned resource)\n\n"; - return; + print STDERR "svn: '$path' is not under version control\n"; + exit 1; } my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); @@@ -845,21 -825,21 +845,21 @@@ # canonicalize_path() will return "" to make libsvn 1.5.x happy, $path = "." if $path eq ""; - my $full_url = $url . ($path eq "." ? "" : "/$path"); + my $full_url = $url . ($fullpath eq "" ? "" : "/$fullpath"); if ($_url) { - print $full_url, "\n"; + print escape_url($full_url), "\n"; return; } my $result = "Path: $path\n"; $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir"; - $result .= "URL: " . $full_url . "\n"; + $result .= "URL: " . escape_url($full_url) . "\n"; eval { my $repos_root = $gs->repos_root; Git::SVN::remove_username($repos_root); - $result .= "Repository Root: $repos_root\n"; + $result .= "Repository Root: " . escape_url($repos_root) . "\n"; }; if ($@) { $result .= "Repository Root: (offline)\n"; @@@ -881,7 -861,7 +881,7 @@@ } my ($lc_author, $lc_rev, $lc_date_utc); - my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path); + my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $fullpath); my $log = command_output_pipe(@args); my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/; while (<$log>) { @@@ -3400,12 -3380,11 +3400,12 @@@ sub generate_diff while (<$diff_fh>) { chomp $_; # this gets rid of the trailing "\0" if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s - $::sha1\s($::sha1)\s + ($::sha1)\s($::sha1)\s ([MTCRAD])\d*$/xo) { push @mods, { mode_a => $1, mode_b => $2, - sha1_b => $3, chg => $4 }; - if ($4 =~ /^(?:C|R)$/) { + sha1_a => $3, sha1_b => $4, + chg => $5 }; + if ($5 =~ /^(?:C|R)$/) { $state = 'file_a'; } else { $state = 'file_b'; @@@ -3657,7 -3636,6 +3657,7 @@@ sub R my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, $self->url_path($m->{file_a}), $self->{r}); print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q; + $self->apply_autoprops($file, $fbat); $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); @@@ -3684,52 -3662,33 +3684,52 @@@ sub change_file_prop $self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool}); } -sub chg_file { - my ($self, $fbat, $m) = @_; - if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) { - $self->change_file_prop($fbat,'svn:executable','*'); - } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) { - $self->change_file_prop($fbat,'svn:executable',undef); - } - my $fh = Git::temp_acquire('git_blob'); - if ($m->{mode_b} =~ /^120/) { +sub _chg_file_get_blob ($$$$) { + my ($self, $fbat, $m, $which) = @_; + my $fh = Git::temp_acquire("git_blob_$which"); + if ($m->{"mode_$which"} =~ /^120/) { print $fh 'link ' or croak $!; $self->change_file_prop($fbat,'svn:special','*'); - } elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) { + } elsif ($m->{mode_a} =~ /^120/ && $m->{"mode_$which"} !~ /^120/) { $self->change_file_prop($fbat,'svn:special',undef); } - my $size = $::_repository->cat_blob($m->{sha1_b}, $fh); - croak "Failed to read object $m->{sha1_b}" if ($size < 0); + my $blob = $m->{"sha1_$which"}; + return ($fh,) if ($blob =~ /^0{40}$/); + my $size = $::_repository->cat_blob($blob, $fh); + croak "Failed to read object $blob" if ($size < 0); $fh->flush == 0 or croak $!; seek $fh, 0, 0 or croak $!; my $exp = ::md5sum($fh); seek $fh, 0, 0 or croak $!; + return ($fh, $exp); +} +sub chg_file { + my ($self, $fbat, $m) = @_; + if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) { + $self->change_file_prop($fbat,'svn:executable','*'); + } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) { + $self->change_file_prop($fbat,'svn:executable',undef); + } + my ($fh_a, $exp_a) = _chg_file_get_blob $self, $fbat, $m, 'a'; + my ($fh_b, $exp_b) = _chg_file_get_blob $self, $fbat, $m, 'b'; my $pool = SVN::Pool->new; - my $atd = $self->apply_textdelta($fbat, undef, $pool); - my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool); - die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp); - Git::temp_release($fh, 1); + my $atd = $self->apply_textdelta($fbat, $exp_a, $pool); + if (-s $fh_a) { + my $txstream = SVN::TxDelta::new ($fh_a, $fh_b, $pool); + my $res = SVN::TxDelta::send_txstream($txstream, @$atd, $pool); + if (defined $res) { + die "Unexpected result from send_txstream: $res\n", + "(SVN::Core::VERSION: $SVN::Core::VERSION)\n"; + } + } else { + my $got = SVN::TxDelta::send_stream($fh_b, @$atd, $pool); + die "Checksum mismatch\nexpected: $exp_b\ngot: $got\n" + if ($got ne $exp_b); + } + Git::temp_release($fh_b, 1); + Git::temp_release($fh_a, 1); $pool->clear; } @@@ -4010,21 -3969,21 +4010,21 @@@ sub gs_do_switch my $old_url = $full_url; $full_url .= '/' . escape_uri_only($path) if length $path; my ($ra, $reparented); - if ($old_url ne $full_url) { - if ($old_url !~ m#^svn(\+ssh)?://#) { - SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, - $pool); - $self->{url} = $full_url; - $reparented = 1; - } else { - $_[0] = undef; - $self = undef; - $RA = undef; - $ra = Git::SVN::Ra->new($full_url); - $ra_invalid = 1; - } + + if ($old_url =~ m#^svn(\+ssh)?://#) { + $_[0] = undef; + $self = undef; + $RA = undef; + $ra = Git::SVN::Ra->new($full_url); + $ra_invalid = 1; + } elsif ($old_url ne $full_url) { + SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool); + $self->{url} = $full_url; + $reparented = 1; } + $ra ||= $self; + $url_b = escape_url($url_b); my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool); my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : (); $reporter->set_path('', $rev_a, 0, @lock, $pool); diff --combined gitweb/gitweb.perl index 29e21564c8,269f1125d9..da474d082c --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@@ -1090,23 -1090,13 +1090,23 @@@ sub format_log_line_html } # format marker of refs pointing to given object + +# the destination action is chosen based on object type and current context: +# - for annotated tags, we choose the tag view unless it's the current view +# already, in which case we go to shortlog view +# - for other refs, we keep the current view if we're in history, shortlog or +# log view, and select shortlog otherwise sub format_ref_marker { my ($refs, $id) = @_; my $markers = ''; if (defined $refs->{$id}) { foreach my $ref (@{$refs->{$id}}) { + # this code exploits the fact that non-lightweight tags are the + # only indirect objects, and that they are the only objects for which + # we want to use tag instead of shortlog as action my ($type, $name) = qw(); + my $indirect = ($ref =~ s/\^\{\}$//); # e.g. tags/v2.6.11 or heads/next if ($ref =~ m!^(.*?)s?/(.*)$!) { $type = $1; @@@ -1116,29 -1106,8 +1116,29 @@@ $name = $ref; } - $markers .= " " . - esc_html($name) . ""; + my $class = $type; + $class .= " indirect" if $indirect; + + my $dest_action = "shortlog"; + + if ($indirect) { + $dest_action = "tag" unless $action eq "tag"; + } elsif ($action =~ /^(history|(short)?log)$/) { + $dest_action = $action; + } + + my $dest = ""; + $dest .= "refs/" unless $ref =~ m!^refs/!; + $dest .= $ref; + + my $link = $cgi->a({ + -href => href( + action=>$dest_action, + hash=>$dest + )}, $name); + + $markers .= " " . + $link . ""; } } @@@ -1949,7 -1918,7 +1949,7 @@@ sub git_get_references while (my $line = <$fd>) { chomp $line; - if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type/?[^^]+)!) { + if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) { if (defined $refs{$1}) { push @{$refs{$1}}, $2; } else { @@@ -2123,7 -2092,7 +2123,7 @@@ sub parse_commit_text last; } } - if ($co{'title'} eq "") { + if (! defined $co{'title'} || $co{'title'} eq "") { $co{'title'} = $co{'title_short'} = '(no commit message)'; } # remove added spaces diff --combined read-cache.c index 5150c1e14b,8f96fd1722..5b1b3ad03b --- a/read-cache.c +++ b/read-cache.c @@@ -8,11 -8,6 +8,11 @@@ #include "cache-tree.h" #include "refs.h" #include "dir.h" +#include "tree.h" +#include "commit.h" +#include "diff.h" +#include "diffcore.h" +#include "revision.h" /* Index extensions. * @@@ -1123,10 -1118,6 +1123,10 @@@ static void convert_from_disk(struct on ce->ce_size = ntohl(ondisk->size); /* On-disk flags are just 16 bits */ ce->ce_flags = ntohs(ondisk->flags); + + /* For future extension: we do not understand this entry yet */ + if (ce->ce_flags & CE_EXTENDED) + die("Unknown index entry format"); hashcpy(ce->sha1, ondisk->sha1); len = ce->ce_flags & CE_NAMEMASK; @@@ -1253,6 -1244,7 +1253,7 @@@ int discard_index(struct index_state *i istate->cache_nr = 0; istate->cache_changed = 0; istate->timestamp = 0; + istate->name_hash_initialized = 0; free_hash(&istate->name_hash); cache_tree_free(&(istate->cache_tree)); free(istate->alloc); @@@ -1488,59 -1480,3 +1489,59 @@@ int read_index_unmerged(struct index_st istate->cache_nr = dst - istate->cache; return !!last; } + +struct update_callback_data +{ + int flags; + int add_errors; +}; + +static void update_callback(struct diff_queue_struct *q, + struct diff_options *opt, void *cbdata) +{ + int i; + struct update_callback_data *data = cbdata; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + const char *path = p->one->path; + switch (p->status) { + default: + die("unexpected diff status %c", p->status); + case DIFF_STATUS_UNMERGED: + case DIFF_STATUS_MODIFIED: + case DIFF_STATUS_TYPE_CHANGED: + if (add_file_to_index(&the_index, path, data->flags)) { + if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) + die("updating files failed"); + data->add_errors++; + } + break; + case DIFF_STATUS_DELETED: + if (data->flags & ADD_CACHE_IGNORE_REMOVAL) + break; + if (!(data->flags & ADD_CACHE_PRETEND)) + remove_file_from_index(&the_index, path); + if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) + printf("remove '%s'\n", path); + break; + } + } +} + +int add_files_to_cache(const char *prefix, const char **pathspec, int flags) +{ + struct update_callback_data data; + struct rev_info rev; + init_revisions(&rev, prefix); + setup_revisions(0, NULL, &rev, NULL); + rev.prune_data = pathspec; + rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = update_callback; + data.flags = flags; + data.add_errors = 0; + rev.diffopt.format_callback_data = &data; + run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); + return !!data.add_errors; +} + diff --combined sha1_file.c index 9ee1ed16ad,e2cb342a32..aec81bbae7 --- a/sha1_file.c +++ b/sha1_file.c @@@ -2136,7 -2136,9 +2136,9 @@@ static void write_sha1_file_prepare(con */ int move_temp_to_file(const char *tmpfile, const char *filename) { - int ret = link(tmpfile, filename); + int ret = 0; + if (link(tmpfile, filename)) + ret = errno; /* * Coda hack - coda doesn't like cross-directory links, @@@ -2361,22 -2363,51 +2363,22 @@@ int has_sha1_file(const unsigned char * return has_loose_object(sha1); } -int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +static int index_mem(unsigned char *sha1, void *buf, size_t size, + int write_object, enum object_type type, const char *path) { - struct strbuf buf; - int ret; - - strbuf_init(&buf, 0); - if (strbuf_read(&buf, fd, 4096) < 0) { - strbuf_release(&buf); - return -1; - } - - if (!type) - type = blob_type; - if (write_object) - ret = write_sha1_file(buf.buf, buf.len, type, sha1); - else - ret = hash_sha1_file(buf.buf, buf.len, type, sha1); - strbuf_release(&buf); - - return ret; -} - -int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, - enum object_type type, const char *path) -{ - size_t size = xsize_t(st->st_size); - void *buf = NULL; int ret, re_allocated = 0; - if (size) - buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); - if (!type) type = OBJ_BLOB; /* * Convert blobs to git internal format */ - if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) { + if ((type == OBJ_BLOB) && path) { struct strbuf nbuf; strbuf_init(&nbuf, 0); if (convert_to_git(path, buf, size, &nbuf, write_object ? safe_crlf : 0)) { - munmap(buf, size); buf = strbuf_detach(&nbuf, &size); re_allocated = 1; } @@@ -2386,33 -2417,12 +2388,33 @@@ ret = write_sha1_file(buf, size, typename(type), sha1); else ret = hash_sha1_file(buf, size, typename(type), sha1); - if (re_allocated) { + if (re_allocated) free(buf); - return ret; - } - if (size) + return ret; +} + +int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, + enum object_type type, const char *path) +{ + int ret; + size_t size = xsize_t(st->st_size); + + if (!S_ISREG(st->st_mode)) { + struct strbuf sbuf; + strbuf_init(&sbuf, 0); + if (strbuf_read(&sbuf, fd, 4096) >= 0) + ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object, + type, path); + else + ret = -1; + strbuf_release(&sbuf); + } else if (size) { + void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + ret = index_mem(sha1, buf, size, write_object, type, path); munmap(buf, size); + } else + ret = index_mem(sha1, NULL, size, write_object, type, path); + close(fd); return ret; }