diff options
47 files changed, 2076 insertions, 302 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index c5e094a9c4..0865f4e01a 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -420,6 +420,11 @@ branch.<name>.rebase:: it unless you understand the implications (see linkgit:git-rebase[1] for details). +browser.<tool>.cmd:: + Specify the command to invoke the specified browser. The + specified command is evaluated in shell with the URLs passed + as arguments. (See linkgit:git-web--browse[1].) + browser.<tool>.path:: Override the path for the given tool that may be used to browse HTML help (see '-w' option in linkgit:git-help[1]) or a @@ -590,6 +595,10 @@ gc.packrefs:: at some stage, and setting this to `false` will continue to prevent `git pack-refs` from being run from `git gc`. +gc.pruneexpire:: + When `git gc` is run, it will call `prune --expire 2.weeks.ago`. + Override the grace period with this config variable. + gc.reflogexpire:: `git reflog expire` removes reflog entries older than this time; defaults to 90 days. @@ -748,6 +757,10 @@ log.showroot:: Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which normally hide the root commit will now show it. True by default. +man.viewer:: + Specify the programs that may be used to display help in the + 'man' format. See linkgit:git-help[1]. + merge.summary:: Whether to include summaries of merged commits in newly created merge commit messages. False by default. @@ -860,7 +873,7 @@ pack.indexVersion:: whenever the corresponding pack is larger than 2 GB. Otherwise the default is 1. -pack.packSizeLimit: +pack.packSizeLimit:: The default maximum size of a pack. This setting only affects packing to a file, i.e. the git:// protocol is unaffected. It can be overridden by the `\--max-pack-size` option of diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 47799097ce..c751a17d07 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -207,16 +207,14 @@ patch:: and the working tree file and asks you if you want to stage the change of each hunk. You can say: - y - add the change from that hunk to index - n - do not add the change from that hunk to index - a - add the change from that hunk and all the rest to index - d - do not the change from that hunk nor any of the rest to index - j - do not decide on this hunk now, and view the next - undecided hunk - J - do not decide on this hunk now, and view the next hunk - k - do not decide on this hunk now, and view the previous - undecided hunk - K - do not decide on this hunk now, and view the previous hunk + y - stage this hunk + n - do not stage this hunk + a - stage this and all the remaining hunks in the file + d - do not stage this hunk nor any of the remaining hunks in the file + j - leave this hunk undecided, see next undecided hunk + J - leave this hunk undecided, see next hunk + k - leave this hunk undecided, see previous undecided hunk + K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks ? - print help + diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index 2e7be916aa..229a7c9b30 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- -'git-gc' [--prune] [--aggressive] [--auto] [--quiet] +'git-gc' [--aggressive] [--auto] [--quiet] DESCRIPTION ----------- @@ -25,17 +25,6 @@ operating performance. Some git commands may automatically run OPTIONS ------- ---prune:: - Usually `git-gc` packs refs, expires old reflog entries, - packs loose objects, - and removes old 'rerere' records. Removal - of unreferenced loose objects is an unsafe operation - while other git operations are in progress, so it is not - done by default. Pass this option if you want it, and only - when you know nobody else is creating new objects in the - repository at the same time (e.g. never use this option - in a cron script). - --aggressive:: Usually 'git-gc' runs very quickly while providing good disk space utilization and performance. This option will cause @@ -104,6 +93,10 @@ the value, the more time is spent optimizing the delta compression. See the documentation for the --window' option in linkgit:git-repack[1] for more details. This defaults to 10. +The optional configuration variable 'gc.pruneExpire' controls how old +the unreferenced loose objects have to be before they are pruned. The +default is "2 weeks ago". + See Also -------- linkgit:git-prune[1] diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index 0926dc12ba..be2ae53b90 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -33,17 +33,21 @@ OPTIONS option supersedes any other option. -i|--info:: - Use the 'info' program to display the manual page, instead of - the 'man' program that is used by default. + Display manual page for the command in the 'info' format. The + 'info' program will be used for that purpose. -m|--man:: - Use the 'man' program to display the manual page. This may be - used to override a value set in the 'help.format' - configuration variable. + Display manual page for the command in the 'man' format. This + option may be used to override a value set in the + 'help.format' configuration variable. ++ +By default the 'man' program will be used to display the manual page, +but the 'man.viewer' configuration variable may be used to choose +other display programs (see below). -w|--web:: - Use a web browser to display the HTML manual page, instead of - the 'man' program that is used by default. + Display manual page for the command in the 'web' (HTML) + format. A web browser will be used for that purpose. + The web browser can be specified using the configuration variable 'help.browser', or 'web.browser' if the former is not set. If none of @@ -54,6 +58,9 @@ linkgit:git-web--browse[1] for more information about this. CONFIGURATION VARIABLES ----------------------- +help.format +~~~~~~~~~~~ + If no command line option is passed, the 'help.format' configuration variable will be checked. The following values are supported for this variable; they make 'git-help' behave as their corresponding command @@ -61,15 +68,47 @@ line option: * "man" corresponds to '-m|--man', * "info" corresponds to '-i|--info', -* "web" or "html" correspond to '-w|--web', +* "web" or "html" correspond to '-w|--web'. + +help.browser, web.browser and browser.<tool>.path +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also be checked if the 'web' format is chosen (either by command line option or configuration variable). See '-w|--web' in the OPTIONS section above and linkgit:git-web--browse[1]. -Note that these configuration variables should probably be set using -the '--global' flag, for example like this: +man.viewer +~~~~~~~~~~ + +The 'man.viewer' config variable will be checked if the 'man' format +is chosen. Only the following values are currently supported: + +* "man": use the 'man' program as usual, +* "woman": use 'emacsclient' to launch the "woman" mode in emacs +(this only works starting with emacsclient versions 22), +* "konqueror": use a man KIO slave in konqueror. + +Multiple values may be given to this configuration variable. Their +corresponding programs will be tried in the order listed in the +configuration file. + +For example, this configuration: + + [man] + viewer = konqueror + viewer = woman + +will try to use konqueror first. But this may fail (for example if +DISPLAY is not set) and in that case emacs' woman mode will be tried. + +If everything fails the 'man' program will be tried anyway. + +Note about git config --global +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Note that all these configuration variables should probably be set +using the '--global' flag, for example like this: ------------------------------------------------ $ git config --global help.format web diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index b4d01605a5..41f9f63566 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -12,6 +12,7 @@ SYNOPSIS 'git-submodule' [--quiet] add [-b branch] [--] <repository> [<path>] 'git-submodule' [--quiet] status [--cached] [--] [<path>...] 'git-submodule' [--quiet] [init|update] [--] [<path>...] +'git-submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...] COMMANDS @@ -47,6 +48,11 @@ update:: checkout the commit specified in the index of the containing repository. This will make the submodules HEAD be detached. +summary:: + Show commit summary between the given commit (defaults to HEAD) and + working tree/index. For a submodule in question, a series of commits + in the submodule between the given super project commit and the + index or working tree (switched by --cached) are shown. OPTIONS ------- @@ -57,9 +63,16 @@ OPTIONS Branch of repository to add as submodule. --cached:: - Display the SHA-1 stored in the index, not the SHA-1 of the currently - checked out submodule commit. This option is only valid for the - status command. + This option is only valid for status and summary commands. These + commands typically use the commit found in the submodule HEAD, but + with this option, the commit stored in the index is used instead. + +-n, --summary-limit:: + This option is only valid for the summary command. + Limit the summary size (number of commits shown in total). + Giving 0 will disable the summary; a negative number means unlimted + (the default). This limit only applies to modified submodules. The + size is always limited to 1 for added/deleted/typechanged submodules. <path>:: Path to submodule(s). When specified this will restrict the command diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt index df57d010e5..ddbae5b194 100644 --- a/Documentation/git-web--browse.txt +++ b/Documentation/git-web--browse.txt @@ -27,6 +27,8 @@ The following browsers (or commands) are currently supported: * dillo * open (this is the default under Mac OS X GUI) +Custom commands may also be specified. + OPTIONS ------- -b BROWSER|--browser=BROWSER:: @@ -43,16 +45,35 @@ OPTIONS CONFIGURATION VARIABLES ----------------------- +CONF.VAR (from -c option) and web.browser +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The web browser can be specified using a configuration variable passed with the -c (or --config) command line option, or the 'web.browser' configuration variable if the former is not used. +browser.<tool>.path +~~~~~~~~~~~~~~~~~~~ + You can explicitly provide a full path to your preferred browser by setting the configuration variable 'browser.<tool>.path'. For example, you can configure the absolute path to firefox by setting 'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool is available in PATH. +browser.<tool>.cmd +~~~~~~~~~~~~~~~~~~ + +When the browser, specified by options or configuration variables, is +not among the supported ones, then the corresponding +'browser.<tool>.cmd' configuration variable will be looked up. If this +variable exists then "git web--browse" will treat the specified tool +as a custom command and will use a shell eval to run the command with +the URLs passed as arguments. + +Note about git config --global +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Note that these configuration variables should probably be set using the '--global' flag, for example like this: diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl index fe3cd72d6f..4065a3a27a 100644 --- a/Documentation/manpage-1.72.xsl +++ b/Documentation/manpage-1.72.xsl @@ -1,5 +1,9 @@ -<!-- callout.xsl: converts asciidoc callouts to man page format --> +<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + +<xsl:param name="man.output.quietly" select="1"/> +<xsl:param name="refentry.meta.get.quietly" select="1"/> + <xsl:template match="co"> <xsl:value-of select="concat('▓fB(',substring-after(@id,'-'),')▓fR')"/> </xsl:template> diff --git a/builtin-fetch.c b/builtin-fetch.c index 55f611e3c2..b2b9935ed6 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -40,6 +40,8 @@ static struct option builtin_fetch_options[] = { "force overwrite of local branch"), OPT_SET_INT('t', "tags", &tags, "fetch all tags and associated objects", TAGS_SET), + OPT_SET_INT('n', NULL, &tags, + "do not fetch all tags (--no-tags)", TAGS_UNSET), OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"), OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, "allow updating of HEAD ref"), diff --git a/builtin-gc.c b/builtin-gc.c index 045bf0e487..95917d74a8 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -26,12 +26,13 @@ static int pack_refs = 1; static int aggressive_window = -1; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 20; +static char *prune_expire = "2.weeks.ago"; #define MAX_ADD 10 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL}; static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL}; -static const char *argv_prune[] = {"prune", NULL}; +static const char *argv_prune[] = {"prune", "--expire", NULL, NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; static int gc_config(const char *var, const char *value) @@ -55,6 +56,17 @@ static int gc_config(const char *var, const char *value) gc_auto_pack_limit = git_config_int(var, value); return 0; } + if (!strcmp(var, "gc.pruneexpire")) { + if (!value) + return config_error_nonbool(var); + if (strcmp(value, "now")) { + unsigned long now = approxidate("now"); + if (approxidate(value) >= now) + return error("Invalid %s: '%s'", var, value); + } + prune_expire = xstrdup(value); + return 0; + } return git_default_config(var, value); } @@ -234,7 +246,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (run_command_v_opt(argv_repack, RUN_GIT_CMD)) return error(FAILED_RUN, argv_repack[0]); - if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD)) + argv_prune[2] = prune_expire; + if (run_command_v_opt(argv_prune, RUN_GIT_CMD)) return error(FAILED_RUN, argv_prune[0]); if (run_command_v_opt(argv_rerere, RUN_GIT_CMD)) diff --git a/builtin-log.c b/builtin-log.c index d983cbc7bc..5c00725f03 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -662,6 +662,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, int i; const char *encoding = "utf-8"; struct diff_options opts; + int need_8bit_cte = 0; if (rev->commit_format != CMIT_FMT_EMAIL) die("Cover letter needs email format"); @@ -672,7 +673,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, head_sha1 = sha1_to_hex(head->object.sha1); - log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers); + log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers, + &need_8bit_cte); committer = git_committer_info(0); @@ -681,7 +683,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822, encoding); pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers, - encoding, 0); + encoding, need_8bit_cte); pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0); printf("%s\n", sb.buf); diff --git a/builtin-merge-file.c b/builtin-merge-file.c index adce6d4635..3605960c2d 100644 --- a/builtin-merge-file.c +++ b/builtin-merge-file.c @@ -57,7 +57,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) if (!f) ret = error("Could not open %s for writing", filename); - else if (fwrite(result.ptr, result.size, 1, f) != 1) + else if (result.size && + fwrite(result.ptr, result.size, 1, f) != 1) ret = error("Could not write to %s", filename); else if (fclose(f)) ret = error("Could not close %s", filename); diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 5c7fbb2599..910c0d20e7 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -668,9 +668,20 @@ static struct merge_file_info merge_file(struct diff_filespec *o, if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1)) result.merge = 1; - result.mode = a->mode == o->mode ? b->mode: a->mode; + /* + * Merge modes + */ + if (a->mode == b->mode || a->mode == o->mode) + result.mode = b->mode; + else { + result.mode = a->mode; + if (b->mode != o->mode) { + result.clean = 0; + result.merge = 1; + } + } - if (sha_eq(a->sha1, o->sha1)) + if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, o->sha1)) hashcpy(result.sha, b->sha1); else if (sha_eq(b->sha1, o->sha1)) hashcpy(result.sha, a->sha1); diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index f504cff756..777f272668 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -454,6 +454,7 @@ static void write_pack_file(void) struct pack_header hdr; int do_progress = progress >> pack_to_stdout; uint32_t nr_remaining = nr_result; + time_t last_mtime = 0; if (do_progress) progress_state = start_progress("Writing objects", nr_result); @@ -504,6 +505,7 @@ static void write_pack_file(void) if (!pack_to_stdout) { mode_t mode = umask(0); + struct stat st; char *idx_tmp_name, tmpname[PATH_MAX]; umask(mode); @@ -511,6 +513,7 @@ static void write_pack_file(void) idx_tmp_name = write_idx_file(NULL, written_list, nr_written, sha1); + snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", base_name, sha1_to_hex(sha1)); if (adjust_perm(pack_tmp_name, mode)) @@ -519,6 +522,28 @@ static void write_pack_file(void) if (rename(pack_tmp_name, tmpname)) die("unable to rename temporary pack file: %s", strerror(errno)); + + /* + * Packs are runtime accessed in their mtime + * order since newer packs are more likely to contain + * younger objects. So if we are creating multiple + * packs then we should modify the mtime of later ones + * to preserve this property. + */ + if (stat(tmpname, &st) < 0) { + warning("failed to stat %s: %s", + tmpname, strerror(errno)); + } else if (!last_mtime) { + last_mtime = st.st_mtime; + } else { + struct utimbuf utb; + utb.actime = st.st_atime; + utb.modtime = --last_mtime; + if (utime(tmpname, &utb) < 0) + warning("failed utime() on %s: %s", + tmpname, strerror(errno)); + } + snprintf(tmpname, sizeof(tmpname), "%s-%s.idx", base_name, sha1_to_hex(sha1)); if (adjust_perm(idx_tmp_name, mode)) @@ -527,6 +552,7 @@ static void write_pack_file(void) if (rename(idx_tmp_name, tmpname)) die("unable to rename temporary index file: %s", strerror(errno)); + free(idx_tmp_name); free(pack_tmp_name); puts(sha1_to_hex(sha1)); diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 160456dad1..e9cfd2bbc5 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -13,16 +13,15 @@ #include "dir.h" #include "builtin.h" -#define MAX_TREES 8 static int nr_trees; -static struct tree *trees[MAX_TREES]; +static struct tree *trees[MAX_UNPACK_TREES]; static int list_tree(unsigned char *sha1) { struct tree *tree; - if (nr_trees >= MAX_TREES) - die("I cannot read more than %d trees", MAX_TREES); + if (nr_trees >= MAX_UNPACK_TREES) + die("I cannot read more than %d trees", MAX_UNPACK_TREES); tree = parse_tree_indirect(sha1); if (!tree) return -1; @@ -97,7 +96,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, newfd, stage = 0; unsigned char sha1[20]; - struct tree_desc t[MAX_TREES]; + struct tree_desc t[MAX_UNPACK_TREES]; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); diff --git a/builtin-shortlog.c b/builtin-shortlog.c index b22b0edd65..bd795b1db7 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -229,7 +229,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) { struct shortlog log; struct rev_info rev; + int nongit; + prefix = setup_git_directory_gently(&nongit); shortlog_init(&log); /* since -n is a shadowed rev argument, parse our args first */ @@ -259,7 +261,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) die ("unrecognized argument: %s", argv[1]); /* assume HEAD if from a tty */ - if (!rev.pending.nr && isatty(0)) + if (!nongit && !rev.pending.nr && isatty(0)) add_head_to_pending(&rev); if (rev.pending.nr == 0) { read_from_stdin(&log); @@ -70,7 +70,7 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, struct strbuf *, int abbrev, const char *subject, const char *after_subject, enum date_mode, - int non_ascii_present); + int need_8bit_cte); void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding); @@ -80,7 +80,7 @@ void pp_title_line(enum cmit_fmt fmt, const char *subject, const char *after_subject, const char *encoding, - int plain_non_ascii); + int need_8bit_cte); void pp_remainder(enum cmit_fmt fmt, const char **msg_p, struct strbuf *sb, diff --git a/config.mak.in b/config.mak.in index 8e1cd5f03d..7868dfd93a 100644 --- a/config.mak.in +++ b/config.mak.in @@ -46,4 +46,5 @@ NO_MKDTEMP=@NO_MKDTEMP@ NO_ICONV=@NO_ICONV@ OLD_ICONV=@OLD_ICONV@ NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@ +FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@ SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@ diff --git a/configure.ac b/configure.ac index 287149d304..82584e9153 100644 --- a/configure.ac +++ b/configure.ac @@ -327,6 +327,26 @@ else fi AC_SUBST(NO_C99_FORMAT) # +# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds +# when attempting to read from an fopen'ed directory. +AC_CACHE_CHECK([whether system succeeds to read fopen'ed directory], + [ac_cv_fread_reads_directories], +[ +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], + [[char c; + FILE *f = fopen(".", "r"); + return f && fread(&c, 1, 1, f)]])], + [ac_cv_fread_reads_directories=no], + [ac_cv_fread_reads_directories=yes]) +]) +if test $ac_cv_fread_reads_directories = yes; then + FREAD_READS_DIRECTORIES=UnfortunatelyYes +else + FREAD_READS_DIRECTORIES= +fi +AC_SUBST(FREAD_READS_DIRECTORIES) +# # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf() # or vsnprintf() return -1 instead of number of characters which would # have been written to the final string if enough space had been available. diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 2838546d16..7959eab902 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -63,7 +63,7 @@ mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" # create the links to the original repo. explictly exclude index, HEAD and # logs/HEAD from the list since they are purely related to the current working # directory, and should not be shared. -for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache +for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn do case $x in */*) @@ -256,40 +256,41 @@ static int count_lines(const char *data, int size) return count; } -static void print_line_count(int count) +static void print_line_count(FILE *file, int count) { switch (count) { case 0: - printf("0,0"); + fprintf(file, "0,0"); break; case 1: - printf("1"); + fprintf(file, "1"); break; default: - printf("1,%d", count); + fprintf(file, "1,%d", count); break; } } -static void copy_file_with_prefix(int prefix, const char *data, int size, +static void copy_file_with_prefix(FILE *file, + int prefix, const char *data, int size, const char *set, const char *reset) { int ch, nl_just_seen = 1; while (0 < size--) { ch = *data++; if (nl_just_seen) { - fputs(set, stdout); - putchar(prefix); + fputs(set, file); + putc(prefix, file); } if (ch == '\n') { nl_just_seen = 1; - fputs(reset, stdout); + fputs(reset, file); } else nl_just_seen = 0; - putchar(ch); + putc(ch, file); } if (!nl_just_seen) - printf("%s\n\\ No newline at end of file\n", reset); + fprintf(file, "%s\n\\ No newline at end of file\n", reset); } static void emit_rewrite_diff(const char *name_a, @@ -322,17 +323,18 @@ static void emit_rewrite_diff(const char *name_a, diff_populate_filespec(two, 0); lc_a = count_lines(one->data, one->size); lc_b = count_lines(two->data, two->size); - printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", - metainfo, a_name.buf, name_a_tab, reset, - metainfo, b_name.buf, name_b_tab, reset, fraginfo); - print_line_count(lc_a); - printf(" +"); - print_line_count(lc_b); - printf(" @@%s\n", reset); + fprintf(o->file, + "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", + metainfo, a_name.buf, name_a_tab, reset, + metainfo, b_name.buf, name_b_tab, reset, fraginfo); + print_line_count(o->file, lc_a); + fprintf(o->file, " +"); + print_line_count(o->file, lc_b); + fprintf(o->file, " @@%s\n", reset); if (lc_a) - copy_file_with_prefix('-', one->data, one->size, old, reset); + copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset); if (lc_b) - copy_file_with_prefix('+', two->data, two->size, new, reset); + copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset); } static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) @@ -372,9 +374,10 @@ static void diff_words_append(char *line, unsigned long len, struct diff_words_data { struct xdiff_emit_state xm; struct diff_words_buffer minus, plus; + FILE *file; }; -static void print_word(struct diff_words_buffer *buffer, int len, int color, +static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color, int suppress_newline) { const char *ptr; @@ -391,15 +394,15 @@ static void print_word(struct diff_words_buffer *buffer, int len, int color, len--; } - fputs(diff_get_color(1, color), stdout); - fwrite(ptr, len, 1, stdout); - fputs(diff_get_color(1, DIFF_RESET), stdout); + fputs(diff_get_color(1, color), file); + fwrite(ptr, len, 1, file); + fputs(diff_get_color(1, DIFF_RESET), file); if (eol) { if (suppress_newline) buffer->suppressed_newline = 1; else - putchar('\n'); + putc('\n', file); } } @@ -409,20 +412,23 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) if (diff_words->minus.suppressed_newline) { if (line[0] != '+') - putchar('\n'); + putc('\n', diff_words->file); diff_words->minus.suppressed_newline = 0; } len--; switch (line[0]) { case '-': - print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1); + print_word(diff_words->file, + &diff_words->minus, len, DIFF_FILE_OLD, 1); break; case '+': - print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0); + print_word(diff_words->file, + &diff_words->plus, len, DIFF_FILE_NEW, 0); break; case ' ': - print_word(&diff_words->plus, len, DIFF_PLAIN, 0); + print_word(diff_words->file, + &diff_words->plus, len, DIFF_PLAIN, 0); diff_words->minus.current += len; break; } @@ -466,7 +472,7 @@ static void diff_words_show(struct diff_words_data *diff_words) diff_words->minus.text.size = diff_words->plus.text.size = 0; if (diff_words->minus.suppressed_newline) { - putchar('\n'); + putc('\n', diff_words->file); diff_words->minus.suppressed_newline = 0; } } @@ -481,6 +487,7 @@ struct emit_callback { const char **label_path; struct diff_words_data *diff_words; int *found_changesp; + FILE *file; }; static void free_diff_words_data(struct emit_callback *ecbdata) @@ -505,11 +512,11 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) return ""; } -static void emit_line(const char *set, const char *reset, const char *line, int len) +static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len) { - fputs(set, stdout); - fwrite(line, len, 1, stdout); - fputs(reset, stdout); + fputs(set, file); + fwrite(line, len, 1, file); + fputs(reset, file); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) @@ -518,13 +525,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW); if (!*ws) - emit_line(set, reset, line, len); + emit_line(ecbdata->file, set, reset, line, len); else { /* Emit just the prefix, then the rest. */ - emit_line(set, reset, line, ecbdata->nparents); + emit_line(ecbdata->file, set, reset, line, ecbdata->nparents); (void)check_and_emit_line(line + ecbdata->nparents, len - ecbdata->nparents, ecbdata->ws_rule, - stdout, set, reset, ws); + ecbdata->file, set, reset, ws); } } @@ -563,10 +570,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : ""; name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : ""; - printf("%s--- %s%s%s\n", - meta, ecbdata->label_path[0], reset, name_a_tab); - printf("%s+++ %s%s%s\n", - meta, ecbdata->label_path[1], reset, name_b_tab); + fprintf(ecbdata->file, "%s--- %s%s%s\n", + meta, ecbdata->label_path[0], reset, name_a_tab); + fprintf(ecbdata->file, "%s+++ %s%s%s\n", + meta, ecbdata->label_path[1], reset, name_b_tab); ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } @@ -578,15 +585,16 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) if (2 <= i && i < len && line[i] == ' ') { ecbdata->nparents = i - 1; len = sane_truncate_line(ecbdata, line, len); - emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO), + emit_line(ecbdata->file, + diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO), reset, line, len); if (line[len-1] != '\n') - putchar('\n'); + putc('\n', ecbdata->file); return; } if (len < ecbdata->nparents) { - emit_line(reset, reset, line, len); + emit_line(ecbdata->file, reset, reset, line, len); return; } @@ -609,7 +617,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) diff_words_show(ecbdata->diff_words); line++; len--; - emit_line(plain, reset, line, len); + emit_line(ecbdata->file, plain, reset, line, len); return; } for (i = 0; i < ecbdata->nparents && len; i++) { @@ -620,7 +628,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) } if (color != DIFF_FILE_NEW) { - emit_line(diff_get_color(ecbdata->color_diff, color), + emit_line(ecbdata->file, + diff_get_color(ecbdata->color_diff, color), reset, line, len); return; } @@ -759,20 +768,21 @@ static int scale_linear(int it, int width, int max_change) return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1); } -static void show_name(const char *prefix, const char *name, int len, +static void show_name(FILE *file, + const char *prefix, const char *name, int len, const char *reset, const char *set) { - printf(" %s%s%-*s%s |", set, prefix, len, name, reset); + fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset); } -static void show_graph(char ch, int cnt, const char *set, const char *reset) +static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset) { if (cnt <= 0) return; - printf("%s", set); + fprintf(file, "%s", set); while (cnt--) - putchar(ch); - printf("%s", reset); + putc(ch, file); + fprintf(file, "%s", reset); } static void fill_print_name(struct diffstat_file *file) @@ -877,18 +887,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) } if (data->files[i]->is_binary) { - show_name(prefix, name, len, reset, set); - printf(" Bin "); - printf("%s%d%s", del_c, deleted, reset); - printf(" -> "); - printf("%s%d%s", add_c, added, reset); - printf(" bytes"); - printf("\n"); + show_name(options->file, prefix, name, len, reset, set); + fprintf(options->file, " Bin "); + fprintf(options->file, "%s%d%s", del_c, deleted, reset); + fprintf(options->file, " -> "); + fprintf(options->file, "%s%d%s", add_c, added, reset); + fprintf(options->file, " bytes"); + fprintf(options->file, "\n"); continue; } else if (data->files[i]->is_unmerged) { - show_name(prefix, name, len, reset, set); - printf(" Unmerged\n"); + show_name(options->file, prefix, name, len, reset, set); + fprintf(options->file, " Unmerged\n"); continue; } else if (!data->files[i]->is_renamed && @@ -911,17 +921,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) del = scale_linear(del, width, max_change); total = add + del; } - show_name(prefix, name, len, reset, set); - printf("%5d ", added + deleted); - show_graph('+', add, add_c, reset); - show_graph('-', del, del_c, reset); - putchar('\n'); - } - printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n", + show_name(options->file, prefix, name, len, reset, set); + fprintf(options->file, "%5d ", added + deleted); + show_graph(options->file, '+', add, add_c, reset); + show_graph(options->file, '-', del, del_c, reset); + fprintf(options->file, "\n"); + } + fprintf(options->file, + "%s %d files changed, %d insertions(+), %d deletions(-)%s\n", set, total_files, adds, dels, reset); } -static void show_shortstats(struct diffstat_t* data) +static void show_shortstats(struct diffstat_t* data, struct diff_options *options) { int i, adds = 0, dels = 0, total_files = data->nr; @@ -942,7 +953,7 @@ static void show_shortstats(struct diffstat_t* data) } } } - printf(" %d files changed, %d insertions(+), %d deletions(-)\n", + fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n", total_files, adds, dels); } @@ -957,24 +968,25 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) struct diffstat_file *file = data->files[i]; if (file->is_binary) - printf("-\t-\t"); + fprintf(options->file, "-\t-\t"); else - printf("%d\t%d\t", file->added, file->deleted); + fprintf(options->file, + "%d\t%d\t", file->added, file->deleted); if (options->line_termination) { fill_print_name(file); if (!file->is_renamed) - write_name_quoted(file->name, stdout, + write_name_quoted(file->name, options->file, options->line_termination); else { - fputs(file->print_name, stdout); - putchar(options->line_termination); + fputs(file->print_name, options->file); + putc(options->line_termination, options->file); } } else { if (file->is_renamed) { - putchar('\0'); - write_name_quoted(file->from_name, stdout, '\0'); + putc('\0', options->file); + write_name_quoted(file->from_name, options->file, '\0'); } - write_name_quoted(file->name, stdout, '\0'); + write_name_quoted(file->name, options->file, '\0'); } } } @@ -984,7 +996,7 @@ struct diffstat_dir { int nr, percent, cumulative; }; -static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) +static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) { unsigned long this_dir = 0; unsigned int sources = 0; @@ -1002,7 +1014,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons slash = strchr(f->name + baselen, '/'); if (slash) { int newbaselen = slash + 1 - f->name; - this = gather_dirstat(dir, changed, f->name, newbaselen); + this = gather_dirstat(file, dir, changed, f->name, newbaselen); sources++; } else { if (f->is_unmerged || f->is_binary) @@ -1027,7 +1039,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons if (permille) { int percent = permille / 10; if (percent >= dir->percent) { - printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base); + fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base); if (!dir->cumulative) return 0; } @@ -1060,7 +1072,7 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options) dir.nr = data->nr; dir.percent = options->dirstat_percent; dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; - gather_dirstat(&dir, changed, "", 0); + gather_dirstat(options->file, &dir, changed, "", 0); } static void free_diffstat_info(struct diffstat_t *diffstat) @@ -1083,6 +1095,7 @@ struct checkdiff_t { int lineno, color_diff; unsigned ws_rule; unsigned status; + FILE *file; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) @@ -1100,11 +1113,11 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) if (!data->status) return; err = whitespace_error_string(data->status); - printf("%s:%d: %s.\n", data->filename, data->lineno, err); + fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err); free(err); - emit_line(set, reset, line, 1); + emit_line(data->file, set, reset, line, 1); (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, - stdout, set, reset, ws); + data->file, set, reset, ws); } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { @@ -1140,7 +1153,7 @@ static unsigned char *deflate_it(char *data, return deflated; } -static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two) +static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two) { void *cp; void *delta; @@ -1169,13 +1182,13 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two) } if (delta && delta_size < deflate_size) { - printf("delta %lu\n", orig_size); + fprintf(file, "delta %lu\n", orig_size); free(deflated); data = delta; data_size = delta_size; } else { - printf("literal %lu\n", two->size); + fprintf(file, "literal %lu\n", two->size); free(delta); data = deflated; data_size = deflate_size; @@ -1193,17 +1206,18 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two) line[0] = bytes - 26 + 'a' - 1; encode_85(line + 1, cp, bytes); cp = (char *) cp + bytes; - puts(line); + fputs(line, file); + fputc('\n', file); } - printf("\n"); + fprintf(file, "\n"); free(data); } -static void emit_binary_diff(mmfile_t *one, mmfile_t *two) +static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two) { - printf("GIT binary patch\n"); - emit_binary_diff_body(one, two); - emit_binary_diff_body(two, one); + fprintf(file, "GIT binary patch\n"); + emit_binary_diff_body(file, one, two); + emit_binary_diff_body(file, two, one); } static void setup_diff_attr_check(struct git_attr_check *check) @@ -1334,25 +1348,25 @@ static void builtin_diff(const char *name_a, b_two = quote_two(o->b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; - printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset); + fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset); if (lbl[0][0] == '/') { /* /dev/null */ - printf("%snew file mode %06o%s\n", set, two->mode, reset); + fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset); if (xfrm_msg && xfrm_msg[0]) - printf("%s%s%s\n", set, xfrm_msg, reset); + fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset); } else if (lbl[1][0] == '/') { - printf("%sdeleted file mode %06o%s\n", set, one->mode, reset); + fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset); if (xfrm_msg && xfrm_msg[0]) - printf("%s%s%s\n", set, xfrm_msg, reset); + fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset); } else { if (one->mode != two->mode) { - printf("%sold mode %06o%s\n", set, one->mode, reset); - printf("%snew mode %06o%s\n", set, two->mode, reset); + fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset); + fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset); } if (xfrm_msg && xfrm_msg[0]) - printf("%s%s%s\n", set, xfrm_msg, reset); + fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset); /* * we do not run diff between different kind * of objects. @@ -1376,10 +1390,10 @@ static void builtin_diff(const char *name_a, !memcmp(mf1.ptr, mf2.ptr, mf1.size)) goto free_ab_and_return; if (DIFF_OPT_TST(o, BINARY)) - emit_binary_diff(&mf1, &mf2); + emit_binary_diff(o->file, &mf1, &mf2); else - printf("Binary files %s and %s differ\n", - lbl[0], lbl[1]); + fprintf(o->file, "Binary files %s and %s differ\n", + lbl[0], lbl[1]); o->found_changes = 1; } else { @@ -1401,6 +1415,7 @@ static void builtin_diff(const char *name_a, ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); ecbdata.found_changesp = &o->found_changes; ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a); + ecbdata.file = o->file; xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; @@ -1415,9 +1430,11 @@ static void builtin_diff(const char *name_a, ecb.outf = xdiff_outf; ecb.priv = &ecbdata; ecbdata.xm.consume = fn_out_consume; - if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) + 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); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); @@ -1496,6 +1513,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.lineno = 0; data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); data.ws_rule = whitespace_rule(attr_path); + data.file = o->file; if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); @@ -1966,7 +1984,7 @@ static void run_diff_cmd(const char *pgm, builtin_diff(name, other ? other : name, one, two, xfrm_msg, o, complete_rewrite); else - printf("* Unmerged path %s\n", name); + fprintf(o->file, "* Unmerged path %s\n", name); } static void diff_fill_sha1_info(struct diff_filespec *one) @@ -2157,6 +2175,9 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) void diff_setup(struct diff_options *options) { memset(options, 0, sizeof(*options)); + + options->file = stdout; + options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; @@ -2470,7 +2491,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->b_prefix = arg + 13; else if (!strcmp(arg, "--no-prefix")) options->a_prefix = options->b_prefix = ""; - else + else if (!prefixcmp(arg, "--output=")) { + options->file = fopen(arg + strlen("--output="), "w"); + options->close_file = 1; + } else return 0; return 1; } @@ -2599,15 +2623,15 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) int inter_name_termination = line_termination ? '\t' : '\0'; if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) { - printf(":%06o %06o %s ", p->one->mode, p->two->mode, - diff_unique_abbrev(p->one->sha1, opt->abbrev)); - printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev)); + fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode, + diff_unique_abbrev(p->one->sha1, opt->abbrev)); + fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev)); } if (p->score) { - printf("%c%03d%c", p->status, similarity_index(p), - inter_name_termination); + fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p), + inter_name_termination); } else { - printf("%c%c", p->status, inter_name_termination); + fprintf(opt->file, "%c%c", p->status, inter_name_termination); } if (p->status == DIFF_STATUS_COPIED || @@ -2616,14 +2640,14 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) name_a = p->one->path; name_b = p->two->path; strip_prefix(opt->prefix_length, &name_a, &name_b); - write_name_quoted(name_a, stdout, inter_name_termination); - write_name_quoted(name_b, stdout, line_termination); + write_name_quoted(name_a, opt->file, inter_name_termination); + write_name_quoted(name_b, opt->file, line_termination); } else { const char *name_a, *name_b; name_a = p->one->mode ? p->one->path : p->two->path; name_b = NULL; strip_prefix(opt->prefix_length, &name_a, &name_b); - write_name_quoted(name_a, stdout, line_termination); + write_name_quoted(name_a, opt->file, line_termination); } } @@ -2825,62 +2849,62 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) name_a = p->two->path; name_b = NULL; strip_prefix(opt->prefix_length, &name_a, &name_b); - write_name_quoted(name_a, stdout, opt->line_termination); + write_name_quoted(name_a, opt->file, opt->line_termination); } } -static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs) +static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_filespec *fs) { if (fs->mode) - printf(" %s mode %06o ", newdelete, fs->mode); + fprintf(file, " %s mode %06o ", newdelete, fs->mode); else - printf(" %s ", newdelete); - write_name_quoted(fs->path, stdout, '\n'); + fprintf(file, " %s ", newdelete); + write_name_quoted(fs->path, file, '\n'); } -static void show_mode_change(struct diff_filepair *p, int show_name) +static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name) { if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) { - printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode, + fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode, show_name ? ' ' : '\n'); if (show_name) { - write_name_quoted(p->two->path, stdout, '\n'); + write_name_quoted(p->two->path, file, '\n'); } } } -static void show_rename_copy(const char *renamecopy, struct diff_filepair *p) +static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p) { char *names = pprint_rename(p->one->path, p->two->path); - printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p)); + fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p)); free(names); - show_mode_change(p, 0); + show_mode_change(file, p, 0); } -static void diff_summary(struct diff_filepair *p) +static void diff_summary(FILE *file, struct diff_filepair *p) { switch(p->status) { case DIFF_STATUS_DELETED: - show_file_mode_name("delete", p->one); + show_file_mode_name(file, "delete", p->one); break; case DIFF_STATUS_ADDED: - show_file_mode_name("create", p->two); + show_file_mode_name(file, "create", p->two); break; case DIFF_STATUS_COPIED: - show_rename_copy("copy", p); + show_rename_copy(file, "copy", p); break; case DIFF_STATUS_RENAMED: - show_rename_copy("rename", p); + show_rename_copy(file, "rename", p); break; default: if (p->score) { - fputs(" rewrite ", stdout); - write_name_quoted(p->two->path, stdout, ' '); - printf("(%d%%)\n", similarity_index(p)); + fputs(" rewrite ", file); + write_name_quoted(p->two->path, file, ' '); + fprintf(file, "(%d%%)\n", similarity_index(p)); } - show_mode_change(p, !p->score); + show_mode_change(file, p, !p->score); break; } } @@ -3088,14 +3112,14 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_DIFFSTAT) show_stats(&diffstat, options); if (output_format & DIFF_FORMAT_SHORTSTAT) - show_shortstats(&diffstat); + show_shortstats(&diffstat, options); free_diffstat_info(&diffstat); separator++; } if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) { for (i = 0; i < q->nr; i++) - diff_summary(q->queue[i]); + diff_summary(options->file, q->queue[i]); separator++; } @@ -3103,9 +3127,9 @@ void diff_flush(struct diff_options *options) if (separator) { if (options->stat_sep) { /* attach patch instead of inline */ - fputs(options->stat_sep, stdout); + fputs(options->stat_sep, options->file); } else { - putchar(options->line_termination); + putc(options->line_termination, options->file); } } @@ -3125,6 +3149,8 @@ free_queue: free(q->queue); q->queue = NULL; q->nr = q->alloc = 0; + if (options->close_file) + fclose(options->file); } static void diffcore_apply_filter(const char *filter) @@ -98,6 +98,9 @@ struct diff_options { /* this is set by diffcore for DIFF_FORMAT_PATCH */ int found_changes; + FILE *file; + int close_file; + int nr_paths; const char **paths; int *pathlens; diff --git a/git-compat-util.h b/git-compat-util.h index 73968e02b0..a18235e6d0 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -68,6 +68,7 @@ #include <sys/poll.h> #include <sys/socket.h> #include <sys/ioctl.h> +#include <utime.h> #ifndef NO_SYS_SELECT_H #include <sys/select.h> #endif diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 47f116f37e..95c5eec51e 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -735,7 +735,7 @@ sub commit { next unless $logmsg =~ $rx && $1; my $mparent = $1 eq 'HEAD' ? $opt_o : $1; if (my $sha1 = get_headref("$remote/$mparent")) { - push @commit_args, '-p', $mparent; + push @commit_args, '-p', "$remote/$mparent"; print "Merge parent branch: $mparent\n" if $opt_v; } } diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 233e5eae1d..7cd8f7134e 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -63,7 +63,23 @@ tmp_info="$tmp_dir/info" commit=$(git rev-parse HEAD) mkdir $tmp_dir || exit 2 -for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do +while read patch_name level garbage +do + case "$patch_name" in ''|'#'*) continue;; esac + case "$level" in + -p*) ;; + ''|'#'*) + level=;; + *) + echo "unable to parse patch level, ignoring it." + level=;; + esac + case "$garbage" in + ''|'#'*);; + *) + echo "trailing garbage found in series file: $garbage" + exit 1;; + esac if ! [ -f "$QUILT_PATCHES/$patch_name" ] ; then echo "$patch_name doesn't exist. Skipping." continue @@ -113,10 +129,10 @@ for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do fi if [ -z "$dry_run" ] ; then - git apply --index -C1 "$tmp_patch" && + git apply --index -C1 ${level:+"$level"} "$tmp_patch" && tree=$(git write-tree) && commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) && git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4 fi -done +done <"$QUILT_PATCHES/series" rm -rf $tmp_dir || exit 5 diff --git a/git-submodule.sh b/git-submodule.sh index ceb22952aa..56ec3536e0 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -4,7 +4,9 @@ # # Copyright (c) 2007 Lars Hjemli -USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]' +USAGE="[--quiet] [--cached] \ +[add <repo> [-b branch]|status|init|update|summary [-n|--summary-limit <n>] [<commit>]] \ +[--] [<path>...]" OPTIONS_SPEC= . git-sh-setup require_work_tree @@ -330,7 +332,175 @@ set_name_rev () { ) ) test -z "$revname" || revname=" ($revname)" } +# +# Show commit summary for submodules in index or working tree +# +# If '--cached' is given, show summary between index and given commit, +# or between working tree and given commit +# +# $@ = [commit (default 'HEAD'),] requested paths (default all) +# +cmd_summary() { + summary_limit=-1 + + # parse $args after "submodule ... summary". + while test $# -ne 0 + do + case "$1" in + --cached) + cached="$1" + ;; + -n|--summary-limit) + if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2" + then + : + else + usage + fi + shift + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift + done + + test $summary_limit = 0 && return + + if rev=$(git rev-parse --verify "$1^0" 2>/dev/null) + then + head=$rev + shift + else + head=HEAD + fi + + cd_to_toplevel + # Get modified modules cared by user + modules=$(git diff-index $cached --raw $head -- "$@" | + grep -e '^:160000' -e '^:[0-7]* 160000' | + while read mod_src mod_dst sha1_src sha1_dst status name + do + # Always show modules deleted or type-changed (blob<->module) + test $status = D -o $status = T && echo "$name" && continue + # Also show added or modified modules which are checked out + GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 && + echo "$name" + done + ) + + test -n "$modules" && + git diff-index $cached --raw $head -- $modules | + grep -e '^:160000' -e '^:[0-7]* 160000' | + cut -c2- | + while read mod_src mod_dst sha1_src sha1_dst status name + do + if test -z "$cached" && + test $sha1_dst = 0000000000000000000000000000000000000000 + then + case "$mod_dst" in + 160000) + sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD) + ;; + 100644 | 100755 | 120000) + sha1_dst=$(git hash-object $name) + ;; + 000000) + ;; # removed + *) + # unexpected type + echo >&2 "unexpected mode $mod_dst" + continue ;; + esac + fi + missing_src= + missing_dst= + + test $mod_src = 160000 && + ! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_src^0 >/dev/null 2>&1 && + missing_src=t + + test $mod_dst = 160000 && + ! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_dst^0 >/dev/null 2>&1 && + missing_dst=t + total_commits= + case "$missing_src,$missing_dst" in + t,) + errmsg=" Warn: $name doesn't contain commit $sha1_src" + ;; + ,t) + errmsg=" Warn: $name doesn't contain commit $sha1_dst" + ;; + t,t) + errmsg=" Warn: $name doesn't contain commits $sha1_src and $sha1_dst" + ;; + *) + errmsg= + total_commits=$( + if test $mod_src = 160000 -a $mod_dst = 160000 + then + range="$sha1_src...$sha1_dst" + elif test $mod_src = 160000 + then + range=$sha1_src + else + range=$sha1_dst + fi + GIT_DIR="$name/.git" \ + git log --pretty=oneline --first-parent $range | wc -l + ) + total_commits=" ($(($total_commits + 0)))" + ;; + esac + + sha1_abbr_src=$(echo $sha1_src | cut -c1-7) + sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7) + if test $status = T + then + if test $mod_dst = 160000 + then + echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:" + else + echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:" + fi + else + echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:" + fi + if test -n "$errmsg" + then + # Don't give error msg for modification whose dst is not submodule + # i.e. deleted or changed to blob + test $mod_dst = 160000 && echo "$errmsg" + else + if test $mod_src = 160000 -a $mod_dst = 160000 + then + limit= + test $summary_limit -gt 0 && limit="-$summary_limit" + GIT_DIR="$name/.git" \ + git log $limit --pretty='format: %m %s' \ + --first-parent $sha1_src...$sha1_dst + elif test $mod_dst = 160000 + then + GIT_DIR="$name/.git" \ + git log --pretty='format: > %s' -1 $sha1_dst + else + GIT_DIR="$name/.git" \ + git log --pretty='format: < %s' -1 $sha1_src + fi + echo + fi + echo + done +} # # List all submodules, prefixed with: # - submodule not initialized @@ -401,7 +571,7 @@ cmd_status() while test $# != 0 && test -z "$command" do case "$1" in - add | init | update | status) + add | init | update | status | summary) command=$1 ;; -q|--quiet) @@ -416,7 +586,7 @@ do branch="$2"; shift ;; --cached) - cached=1 + cached="$1" ;; --) break @@ -440,8 +610,8 @@ then usage fi -# "--cached" is accepted only by "status" -if test -n "$cached" && test "$command" != status +# "--cached" is accepted only by "status" and "summary" +if test -n "$cached" && test "$command" != status -a "$command" != summary then usage fi diff --git a/git-svn.perl b/git-svn.perl index d8b38c9a47..bba22c1321 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -958,9 +958,10 @@ sub complete_url_ls_init { "wanted to set to: $gs->{url}\n"; } command_oneline('config', $k, $gs->{url}) unless $orig_url; - my $remote_path = "$ra->{svn_path}/$repo_path/*"; + my $remote_path = "$ra->{svn_path}/$repo_path"; $remote_path =~ s#/+#/#g; $remote_path =~ s#^/##g; + $remote_path .= "/*" if $remote_path !~ /\*/; my ($n) = ($switch =~ /^--(\w+)/); if (length $pfx && $pfx !~ m#/$#) { die "--prefix='$pfx' must have a trailing slash '/'\n"; diff --git a/git-web--browse.sh b/git-web--browse.sh index 1023b90859..384148a59f 100755 --- a/git-web--browse.sh +++ b/git-web--browse.sh @@ -23,12 +23,18 @@ USAGE='[--browser=browser|--tool=browser] [--config=conf.var] url/file ...' NONGIT_OK=Yes . git-sh-setup +valid_custom_tool() +{ + browser_cmd="$(git config "browser.$1.cmd")" + test -n "$browser_cmd" +} + valid_tool() { case "$1" in firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open) ;; # happy *) - return 1 + valid_custom_tool "$1" || return 1 ;; esac } @@ -122,7 +128,7 @@ else init_browser_path "$browser" - if ! type "$browser_path" > /dev/null 2>&1; then + if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then die "The browser $browser is not available as '$browser_path'." fi fi @@ -157,4 +163,9 @@ case "$browser" in dillo) "$browser_path" "$@" & ;; + *) + if test -n "$browser_cmd"; then + ( eval $browser_cmd "$@" ) + fi + ;; esac @@ -343,7 +343,7 @@ static void handle_internal_command(int argc, const char **argv) { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE }, { "rm", cmd_rm, RUN_SETUP }, { "send-pack", cmd_send_pack, RUN_SETUP }, - { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, + { "shortlog", cmd_shortlog, USE_PAGER }, { "show-branch", cmd_show_branch, RUN_SETUP }, { "show", cmd_show, RUN_SETUP | USE_PAGER }, { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE }, diff --git a/gitk-git/Makefile b/gitk-git/Makefile index ae2b80b108..f90dfabffa 100644 --- a/gitk-git/Makefile +++ b/gitk-git/Makefile @@ -8,6 +8,7 @@ gitk_libdir ?= $(sharedir)/gitk/lib msgsdir ?= $(gitk_libdir)/msgs msgsdir_SQ = $(subst ','\'',$(msgsdir)) +TCL_PATH ?= tclsh TCLTK_PATH ?= wish INSTALL ?= install RM ?= rm -f @@ -22,6 +23,9 @@ ifdef NO_MSGFMT MSGFMT ?= $(TCL_PATH) po/po2msg.sh else MSGFMT ?= msgfmt + ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0) + MSGFMT := $(TCL_PATH) po/po2msg.sh + endif endif PO_TEMPLATE = po/gitk.pot diff --git a/gitk-git/gitk b/gitk-git/gitk index f1f21e97bf..84ab02e15f 100644 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -82,7 +82,7 @@ proc dorunq {} { proc start_rev_list {view} { global startmsecs global commfd leftover tclencoding datemode - global viewargs viewfiles commitidx viewcomplete vnextroot + global viewargs viewargscmd viewfiles commitidx viewcomplete vnextroot global showlocalchanges commitinterest mainheadid global progressdirn progresscoords proglastnc curview @@ -90,13 +90,23 @@ proc start_rev_list {view} { set commitidx($view) 0 set viewcomplete($view) 0 set vnextroot($view) 0 + set args $viewargs($view) + if {$viewargscmd($view) ne {}} { + if {[catch { + set str [exec sh -c $viewargscmd($view)] + } err]} { + error_popup "Error executing --argscmd command: $err" + exit 1 + } + set args [concat $args [split $str "\n"]] + } set order "--topo-order" if {$datemode} { set order "--date-order" } if {[catch { set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \ - --boundary $viewargs($view) "--" $viewfiles($view)] r] + --boundary $args "--" $viewfiles($view)] r] } err]} { error_popup "[mc "Error executing git rev-list:"] $err" exit 1 @@ -393,6 +403,9 @@ proc readcommit {id} { proc updatecommits {} { global viewdata curview phase displayorder ordertok idpending global children commitrow selectedline thickerline showneartags + global isworktree + + set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}] if {$phase ne {}} { stop_rev_list @@ -827,6 +840,7 @@ proc makewindow {} { } frame .bleft.top frame .bleft.mid + frame .bleft.bottom button .bleft.top.search -text [mc "Search"] -command dosearch pack .bleft.top.search -side left -padx 5 @@ -854,18 +868,25 @@ proc makewindow {} { checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \ -command changeignorespace -variable ignorespace pack .bleft.mid.ignspace -side left -padx 5 - set ctext .bleft.ctext + set ctext .bleft.bottom.ctext text $ctext -background $bgcolor -foreground $fgcolor \ -state disabled -font textfont \ - -yscrollcommand scrolltext -wrap none + -yscrollcommand scrolltext -wrap none \ + -xscrollcommand ".bleft.bottom.sbhorizontal set" if {$have_tk85} { $ctext conf -tabstyle wordprocessor } - scrollbar .bleft.sb -command "$ctext yview" + scrollbar .bleft.bottom.sb -command "$ctext yview" + scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h \ + -width 10 pack .bleft.top -side top -fill x pack .bleft.mid -side top -fill x - pack .bleft.sb -side right -fill y - pack $ctext -side left -fill both -expand 1 + grid $ctext .bleft.bottom.sb -sticky nsew + grid .bleft.bottom.sbhorizontal -sticky ew + grid columnconfigure .bleft.bottom 0 -weight 1 + grid rowconfigure .bleft.bottom 0 -weight 1 + grid rowconfigure .bleft.bottom 1 -weight 0 + pack .bleft.bottom -side top -fill both -expand 1 lappend bglist $ctext lappend fglist $ctext @@ -930,9 +951,17 @@ proc makewindow {} { .pwbottom add .bright .ctop add .pwbottom - # restore window position if known + # restore window width & height if known if {[info exists geometry(main)]} { - wm geometry . "$geometry(main)" + if {[scan $geometry(main) "%dx%d" w h] >= 2} { + if {$w > [winfo screenwidth .]} { + set w [winfo screenwidth .] + } + if {$h > [winfo screenheight .]} { + set h [winfo screenheight .] + } + wm geometry . "${w}x$h" + } } if {[tk windowingsystem] eq {aqua}} { @@ -1160,9 +1189,10 @@ proc savestuff {w} { global canv canv2 canv3 mainfont textfont uifont tabstop global stuffsaved findmergefiles maxgraphpct global maxwidth showneartags showlocalchanges - global viewname viewfiles viewargs viewperm nextviewnum + global viewname viewfiles viewargs viewargscmd viewperm nextviewnum global cmitmode wrapcomment datetimeformat limitdiffs global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor + global autoselect if {$stuffsaved} return if {![winfo viewable .]} return @@ -1177,6 +1207,7 @@ proc savestuff {w} { puts $f [list set maxwidth $maxwidth] puts $f [list set cmitmode $cmitmode] puts $f [list set wrapcomment $wrapcomment] + puts $f [list set autoselect $autoselect] puts $f [list set showneartags $showneartags] puts $f [list set showlocalchanges $showlocalchanges] puts $f [list set datetimeformat $datetimeformat] @@ -1199,7 +1230,7 @@ proc savestuff {w} { puts -nonewline $f "set permviews {" for {set v 0} {$v < $nextviewnum} {incr v} { if {$viewperm($v)} { - puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}" + puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}" } } puts $f "}" @@ -1850,7 +1881,7 @@ proc shellsplit {str} { proc newview {ishighlight} { global nextviewnum newviewname newviewperm newishighlight - global newviewargs revtreeargs + global newviewargs revtreeargs viewargscmd newviewargscmd curview set newishighlight $ishighlight set top .gitkview @@ -1858,16 +1889,17 @@ proc newview {ishighlight} { raise $top return } - set newviewname($nextviewnum) "View $nextviewnum" + set newviewname($nextviewnum) "[mc "View"] $nextviewnum" set newviewperm($nextviewnum) 0 set newviewargs($nextviewnum) [shellarglist $revtreeargs] + set newviewargscmd($nextviewnum) $viewargscmd($curview) vieweditor $top $nextviewnum [mc "Gitk view definition"] } proc editview {} { global curview global viewname viewperm newviewname newviewperm - global viewargs newviewargs + global viewargs newviewargs viewargscmd newviewargscmd set top .gitkvedit-$curview if {[winfo exists $top]} { @@ -1877,6 +1909,7 @@ proc editview {} { set newviewname($curview) $viewname($curview) set newviewperm($curview) $viewperm($curview) set newviewargs($curview) [shellarglist $viewargs($curview)] + set newviewargscmd($curview) $viewargscmd($curview) vieweditor $top $curview "Gitk: edit view $viewname($curview)" } @@ -1897,6 +1930,14 @@ proc vieweditor {top n title} { entry $top.args -width 50 -textvariable newviewargs($n) \ -background $bgcolor grid $top.args - -sticky ew -padx 5 + + message $top.ac -aspect 1000 \ + -text [mc "Command to generate more commits to include:"] + grid $top.ac - -sticky w -pady 5 + entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \ + -background white + grid $top.argscmd - -sticky ew -padx 5 + message $top.l -aspect 1000 \ -text [mc "Enter files and directories to include, one per line:"] grid $top.l - -sticky w @@ -1940,7 +1981,7 @@ proc allviewmenus {n op args} { proc newviewok {top n} { global nextviewnum newviewperm newviewname newishighlight global viewname viewfiles viewperm selectedview curview - global viewargs newviewargs viewhlmenu + global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu if {[catch { set newargs [shellsplit $newviewargs($n)] @@ -1964,6 +2005,7 @@ proc newviewok {top n} { set viewperm($n) $newviewperm($n) set viewfiles($n) $files set viewargs($n) $newargs + set viewargscmd($n) $newviewargscmd($n) addviewmenu $n if {!$newishighlight} { run showview $n @@ -1980,9 +2022,11 @@ proc newviewok {top n} { # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \ # entryconf [list -label $viewname($n) -value $viewname($n)] } - if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} { + if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \ + $newviewargscmd($n) ne $viewargscmd($n)} { set viewfiles($n) $files set viewargs($n) $newargs + set viewargscmd($n) $newviewargscmd($n) if {$curview == $n} { run updatecommits } @@ -2058,8 +2102,6 @@ proc showview {n} { set ybot [expr {[lindex $span 1] * $ymax}] if {$ytop < $y && $y < $ybot} { set yscreen [expr {$y - $ytop}] - } else { - set yscreen [expr {($ybot - $ytop) / 2}] } } elseif {[info exists pending_select]} { set selid $pending_select @@ -2120,7 +2162,7 @@ proc showview {n} { set yf 0 set row {} set selectfirst 0 - if {$selid ne {} && [info exists commitrow($n,$selid)]} { + if {[info exists yscreen] && [info exists commitrow($n,$selid)]} { set row $commitrow($n,$selid) # try to get the selected row in the same position on the screen set ymax [lindex [$canv cget -scrollregion] 3] @@ -2844,8 +2886,9 @@ proc dohidelocalchanges {} { # spawn off a process to do git diff-index --cached HEAD proc dodiffindex {} { global localirow localfrow lserial showlocalchanges + global isworktree - if {!$showlocalchanges} return + if {!$showlocalchanges || !$isworktree} return incr lserial set localfrow -1 set localirow -1 @@ -4650,6 +4693,7 @@ proc selectline {l isnew} { global commentend idtags linknum global mergemax numcommits pending_select global cmitmode showneartags allcommits + global autoselect catch {unset pending_select} $canv delete hover @@ -4705,8 +4749,10 @@ proc selectline {l isnew} { set currentid $id $sha1entry delete 0 end $sha1entry insert 0 $id - $sha1entry selection from 0 - $sha1entry selection to end + if {$autoselect} { + $sha1entry selection from 0 + $sha1entry selection to end + } rhighlight_sel $id $ctext conf -state normal @@ -5604,7 +5650,7 @@ proc searchmarkvisible {doall} { proc scrolltext {f0 f1} { global searchstring - .bleft.sb set $f0 $f1 + .bleft.bottom.sb set $f0 $f1 if {$searchstring ne {}} { searchmarkvisible 0 } @@ -7943,7 +7989,7 @@ proc doprefs {} { global maxwidth maxgraphpct global oldprefs prefstop showneartags showlocalchanges global bgcolor fgcolor ctext diffcolors selectbgcolor - global tabstop limitdiffs + global tabstop limitdiffs autoselect set top .gitkprefs set prefstop $top @@ -7973,6 +8019,11 @@ proc doprefs {} { checkbutton $top.showlocal.b -variable showlocalchanges pack $top.showlocal.b $top.showlocal.l -side left grid x $top.showlocal -sticky w + frame $top.autoselect + label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont + checkbutton $top.autoselect.b -variable autoselect + pack $top.autoselect.b $top.autoselect.l -side left + grid x $top.autoselect -sticky w label $top.ddisp -text [mc "Diff display options"] grid $top.ddisp - -sticky w -pady 10 @@ -8463,6 +8514,7 @@ set maxlinelen 200 set showlocalchanges 1 set limitdiffs 1 set datetimeformat "%Y-%m-%d %H:%M:%S" +set autoselect 1 set colors {green red blue magenta darkgrey brown orange} set bgcolor white @@ -8522,8 +8574,9 @@ set mergeonly 0 set revtreeargs {} set cmdline_files {} set i 0 +set revtreeargscmd {} foreach arg $argv { - switch -- $arg { + switch -glob -- $arg { "" { } "-d" { set datemode 1 } "--merge" { @@ -8534,6 +8587,9 @@ foreach arg $argv { set cmdline_files [lrange $argv [expr {$i + 1}] end] break } + "--argscmd=*" { + set revtreeargscmd [string range $arg 10 end] + } default { lappend revtreeargs $arg } @@ -8635,6 +8691,7 @@ set highlight_files {} set viewfiles(0) {} set viewperm(0) 0 set viewargs(0) {} +set viewargscmd(0) {} set cmdlineok 0 set stopped 0 @@ -8643,6 +8700,7 @@ set patchnum 0 set localirow -1 set localfrow -1 set lserial 0 +set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}] setcoords makewindow # wait for the window to become visible @@ -8650,7 +8708,7 @@ tkwait visibility . wm title . "[file tail $argv0]: [file tail [pwd]]" readrefs -if {$cmdline_files ne {} || $revtreeargs ne {}} { +if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} { # create a view for the files/dirs specified on the command line set curview 1 set selectedview 1 @@ -8658,6 +8716,7 @@ if {$cmdline_files ne {} || $revtreeargs ne {}} { set viewname(1) [mc "Command line"] set viewfiles(1) $cmdline_files set viewargs(1) $revtreeargs + set viewargscmd(1) $revtreeargscmd set viewperm(1) 0 addviewmenu 1 .bar.view entryconf [mc "Edit view..."] -state normal @@ -8671,6 +8730,7 @@ if {[info exists permviews]} { set viewname($n) [lindex $v 0] set viewfiles($n) [lindex $v 1] set viewargs($n) [lindex $v 2] + set viewargscmd($n) [lindex $v 3] set viewperm($n) 1 addviewmenu $n } diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po new file mode 100644 index 0000000000..d0f4c2e19a --- /dev/null +++ b/gitk-git/po/it.po @@ -0,0 +1,890 @@ +# Translation of gitk +# Copyright (C) 2005-2008 Paul Mackerras +# This file is distributed under the same license as the gitk package. +# Michele Ballabio <barra_cuda@katamail.com>, 2008. +# +# +msgid "" +msgstr "" +"Project-Id-Version: gitk\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-03-13 17:29+0100\n" +"PO-Revision-Date: 2008-03-13 17:34+0100\n" +"Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n" +"Language-Team: Italian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: gitk:111 +msgid "Error executing git rev-list:" +msgstr "Errore nell'esecuzione di git rev-list:" + +#: gitk:124 +msgid "Reading" +msgstr "Lettura in corso" + +#: gitk:151 gitk:2191 +msgid "Reading commits..." +msgstr "Lettura delle revisioni in corso..." + +#: gitk:275 +msgid "Can't parse git log output:" +msgstr "Impossibile elaborare i dati di git log:" + +#: gitk:386 gitk:2195 +msgid "No commits selected" +msgstr "Nessuna revisione selezionata" + +#: gitk:500 +msgid "No commit information available" +msgstr "Nessuna informazione disponibile sulle revisioni" + +#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082 +msgid "OK" +msgstr "OK" + +#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425 +#: gitk:7924 gitk:8083 +msgid "Cancel" +msgstr "Annulla" + +#: gitk:661 +msgid "File" +msgstr "File" + +#: gitk:663 +msgid "Update" +msgstr "Aggiorna" + +#: gitk:664 +msgid "Reread references" +msgstr "Rileggi riferimenti" + +#: gitk:665 +msgid "List references" +msgstr "Elenca riferimenti" + +#: gitk:666 +msgid "Quit" +msgstr "Esci" + +#: gitk:668 +msgid "Edit" +msgstr "Modifica" + +#: gitk:669 +msgid "Preferences" +msgstr "Preferenze" + +#: gitk:672 gitk:1892 +msgid "View" +msgstr "Vista" + +#: gitk:673 +msgid "New view..." +msgstr "Nuova vista..." + +#: gitk:674 gitk:2133 gitk:8722 +msgid "Edit view..." +msgstr "Modifica vista..." + +#: gitk:676 gitk:2134 gitk:8723 +msgid "Delete view" +msgstr "Elimina vista" + +#: gitk:678 +msgid "All files" +msgstr "Tutti i file" + +#: gitk:682 +msgid "Help" +msgstr "Aiuto" + +#: gitk:683 gitk:1317 +msgid "About gitk" +msgstr "Informazioni su gitk" + +#: gitk:684 +msgid "Key bindings" +msgstr "Scorciatoie da tastiera" + +#: gitk:741 +msgid "SHA1 ID: " +msgstr "SHA1 ID: " + +#: gitk:791 +msgid "Find" +msgstr "Trova" + +#: gitk:792 +msgid "next" +msgstr "succ" + +#: gitk:793 +msgid "prev" +msgstr "prec" + +#: gitk:794 +msgid "commit" +msgstr "revisione" + +#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369 +msgid "containing:" +msgstr "contenente:" + +#: gitk:800 gitk:1778 gitk:1783 gitk:2431 +msgid "touching paths:" +msgstr "che riguarda i percorsi:" + +#: gitk:801 gitk:2436 +msgid "adding/removing string:" +msgstr "che aggiunge/rimuove la stringa:" + +#: gitk:810 gitk:812 +msgid "Exact" +msgstr "Esatto" + +#: gitk:812 gitk:2514 gitk:4274 +msgid "IgnCase" +msgstr "" + +#: gitk:812 gitk:2405 gitk:2512 gitk:4270 +msgid "Regexp" +msgstr "" + +#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436 +msgid "All fields" +msgstr "Tutti i campi" + +#: gitk:815 gitk:2531 gitk:2563 gitk:4336 +msgid "Headline" +msgstr "Titolo" + +#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827 +msgid "Comments" +msgstr "Commenti" + +#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956 +#: gitk:5971 +msgid "Author" +msgstr "Autore" + +#: gitk:816 gitk:2531 gitk:4336 gitk:4765 +msgid "Committer" +msgstr "Revisione creata da" + +#: gitk:845 +msgid "Search" +msgstr "Cerca" + +#: gitk:852 +msgid "Diff" +msgstr "" + +#: gitk:854 +msgid "Old version" +msgstr "Vecchia versione" + +#: gitk:856 +msgid "New version" +msgstr "Nuova versione" + +#: gitk:858 +msgid "Lines of context" +msgstr "Linee di contesto" + +#: gitk:868 +msgid "Ignore space change" +msgstr "Ignora modifiche agli spazi" + +#: gitk:926 +msgid "Patch" +msgstr "Modifiche" + +#: gitk:928 +msgid "Tree" +msgstr "Directory" + +#: gitk:1053 gitk:1068 gitk:6022 +msgid "Diff this -> selected" +msgstr "Diff questo -> selezionato" + +#: gitk:1055 gitk:1070 gitk:6023 +msgid "Diff selected -> this" +msgstr "Diff selezionato -> questo" + +#: gitk:1057 gitk:1072 gitk:6024 +msgid "Make patch" +msgstr "Crea patch" + +#: gitk:1058 gitk:6162 +msgid "Create tag" +msgstr "Crea etichetta" + +#: gitk:1059 gitk:6255 +msgid "Write commit to file" +msgstr "Scrivi revisione in un file" + +#: gitk:1060 gitk:6309 +msgid "Create new branch" +msgstr "Crea un nuovo ramo" + +#: gitk:1061 +msgid "Cherry-pick this commit" +msgstr "Porta questa revisione in cima al ramo attuale" + +#: gitk:1063 +msgid "Reset HEAD branch to here" +msgstr "Aggiorna il ramo HEAD a questa revisione" + +#: gitk:1079 +msgid "Check out this branch" +msgstr "Attiva questo ramo" + +#: gitk:1081 +msgid "Remove this branch" +msgstr "Elimina questo ramo" + +#: gitk:1087 +msgid "Highlight this too" +msgstr "Evidenzia anche questo" + +#: gitk:1089 +msgid "Highlight this only" +msgstr "Evidenzia solo questo" + +#: gitk:1318 +msgid "" +"\n" +"Gitk - a commit viewer for git\n" +"\n" +"Copyright © 2005-2006 Paul Mackerras\n" +"\n" +"Use and redistribute under the terms of the GNU General Public License" +msgstr "" +"\n" +"Gitk - un visualizzatore di revisioni per git\n" +"\n" +"Copyright © 2005-2006 Paul Mackerras\n" +"\n" +"Utilizzo e redistribuzione permessi sotto i termini della GNU General Public " +"License" + +#: gitk:1326 gitk:1387 gitk:6581 +msgid "Close" +msgstr "Chiudi" + +#: gitk:1345 +msgid "Gitk key bindings" +msgstr "Scorciatoie da tastiera di Gitk" + +#: gitk:1347 +msgid "Gitk key bindings:" +msgstr "Scorciatoie da tastiera di Gitk:" + +#: gitk:1349 +#, tcl-format +msgid "<%s-Q>\t\tQuit" +msgstr "<%s-Q>\t\tEsci" + +#: gitk:1350 +msgid "<Home>\t\tMove to first commit" +msgstr "<Home>\t\tVai alla prima revisione" + +#: gitk:1351 +msgid "<End>\t\tMove to last commit" +msgstr "<End>\t\tVai all'ultima revisione" + +#: gitk:1352 +msgid "<Up>, p, i\tMove up one commit" +msgstr "<Up>, p, i\tVai più in alto di una revisione" + +#: gitk:1353 +msgid "<Down>, n, k\tMove down one commit" +msgstr "<Down>, n, k\tVai più in basso di una revisione" + +#: gitk:1354 +msgid "<Left>, z, j\tGo back in history list" +msgstr "<Left>, z, j\tTorna indietro nella cronologia" + +#: gitk:1355 +msgid "<Right>, x, l\tGo forward in history list" +msgstr "<Right>, x, l\tVai avanti nella cronologia" + +#: gitk:1356 +msgid "<PageUp>\tMove up one page in commit list" +msgstr "<PageUp>\tVai più in alto di una pagina nella lista delle revisioni" + +#: gitk:1357 +msgid "<PageDown>\tMove down one page in commit list" +msgstr "<PageDown>\tVai più in basso di una pagina nella lista delle revisioni" + +#: gitk:1358 +#, tcl-format +msgid "<%s-Home>\tScroll to top of commit list" +msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni" + +#: gitk:1359 +#, tcl-format +msgid "<%s-End>\tScroll to bottom of commit list" +msgstr "<%s-End>\tScorri alla fine della lista delle revisioni" + +#: gitk:1360 +#, tcl-format +msgid "<%s-Up>\tScroll commit list up one line" +msgstr "<%s-Up>\tScorri la lista delle revisioni in alto di una riga" + +#: gitk:1361 +#, tcl-format +msgid "<%s-Down>\tScroll commit list down one line" +msgstr "<%s-Down>\tScorri la lista delle revisioni in basso di una riga" + +#: gitk:1362 +#, tcl-format +msgid "<%s-PageUp>\tScroll commit list up one page" +msgstr "<%s-PageUp>\tScorri la lista delle revisioni in alto di una pagina" + +#: gitk:1363 +#, tcl-format +msgid "<%s-PageDown>\tScroll commit list down one page" +msgstr "<%s-PageDown>\tScorri la lista delle revisioni in basso di una pagina" + +#: gitk:1364 +msgid "<Shift-Up>\tFind backwards (upwards, later commits)" +msgstr "<Shift-Up>\tTrova all'indietro (verso l'alto, revisioni successive)" + +#: gitk:1365 +msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" +msgstr "<Shift-Down>\tTrova in avanti (verso il basso, revisioni precedenti)" + +#: gitk:1366 +msgid "<Delete>, b\tScroll diff view up one page" +msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina" + +#: gitk:1367 +msgid "<Backspace>\tScroll diff view up one page" +msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina" + +#: gitk:1368 +msgid "<Space>\t\tScroll diff view down one page" +msgstr "<Space>\t\tScorri la vista delle differenze in basso di una pagina" + +#: gitk:1369 +msgid "u\t\tScroll diff view up 18 lines" +msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee" + +#: gitk:1370 +msgid "d\t\tScroll diff view down 18 lines" +msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee" + +#: gitk:1371 +#, tcl-format +msgid "<%s-F>\t\tFind" +msgstr "<%s-F>\t\tTrova" + +#: gitk:1372 +#, tcl-format +msgid "<%s-G>\t\tMove to next find hit" +msgstr "<%s-G>\t\tTrova in avanti" + +#: gitk:1373 +msgid "<Return>\tMove to next find hit" +msgstr "<Return>\tTrova in avanti" + +#: gitk:1374 +msgid "/\t\tMove to next find hit, or redo find" +msgstr "/\t\tTrova in avanti, o cerca di nuovo" + +#: gitk:1375 +msgid "?\t\tMove to previous find hit" +msgstr "?\t\tTrova all'indietro" + +#: gitk:1376 +msgid "f\t\tScroll diff view to next file" +msgstr "f\t\tScorri la vista delle differenze al file successivo" + +#: gitk:1377 +#, tcl-format +msgid "<%s-S>\t\tSearch for next hit in diff view" +msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze" + +#: gitk:1378 +#, tcl-format +msgid "<%s-R>\t\tSearch for previous hit in diff view" +msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze" + +#: gitk:1379 +#, tcl-format +msgid "<%s-KP+>\tIncrease font size" +msgstr "<%s-KP+>\tAumenta grandezza carattere" + +#: gitk:1380 +#, tcl-format +msgid "<%s-plus>\tIncrease font size" +msgstr "<%s-plus>\tAumenta grandezza carattere" + +#: gitk:1381 +#, tcl-format +msgid "<%s-KP->\tDecrease font size" +msgstr "<%s-KP->\tDiminuisci grandezza carattere" + +#: gitk:1382 +#, tcl-format +msgid "<%s-minus>\tDecrease font size" +msgstr "<%s-minus>\tDiminuisci grandezza carattere" + +#: gitk:1383 +msgid "<F5>\t\tUpdate" +msgstr "<F5>\t\tAggiorna" + +#: gitk:1896 +msgid "Gitk view definition" +msgstr "Scelta vista Gitk" + +#: gitk:1921 +msgid "Name" +msgstr "Nome" + +#: gitk:1924 +msgid "Remember this view" +msgstr "Ricorda questa vista" + +#: gitk:1928 +msgid "Commits to include (arguments to git rev-list):" +msgstr "Revisioni da includere (argomenti di git rev-list):" + +#: gitk:1935 +msgid "Command to generate more commits to include:" +msgstr "Comando che genera altre revisioni da visualizzare:" + +#: gitk:1942 +msgid "Enter files and directories to include, one per line:" +msgstr "Inserire file e directory da includere, uno per riga:" + +#: gitk:1989 +msgid "Error in commit selection arguments:" +msgstr "Errore negli argomenti di selezione delle revisioni:" + +#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689 +msgid "None" +msgstr "Nessuno" + +#: gitk:2531 gitk:4336 gitk:5958 gitk:5973 +msgid "Date" +msgstr "Data" + +#: gitk:2531 gitk:4336 +msgid "CDate" +msgstr "" + +#: gitk:2680 gitk:2685 +msgid "Descendant" +msgstr "Discendente" + +#: gitk:2681 +msgid "Not descendant" +msgstr "Non discendente" + +#: gitk:2688 gitk:2693 +msgid "Ancestor" +msgstr "Ascendente" + +#: gitk:2689 +msgid "Not ancestor" +msgstr "Non ascendente" + +#: gitk:2924 +msgid "Local changes checked in to index but not committed" +msgstr "Modifiche locali presenti nell'indice ma non nell'archivio" + +#: gitk:2954 +msgid "Local uncommitted changes, not checked in to index" +msgstr "Modifiche locali non presenti né nell'archivio né nell'indice" + +#: gitk:4305 +msgid "Searching" +msgstr "Ricerca in corso" + +#: gitk:4767 +msgid "Tags:" +msgstr "Etichette:" + +#: gitk:4784 gitk:4790 gitk:5951 +msgid "Parent" +msgstr "Genitore" + +#: gitk:4795 +msgid "Child" +msgstr "Figlio" + +#: gitk:4804 +msgid "Branch" +msgstr "Ramo" + +#: gitk:4807 +msgid "Follows" +msgstr "Segue" + +#: gitk:4810 +msgid "Precedes" +msgstr "Precede" + +#: gitk:5093 +msgid "Error getting merge diffs:" +msgstr "Errore nella lettura delle differenze di fusione:" + +#: gitk:5778 +msgid "Goto:" +msgstr "Vai a:" + +#: gitk:5780 +msgid "SHA1 ID:" +msgstr "SHA1 ID:" + +#: gitk:5805 +#, tcl-format +msgid "Short SHA1 id %s is ambiguous" +msgstr "La SHA1 id abbreviata %s è ambigua" + +#: gitk:5817 +#, tcl-format +msgid "SHA1 id %s is not known" +msgstr "La SHA1 id %s è sconosciuta" + +#: gitk:5819 +#, tcl-format +msgid "Tag/Head %s is not known" +msgstr "L'etichetta/ramo %s è sconosciuto" + +#: gitk:5961 +msgid "Children" +msgstr "Figli" + +#: gitk:6018 +#, tcl-format +msgid "Reset %s branch to here" +msgstr "Aggiorna il ramo %s a questa revisione" + +#: gitk:6049 +msgid "Top" +msgstr "Inizio" + +#: gitk:6050 +msgid "From" +msgstr "Da" + +#: gitk:6055 +msgid "To" +msgstr "A" + +#: gitk:6078 +msgid "Generate patch" +msgstr "Genera patch" + +#: gitk:6080 +msgid "From:" +msgstr "Da:" + +#: gitk:6089 +msgid "To:" +msgstr "A:" + +#: gitk:6098 +msgid "Reverse" +msgstr "Inverti" + +#: gitk:6100 gitk:6269 +msgid "Output file:" +msgstr "Scrivi sul file:" + +#: gitk:6106 +msgid "Generate" +msgstr "Genera" + +#: gitk:6142 +msgid "Error creating patch:" +msgstr "Errore nella creazione della patch:" + +#: gitk:6164 gitk:6257 gitk:6311 +msgid "ID:" +msgstr "ID:" + +#: gitk:6173 +msgid "Tag name:" +msgstr "Nome etichetta:" + +#: gitk:6177 gitk:6320 +msgid "Create" +msgstr "Crea" + +#: gitk:6192 +msgid "No tag name specified" +msgstr "Nessuna etichetta specificata" + +#: gitk:6196 +#, tcl-format +msgid "Tag \"%s\" already exists" +msgstr "L'etichetta \"%s\" esiste già" + +#: gitk:6202 +msgid "Error creating tag:" +msgstr "Errore nella creazione dell'etichetta:" + +#: gitk:6266 +msgid "Command:" +msgstr "Comando:" + +#: gitk:6274 +msgid "Write" +msgstr "Scrivi" + +#: gitk:6290 +msgid "Error writing commit:" +msgstr "Errore nella scrittura della revisione:" + +#: gitk:6316 +msgid "Name:" +msgstr "Nome:" + +#: gitk:6335 +msgid "Please specify a name for the new branch" +msgstr "Specificare un nome per il nuovo ramo" + +#: gitk:6364 +#, tcl-format +msgid "Commit %s is already included in branch %s -- really re-apply it?" +msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?" + +#: gitk:6369 +msgid "Cherry-picking" +msgstr "" + +#: gitk:6381 +msgid "No changes committed" +msgstr "Nessuna modifica archiviata" + +#: gitk:6404 +msgid "Confirm reset" +msgstr "Conferma git reset" + +#: gitk:6406 +#, tcl-format +msgid "Reset branch %s to %s?" +msgstr "Aggiornare il ramo %s a %s?" + +#: gitk:6410 +msgid "Reset type:" +msgstr "Tipo di aggiornamento:" + +#: gitk:6414 +msgid "Soft: Leave working tree and index untouched" +msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono" + +#: gitk:6417 +msgid "Mixed: Leave working tree untouched, reset index" +msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice" + +#: gitk:6420 +msgid "" +"Hard: Reset working tree and index\n" +"(discard ALL local changes)" +msgstr "" +"Hard: Aggiorna la directory di lavoro e l'indice\n" +"(abbandona TUTTE le modifiche locali)" + +#: gitk:6436 +msgid "Resetting" +msgstr "git reset in corso" + +#: gitk:6493 +msgid "Checking out" +msgstr "Attivazione in corso" + +#: gitk:6523 +msgid "Cannot delete the currently checked-out branch" +msgstr "Impossibile cancellare il ramo attualmente attivo" + +#: gitk:6529 +#, tcl-format +msgid "" +"The commits on branch %s aren't on any other branch.\n" +"Really delete branch %s?" +msgstr "" +"Le revisioni nel ramo %s non sono presenti su altri rami.\n" +"Cancellare il ramo %s?" + +#: gitk:6560 +#, tcl-format +msgid "Tags and heads: %s" +msgstr "Etichette e rami: %s" + +#: gitk:6574 +msgid "Filter" +msgstr "Filtro" + +#: gitk:6868 +msgid "" +"Error reading commit topology information; branch and preceding/following " +"tag information will be incomplete." +msgstr "" +"Errore nella lettura della topologia delle revisioni: le informazioni sul " +"ramo e le etichette precedenti e seguenti saranno incomplete." + +#: gitk:7852 +msgid "Tag" +msgstr "Etichetta" + +#: gitk:7852 +msgid "Id" +msgstr "Id" + +#: gitk:7892 +msgid "Gitk font chooser" +msgstr "Scelta caratteri gitk" + +#: gitk:7909 +msgid "B" +msgstr "B" + +#: gitk:7912 +msgid "I" +msgstr "I" + +#: gitk:8005 +msgid "Gitk preferences" +msgstr "Preferenze gitk" + +#: gitk:8006 +msgid "Commit list display options" +msgstr "Opzioni visualizzazione dell'elenco revisioni" + +#: gitk:8009 +msgid "Maximum graph width (lines)" +msgstr "Larghezza massima del grafico (in linee)" + +#: gitk:8013 +#, tcl-format +msgid "Maximum graph width (% of pane)" +msgstr "Larghezza massima del grafico (% del pannello)" + +#: gitk:8018 +msgid "Show local changes" +msgstr "Mostra modifiche locali" + +#: gitk:8023 +msgid "Auto-select SHA1" +msgstr "Seleziona automaticamente SHA1 hash" + +#: gitk:8028 +msgid "Diff display options" +msgstr "Opzioni di visualizzazione delle differenze" + +#: gitk:8030 +msgid "Tab spacing" +msgstr "Spaziatura tabulazioni" + +#: gitk:8034 +msgid "Display nearby tags" +msgstr "Mostra etichette vicine" + +#: gitk:8039 +msgid "Limit diffs to listed paths" +msgstr "Limita le differenze ai percorsi elencati" + +#: gitk:8044 +msgid "Colors: press to choose" +msgstr "Colori: premere per scegliere" + +#: gitk:8047 +msgid "Background" +msgstr "Sfondo" + +#: gitk:8051 +msgid "Foreground" +msgstr "Primo piano" + +#: gitk:8055 +msgid "Diff: old lines" +msgstr "Diff: vecchie linee" + +#: gitk:8060 +msgid "Diff: new lines" +msgstr "Diff: nuove linee" + +#: gitk:8065 +msgid "Diff: hunk header" +msgstr "Diff: intestazione della sezione" + +#: gitk:8071 +msgid "Select bg" +msgstr "Sfondo selezione" + +#: gitk:8075 +msgid "Fonts: press to choose" +msgstr "Carattere: premere per scegliere" + +#: gitk:8077 +msgid "Main font" +msgstr "Carattere principale" + +#: gitk:8078 +msgid "Diff display font" +msgstr "Carattere per differenze" + +#: gitk:8079 +msgid "User interface font" +msgstr "Carattere per interfaccia utente" + +#: gitk:8095 +#, tcl-format +msgid "Gitk: choose color for %s" +msgstr "Gitk: scegliere un colore per %s" + +#: gitk:8476 +msgid "" +"Sorry, gitk cannot run with this version of Tcl/Tk.\n" +" Gitk requires at least Tcl/Tk 8.4." +msgstr "" +"Questa versione di Tcl/Tk non può avviare gitk.\n" +" Gitk richiede Tcl/Tk versione 8.4 o superiore." + +#: gitk:8565 +msgid "Cannot find a git repository here." +msgstr "Archivio git non trovato." + +#: gitk:8569 +#, tcl-format +msgid "Cannot find the git directory \"%s\"." +msgstr "Directory git \"%s\" non trovata." + +#: gitk:8612 +#, tcl-format +msgid "Ambiguous argument '%s': both revision and filename" +msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file" + +#: gitk:8624 +msgid "Bad arguments to gitk:" +msgstr "Gitk: argomenti errati:" + +#: gitk:8636 +msgid "Couldn't get list of unmerged files:" +msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:" + +#: gitk:8652 +msgid "No files selected: --merge specified but no files are unmerged." +msgstr "" +"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci " +"sono file in attesa di fusione." + +#: gitk:8655 +msgid "" +"No files selected: --merge specified but no unmerged files are within file " +"limit." +msgstr "" +"Nessun file selezionato: è stata specificata l'opzione --merge ma i file " +"specificati non sono in attesa di fusione." + +#: gitk:8716 +msgid "Command line" +msgstr "Linea di comando" @@ -8,6 +8,12 @@ #include "exec_cmd.h" #include "common-cmds.h" #include "parse-options.h" +#include "run-command.h" + +static struct man_viewer_list { + void (*exec)(const char *); + struct man_viewer_list *next; +} *man_viewer_list; enum help_format { HELP_FORMAT_MAN, @@ -42,6 +48,102 @@ static enum help_format parse_help_format(const char *format) die("unrecognized help format '%s'", format); } +static int check_emacsclient_version(void) +{ + struct strbuf buffer = STRBUF_INIT; + struct child_process ec_process; + const char *argv_ec[] = { "emacsclient", "--version", NULL }; + int version; + + /* emacsclient prints its version number on stderr */ + memset(&ec_process, 0, sizeof(ec_process)); + ec_process.argv = argv_ec; + ec_process.err = -1; + ec_process.stdout_to_stderr = 1; + if (start_command(&ec_process)) { + fprintf(stderr, "Failed to start emacsclient.\n"); + return -1; + } + strbuf_read(&buffer, ec_process.err, 20); + close(ec_process.err); + + /* + * Don't bother checking return value, because "emacsclient --version" + * seems to always exits with code 1. + */ + finish_command(&ec_process); + + if (prefixcmp(buffer.buf, "emacsclient")) { + fprintf(stderr, "Failed to parse emacsclient version.\n"); + strbuf_release(&buffer); + return -1; + } + + strbuf_remove(&buffer, 0, strlen("emacsclient")); + version = atoi(buffer.buf); + + if (version < 22) { + fprintf(stderr, + "emacsclient version '%d' too old (< 22).\n", + version); + strbuf_release(&buffer); + return -1; + } + + strbuf_release(&buffer); + return 0; +} + +static void exec_woman_emacs(const char *page) +{ + if (!check_emacsclient_version()) { + /* This works only with emacsclient version >= 22. */ + struct strbuf man_page = STRBUF_INIT; + strbuf_addf(&man_page, "(woman \"%s\")", page); + execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL); + } +} + +static void exec_man_konqueror(const char *page) +{ + const char *display = getenv("DISPLAY"); + if (display && *display) { + struct strbuf man_page = STRBUF_INIT; + strbuf_addf(&man_page, "man:%s(1)", page); + execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL); + } +} + +static void exec_man_man(const char *page) +{ + execlp("man", "man", page, NULL); +} + +static void do_add_man_viewer(void (*exec)(const char *)) +{ + struct man_viewer_list **p = &man_viewer_list; + + while (*p) + p = &((*p)->next); + *p = xmalloc(sizeof(**p)); + (*p)->next = NULL; + (*p)->exec = exec; +} + +static int add_man_viewer(const char *value) +{ + if (!strcasecmp(value, "man")) + do_add_man_viewer(exec_man_man); + else if (!strcasecmp(value, "woman")) + do_add_man_viewer(exec_woman_emacs); + else if (!strcasecmp(value, "konqueror")) + do_add_man_viewer(exec_man_konqueror); + else + warning("'%s': unsupported man viewer.", value); + + return 0; +} + static int git_help_config(const char *var, const char *value) { if (!strcmp(var, "help.format")) { @@ -50,6 +152,11 @@ static int git_help_config(const char *var, const char *value) help_format = parse_help_format(value); return 0; } + if (!strcmp(var, "man.viewer")) { + if (!value) + return config_error_nonbool(var); + return add_man_viewer(value); + } return git_default_config(var, value); } @@ -347,9 +454,16 @@ static void setup_man_path(void) static void show_man_page(const char *git_cmd) { + struct man_viewer_list *viewer; const char *page = cmd_to_page(git_cmd); + setup_man_path(); - execlp("man", "man", page, NULL); + for (viewer = man_viewer_list; viewer; viewer = viewer->next) + { + viewer->exec(page); /* will return when unable */ + } + exec_man_man(page); + die("no man viewer handled the request"); } static void show_info_page(const char *git_cmd) diff --git a/log-tree.c b/log-tree.c index 608f697cf3..5b2963998c 100644 --- a/log-tree.c +++ b/log-tree.c @@ -138,10 +138,14 @@ static int has_non_ascii(const char *s) } void log_write_email_headers(struct rev_info *opt, const char *name, - const char **subject_p, const char **extra_headers_p) + const char **subject_p, + const char **extra_headers_p, + int *need_8bit_cte_p) { const char *subject = NULL; const char *extra_headers = opt->extra_headers; + + *need_8bit_cte_p = 0; /* unknown */ if (opt->total > 0) { static char buffer[64]; snprintf(buffer, sizeof(buffer), @@ -169,6 +173,7 @@ void log_write_email_headers(struct rev_info *opt, const char *name, if (opt->mime_boundary) { static char subject_buffer[1024]; static char buffer[1024]; + *need_8bit_cte_p = -1; /* NEVER */ snprintf(subject_buffer, sizeof(subject_buffer) - 1, "%s" "MIME-Version: 1.0\n" @@ -212,6 +217,7 @@ void show_log(struct rev_info *opt, const char *sep) int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; const char *subject = NULL, *extra_headers = opt->extra_headers; + int need_8bit_cte = 0; opt->loginfo = NULL; if (!opt->verbose_header) { @@ -255,7 +261,8 @@ void show_log(struct rev_info *opt, const char *sep) if (opt->commit_format == CMIT_FMT_EMAIL) { log_write_email_headers(opt, sha1_to_hex(commit->object.sha1), - &subject, &extra_headers); + &subject, &extra_headers, + &need_8bit_cte); } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout); if (opt->commit_format != CMIT_FMT_ONELINE) @@ -299,9 +306,11 @@ void show_log(struct rev_info *opt, const char *sep) * And then the pretty-printed message itself */ strbuf_init(&msgbuf, 0); + if (need_8bit_cte >= 0) + need_8bit_cte = has_non_ascii(opt->add_signoff); pretty_print_commit(opt->commit_format, commit, &msgbuf, abbrev, subject, extra_headers, opt->date_mode, - has_non_ascii(opt->add_signoff)); + need_8bit_cte); if (opt->add_signoff) append_signoff(&msgbuf, opt->add_signoff); diff --git a/log-tree.h b/log-tree.h index 0cc9344eab..8946ff377c 100644 --- a/log-tree.h +++ b/log-tree.h @@ -14,6 +14,8 @@ int log_tree_opt_parse(struct rev_info *, const char **, int); void show_log(struct rev_info *opt, const char *sep); void show_decorations(struct commit *commit); void log_write_email_headers(struct rev_info *opt, const char *name, - const char **subject_p, const char **extra_headers_p); + const char **subject_p, + const char **extra_headers_p, + int *need_8bit_cte_p); #endif @@ -636,7 +636,7 @@ void pp_title_line(enum cmit_fmt fmt, const char *subject, const char *after_subject, const char *encoding, - int plain_non_ascii) + int need_8bit_cte) { struct strbuf title; @@ -669,7 +669,7 @@ void pp_title_line(enum cmit_fmt fmt, } strbuf_addch(sb, '\n'); - if (plain_non_ascii) { + if (need_8bit_cte > 0) { const char *header_fmt = "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=%s\n" @@ -718,9 +718,9 @@ void pp_remainder(enum cmit_fmt fmt, } void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - struct strbuf *sb, int abbrev, - const char *subject, const char *after_subject, - enum date_mode dmode, int plain_non_ascii) + struct strbuf *sb, int abbrev, + const char *subject, const char *after_subject, + enum date_mode dmode, int need_8bit_cte) { unsigned long beginning_of_body; int indent = 4; @@ -746,13 +746,11 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; - /* After-subject is used to pass in Content-Type: multipart - * MIME header; in that case we do not have to do the - * plaintext content type even if the commit message has - * non 7-bit ASCII character. Otherwise, check if we need - * to say this is not a 7-bit ASCII. + /* + * We need to check and emit Content-type: to mark it + * as 8-bit if we haven't done so. */ - if (fmt == CMIT_FMT_EMAIL && !after_subject) { + if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) { int i, ch, in_body; for (in_body = i = 0; (ch = msg[i]); i++) { @@ -765,7 +763,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, in_body = 1; } else if (non_ascii(ch)) { - plain_non_ascii = 1; + need_8bit_cte = 1; break; } } @@ -790,7 +788,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, /* These formats treat the title line specially. */ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) pp_title_line(fmt, &msg, sb, subject, - after_subject, encoding, plain_non_ascii); + after_subject, encoding, need_8bit_cte); beginning_of_body = sb->len; if (fmt != CMIT_FMT_ONELINE) diff --git a/sha1_name.c b/sha1_name.c index 8b6c76f68e..491d2e7ebf 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -407,18 +407,22 @@ static int get_nth_ancestor(const char *name, int len, unsigned char *result, int generation) { unsigned char sha1[20]; - int ret = get_sha1_1(name, len, sha1); + struct commit *commit; + int ret; + + ret = get_sha1_1(name, len, sha1); if (ret) return ret; + commit = lookup_commit_reference(sha1); + if (!commit) + return -1; while (generation--) { - struct commit *commit = lookup_commit_reference(sha1); - - if (!commit || parse_commit(commit) || !commit->parents) + if (parse_commit(commit) || !commit->parents) return -1; - hashcpy(sha1, commit->parents->item->object.sha1); + commit = commit->parents->item; } - hashcpy(result, sha1); + hashcpy(result, commit->object.sha1); return 0; } @@ -544,9 +548,8 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1) int ret, has_suffix; const char *cp; - /* "name~3" is "name^^^", - * "name~" and "name~0" are name -- not "name^0"! - * "name^" is not "name^0"; it is "name^1". + /* + * "name~3" is "name^^^", "name~" is "name~1", and "name^" is "name^1". */ has_suffix = 0; for (cp = name + len - 1; name <= cp; cp--) { @@ -564,11 +567,10 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1) cp++; while (cp < name + len) num = num * 10 + *cp++ - '0'; - if (has_suffix == '^') { - if (!num && len1 == len - 1) - num = 1; + if (!num && len1 == len - 1) + num = 1; + if (has_suffix == '^') return get_parent(name, len1, sha1, num); - } /* else if (has_suffix == '~') -- goes without saying */ return get_nth_ancestor(name, len1, sha1, num); } diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index 24476bede5..73f830db23 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -202,22 +202,4 @@ test_expect_success 'delete' ' ' -test_expect_success 'prune --expire' ' - - before=$(git count-objects | sed "s/ .*//") && - BLOB=$(echo aleph | git hash-object -w --stdin) && - BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && - test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && - test -f $BLOB_FILE && - git reset --hard && - git prune --expire=1.hour.ago && - test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && - test -f $BLOB_FILE && - test-chmtime -86500 $BLOB_FILE && - git prune --expire 1.day && - test $before = $(git count-objects | sed "s/ .*//") && - ! test -f $BLOB_FILE - -' - test_done diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4021-format-patch-signer-mime.sh index 67a70fadab..ba43f18549 100755 --- a/t/t4021-format-patch-signer-mime.sh +++ b/t/t4021-format-patch-signer-mime.sh @@ -32,11 +32,19 @@ test_expect_success 'format with signoff without funny signer name' ' test_expect_success 'format with non ASCII signer name' ' - GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(B" \ + GIT_COMMITTER_NAME="はまの ふにおう" \ git format-patch -s --stdout -1 >output && grep Content-Type output ' +test_expect_success 'attach and signoff do not duplicate mime headers' ' + + GIT_COMMITTER_NAME="はまの ふにおう" \ + git format-patch -s --stdout -1 --attach >output && + test `grep -ci ^MIME-Version: output` = 1 + +' + test_done diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh new file mode 100755 index 0000000000..204ba673cb --- /dev/null +++ b/t/t4028-format-patch-mime-headers.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +test_description='format-patch mime headers and extra headers do not conflict' +. ./test-lib.sh + +test_expect_success 'create commit with utf-8 body' ' + echo content >file && + git add file && + git commit -m one && + echo more >>file && + git commit -a -m "two + + utf-8 body: ñ" +' + +test_expect_success 'patch has mime headers' ' + rm -f 0001-two.patch && + git format-patch HEAD^ && + grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch +' + +test_expect_success 'patch has mime and extra headers' ' + rm -f 0001-two.patch && + git config format.headers "x-foo: bar" && + git format-patch HEAD^ && + grep -i "x-foo: bar" 0001-two.patch && + grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch +' + +test_done diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 91ea696314..672b28859f 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -47,4 +47,9 @@ EOF test_expect_success 'shortlog wrapping' 'test_cmp expect out' +git log HEAD > log +GIT_DIR=non-existing git shortlog -w < log > out + +test_expect_success 'shortlog from non-git directory' 'diff -u expect out' + test_done diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh index 6560af756e..47090c4cf5 100644 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh @@ -29,4 +29,53 @@ test_expect_success 'prune stale packs' ' ' +test_expect_success 'prune --expire' ' + + before=$(git count-objects | sed "s/ .*//") && + BLOB=$(echo aleph | git hash-object -w --stdin) && + BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + git prune --expire=1.hour.ago && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + test-chmtime -86500 $BLOB_FILE && + git prune --expire 1.day && + test $before = $(git count-objects | sed "s/ .*//") && + ! test -f $BLOB_FILE + +' + +test_expect_success 'gc: implicit prune --expire' ' + + before=$(git count-objects | sed "s/ .*//") && + BLOB=$(echo aleph_0 | git hash-object -w --stdin) && + BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + test-chmtime -$((86400*14-30)) $BLOB_FILE && + git gc && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + test-chmtime -$((86400*14+1)) $BLOB_FILE && + git gc && + test $before = $(git count-objects | sed "s/ .*//") && + ! test -f $BLOB_FILE + +' + +test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' ' + + git config gc.pruneExpire invalid && + test_must_fail git gc + +' + +test_expect_success 'gc: start with ok gc.pruneExpire' ' + + git config gc.pruneExpire 2.days.ago && + git gc + +' + test_done diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh new file mode 100755 index 0000000000..5bb6b93780 --- /dev/null +++ b/t/t6031-merge-recursive.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='merge-recursive: handle file mode' +. ./test-lib.sh + +test_expect_success 'mode change in one branch: keep changed version' ' + : >file1 && + git add file1 && + git commit -m initial && + git checkout -b a1 master && + : >dummy && + git add dummy && + git commit -m a && + git checkout -b b1 master && + chmod +x file1 && + git add file1 && + git commit -m b1 && + git checkout a1 && + git merge-recursive master -- a1 b1 && + test -x file1 +' + +test_expect_success 'mode change in both branches: expect conflict' ' + git reset --hard HEAD && + git checkout -b a2 master && + : >file2 && + H=$(git hash-object file2) && + chmod +x file2 && + git add file2 && + git commit -m a2 && + git checkout -b b2 master && + : >file2 && + git add file2 && + git commit -m b2 && + git checkout a2 && + ( + git merge-recursive master -- a2 b2 + test $? = 1 + ) && + git ls-files -u >actual && + ( + echo "100755 $H 2 file2" + echo "100644 $H 3 file2" + ) >expect && + diff -u actual expect && + test -x file2 +' + +test_done diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh new file mode 100755 index 0000000000..0f3c42ab35 --- /dev/null +++ b/t/t7401-submodule-summary.sh @@ -0,0 +1,195 @@ +#!/bin/sh +# +# Copyright (c) 2008 Ping Yin +# + +test_description='Summary support for submodules + +This test tries to verify the sanity of summary subcommand of git-submodule. +' + +. ./test-lib.sh + +add_file () { + sm=$1 + shift + owd=$(pwd) + cd "$sm" + for name; do + echo "$name" > "$name" && + git add "$name" && + test_tick && + git commit -m "Add $name" + done >/dev/null + git rev-parse --verify HEAD | cut -c1-7 + cd "$owd" +} +commit_file () { + test_tick && + git commit "$@" -m "Commit $*" >/dev/null +} + +test_create_repo sm1 && +add_file . foo + +head1=$(add_file sm1 foo1 foo2) + +test_expect_success 'added submodule' " + git add sm1 && + git submodule summary >actual && + diff actual - <<-EOF +* sm1 0000000...$head1 (2): + > Add foo2 + +EOF +" + +commit_file sm1 && +head2=$(add_file sm1 foo3) + +test_expect_success 'modified submodule(forward)' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head1...$head2 (1): + > Add foo3 + +EOF +" + +commit_file sm1 && +cd sm1 && +git reset --hard HEAD~2 >/dev/null && +head3=$(git rev-parse --verify HEAD | cut -c1-7) && +cd .. + +test_expect_success 'modified submodule(backward)' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head2...$head3 (2): + < Add foo3 + < Add foo2 + +EOF +" + +head4=$(add_file sm1 foo4 foo5) && +head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD) +test_expect_success 'modified submodule(backward and forward)' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head2...$head4 (4): + > Add foo5 + > Add foo4 + < Add foo3 + < Add foo2 + +EOF +" + +test_expect_success '--summary-limit' " + git submodule summary -n 3 >actual && + diff actual - <<-EOF +* sm1 $head2...$head4 (4): + > Add foo5 + > Add foo4 + < Add foo3 + +EOF +" + +commit_file sm1 && +mv sm1 sm1-bak && +echo sm1 >sm1 && +head5=$(git hash-object sm1 | cut -c1-7) && +git add sm1 && +rm -f sm1 && +mv sm1-bak sm1 + +test_expect_success 'typechanged submodule(submodule->blob), --cached' " + git submodule summary --cached >actual && + diff actual - <<-EOF +* sm1 $head4(submodule)->$head5(blob) (3): + < Add foo5 + +EOF +" + +rm -rf sm1 && +git checkout-index sm1 +test_expect_success 'typechanged submodule(submodule->blob)' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head4(submodule)->$head5(blob): + +EOF +" + +rm -f sm1 && +test_create_repo sm1 && +head6=$(add_file sm1 foo6 foo7) +test_expect_success 'nonexistent commit' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head4...$head6: + Warn: sm1 doesn't contain commit $head4_full + +EOF +" + +commit_file +test_expect_success 'typechanged submodule(blob->submodule)' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head5(blob)->$head6(submodule) (2): + > Add foo7 + +EOF +" + +commit_file sm1 && +rm -rf sm1 +test_expect_success 'deleted submodule' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head6...0000000: + +EOF +" + +test_create_repo sm2 && +head7=$(add_file sm2 foo8 foo9) && +git add sm2 + +test_expect_success 'multiple submodules' " + git submodule summary >actual && + diff actual - <<-EOF +* sm1 $head6...0000000: + +* sm2 0000000...$head7 (2): + > Add foo9 + +EOF +" + +test_expect_success 'path filter' " + git submodule summary sm2 >actual && + diff actual - <<-EOF +* sm2 0000000...$head7 (2): + > Add foo9 + +EOF +" + +commit_file sm2 +test_expect_success 'given commit' " + git submodule summary HEAD^ >actual && + diff actual - <<-EOF +* sm1 $head6...0000000: + +* sm2 0000000...$head7 (2): + > Add foo9 + +EOF +" + +test_done diff --git a/unpack-trees.c b/unpack-trees.c index be89d52e8c..93019abdc1 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -123,7 +123,7 @@ static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_option int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info) { int i; - struct tree_desc t[3]; + struct tree_desc t[MAX_UNPACK_TREES]; struct traverse_info newinfo; struct name_entry *p; @@ -327,8 +327,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options { static struct cache_entry *dfc; - if (len > 4) - die("unpack_trees takes at most four trees"); + if (len > MAX_UNPACK_TREES) + die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES); memset(&state, 0, sizeof(state)); state.base_dir = ""; state.force = 1; @@ -336,6 +336,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options state.refresh_cache = 1; memset(&o->result, 0, sizeof(o->result)); + if (o->src_index) + o->result.timestamp = o->src_index->timestamp; o->merge_size = len; if (!dfc) diff --git a/unpack-trees.h b/unpack-trees.h index e8abbcd037..50453ed20f 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -1,6 +1,8 @@ #ifndef UNPACK_TREES_H #define UNPACK_TREES_H +#define MAX_UNPACK_TREES 8 + struct unpack_trees_options; typedef int (*merge_fn_t)(struct cache_entry **src, diff --git a/wt-status.c b/wt-status.c index 701d13da7c..b3fd57b79d 100644 --- a/wt-status.c +++ b/wt-status.c @@ -269,27 +269,14 @@ static void wt_status_print_untracked(struct wt_status *s) static void wt_status_print_verbose(struct wt_status *s) { struct rev_info rev; - int saved_stdout; - - fflush(s->fp); - - /* Sigh, the entire diff machinery is hardcoded to output to - * stdout. Do the dup-dance...*/ - saved_stdout = dup(STDOUT_FILENO); - if (saved_stdout < 0 ||dup2(fileno(s->fp), STDOUT_FILENO) < 0) - die("couldn't redirect stdout\n"); init_revisions(&rev, NULL); setup_revisions(0, NULL, &rev, s->reference); rev.diffopt.output_format |= DIFF_FORMAT_PATCH; rev.diffopt.detect_rename = 1; + rev.diffopt.file = s->fp; + rev.diffopt.close_file = 0; run_diff_index(&rev, 1); - - fflush(stdout); - - if (dup2(saved_stdout, STDOUT_FILENO) < 0) - die("couldn't restore stdout\n"); - close(saved_stdout); } void wt_status_print(struct wt_status *s) diff --git a/xdiff-interface.c b/xdiff-interface.c index bba236428a..61dc5c5470 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -152,8 +152,8 @@ int read_mmfile(mmfile_t *ptr, const char *filename) if ((f = fopen(filename, "rb")) == NULL) return error("Could not open %s", filename); sz = xsize_t(st.st_size); - ptr->ptr = xmalloc(sz); - if (fread(ptr->ptr, sz, 1, f) != 1) + ptr->ptr = xmalloc(sz ? sz : 1); + if (sz && fread(ptr->ptr, sz, 1, f) != 1) return error("Could not read %s", filename); fclose(f); ptr->size = sz; |