mail.identity.default.compose_html => false
mail.identity.id?.compose_html => false
+(Lukas Sandström)
+
+There is a script in contrib/thunderbird-patch-inline which can help
+you include patches with Thunderbird in an easy way. To use it, do the
+steps above and then use the script as the external editor.
Gnus
----
relative to the repository root (this was the default for git
prior to v1.5.4).
+status.showUntrackedFiles::
+ By default, linkgit:git-status[1] and linkgit:git-commit[1] show
+ files which are not currently tracked by Git. Directories which
+ contain only untracked files, are shown with the directory name
+ only. Showing untracked files means that Git needs to lstat() all
+ all the files in the whole repository, which might be slow on some
+ systems. So, this variable controls how the commands displays
+ the untracked files. Possible values are:
++
+--
+ - 'no' - Show no untracked files
+ - 'normal' - Shows untracked files and directories
+ - 'all' - Shows also individual files in untracked directories.
+--
++
+If this variable is not specified, it defaults to 'normal'.
+This variable can be overridden with the -u|--untracked-files option
+of linkgit:git-status[1] and linkgit:git-commit[1].
+
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the
SYNOPSIS
--------
[verse]
-'git-commit' [-a | --interactive] [-s] [-v] [-u] [--amend]
+'git-commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend]
[(-c | -C) <commit>] [-F <file> | -m <msg>]
[--allow-empty] [--no-verify] [-e] [--author=<author>]
[--cleanup=<mode>] [--] [[-i | -o ]<file>...]
the last commit without committing changes that have
already been staged.
--u::
---untracked-files::
- Show all untracked files, also those in uninteresting
- directories, in the "Untracked files:" section of commit
- message template. Without this option only its name and
- a trailing slash are displayed for each untracked
- directory.
+-u[<mode>]::
+--untracked-files[=<mode>]::
+ Show untracked files (Default: 'all').
++
+The mode parameter is optional, and is used to specify
+the handling of untracked files. The possible options are:
++
+--
+ - 'no' - Show no untracked files
+ - 'normal' - Shows untracked files and directories
+ - 'all' - Also shows individual files in untracked directories.
+--
++
+See linkgit:git-config[1] for configuration variable
+used to change the default for when the option is not
+specified.
-v::
--verbose::
unsigned, with 'verbatim', they will be silently exported
and with 'warn', they will be exported, but you will see a warning.
+--export-marks=<file>::
+ Dumps the internal marks table to <file> when complete.
+ Marks are written one per line as `:markid SHA-1`. Only marks
+ for revisions are dumped; marks for blobs are ignored.
+ Backends can use this file to validate imports after they
+ have been completed, or to save the marks table across
+ incremental runs. As <file> is only opened and truncated
+ at completion, the same path can also be safely given to
+ \--import-marks.
+
+--import-marks=<file>::
+ Before processing any input, load the marks specified in
+ <file>. The input file must exist, must be readable, and
+ must use the same format as produced by \--export-marks.
++
+Any commits that have already been marked will not be exported again.
+If the backend uses a similar \--import-marks file, this allows for
+incremental bidirectional exporting of the repository by keeping the
+marks the same across runs.
+
EXAMPLES
--------
--mirror::
Instead of naming each ref to push, specifies that all
- refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/`
+ refs under `$GIT_DIR/refs/` (which includes but is not
+ limited to `refs/heads/`, `refs/remotes/`, and `refs/tags/`)
be mirrored to the remote repository. Newly created local
refs will be pushed to the remote end, locally updated refs
will be force updated on the remote end, and deleted refs
the repository with a public http:// or svn:// URL in the
metadata so users of it will see the public URL.
+--
+
Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps
options all affect the metadata generated and used by git-svn; they
*must* be set in the configuration file before any history is imported
Additionally, only one of these four options can be used per-svn-remote
section because they affect the 'git-svn-id:' metadata line.
---
BASIC EXAMPLES
--------------
SYNOPSIS
--------
-'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | [--no-deref] <ref> <newvalue> [<oldvalue>])
+'git-update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>])
DESCRIPTION
-----------
install-doc:
$(MAKE) -C Documentation install
+install-html:
+ $(MAKE) -C Documentation install-html
+
install-info:
$(MAKE) -C Documentation install-info
static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
-static int quiet, verbose, untracked_files, no_verify, allow_empty;
+static int quiet, verbose, no_verify, allow_empty;
+static char *untracked_files_arg;
/*
* The default commit message cleanup mode will remove the lines
* beginning with # (shell comments) and leading and trailing
OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
- OPT_BOOLEAN('u', "untracked-files", &untracked_files, "show all untracked files"),
+ { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
s.reference = "HEAD^1";
}
s.verbose = verbose;
- s.untracked = untracked_files;
+ s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES);
s.index_file = index_file;
s.fp = fp;
s.nowarn = nowarn;
fp = fopen(git_path(commit_editmsg), "w");
if (fp == NULL)
- die("could not open %s", git_path(commit_editmsg));
+ die("could not open %s: %s",
+ git_path(commit_editmsg), strerror(errno));
if (cleanup_mode != CLEANUP_NONE)
stripspace(&sb, 0);
else
die("Invalid cleanup mode %s", cleanup_arg);
+ if (!untracked_files_arg)
+ ; /* default already initialized */
+ else if (!strcmp(untracked_files_arg, "no"))
+ show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+ else if (!strcmp(untracked_files_arg, "normal"))
+ show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ else if (!strcmp(untracked_files_arg, "all"))
+ show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+ else
+ die("Invalid untracked files mode '%s'", untracked_files_arg);
+
if (all && argc > 0)
die("Paths with -a does not make sense.");
else if (interactive && argc > 0)
}
/* Since intptr_t is C99, we do not use it here */
-static void mark_object(struct object *object)
+static inline uint32_t *mark_to_ptr(uint32_t mark)
{
- last_idnum++;
- add_decoration(&idnums, object, ((uint32_t *)NULL) + last_idnum);
+ return ((uint32_t *)NULL) + mark;
+}
+
+static inline uint32_t ptr_to_mark(void * mark)
+{
+ return (uint32_t *)mark - (uint32_t *)NULL;
+}
+
+static inline void mark_object(struct object *object, uint32_t mark)
+{
+ add_decoration(&idnums, object, mark_to_ptr(mark));
+}
+
+static inline void mark_next_object(struct object *object)
+{
+ mark_object(object, ++last_idnum);
}
static int get_object_mark(struct object *object)
void *decoration = lookup_decoration(&idnums, object);
if (!decoration)
return 0;
- return (uint32_t *)decoration - (uint32_t *)NULL;
+ return ptr_to_mark(decoration);
}
static void show_progress(void)
if (!buf)
die ("Could not read blob %s", sha1_to_hex(sha1));
- mark_object(object);
+ mark_next_object(object);
printf("blob\nmark :%d\ndata %lu\n", last_idnum, size);
if (size && fwrite(buf, size, 1, stdout) != 1)
for (i = 0; i < diff_queued_diff.nr; i++)
handle_object(diff_queued_diff.queue[i]->two->sha1);
- mark_object(&commit->object);
+ mark_next_object(&commit->object);
if (!is_encoding_utf8(encoding))
reencoded = reencode_string(message, "UTF-8", encoding);
if (!commit->parents)
}
}
+static void export_marks(char *file)
+{
+ unsigned int i;
+ uint32_t mark;
+ struct object_decoration *deco = idnums.hash;
+ FILE *f;
+
+ f = fopen(file, "w");
+ if (!f)
+ error("Unable to open marks file %s for writing", file);
+
+ for (i = 0; i < idnums.size; ++i) {
+ deco++;
+ if (deco && deco->base && deco->base->type == 1) {
+ mark = ptr_to_mark(deco->decoration);
+ fprintf(f, ":%u %s\n", mark, sha1_to_hex(deco->base->sha1));
+ }
+ }
+
+ if (ferror(f) || fclose(f))
+ error("Unable to write marks file %s.", file);
+}
+
+static void import_marks(char * input_file)
+{
+ char line[512];
+ FILE *f = fopen(input_file, "r");
+ if (!f)
+ die("cannot read %s: %s", input_file, strerror(errno));
+
+ while (fgets(line, sizeof(line), f)) {
+ uint32_t mark;
+ char *line_end, *mark_end;
+ unsigned char sha1[20];
+ struct object *object;
+
+ line_end = strchr(line, '\n');
+ if (line[0] != ':' || !line_end)
+ die("corrupt mark line: %s", line);
+ *line_end = 0;
+
+ mark = strtoumax(line + 1, &mark_end, 10);
+ if (!mark || mark_end == line + 1
+ || *mark_end != ' ' || get_sha1(mark_end + 1, sha1))
+ die("corrupt mark line: %s", line);
+
+ object = parse_object(sha1);
+ if (!object)
+ die ("Could not read blob %s", sha1_to_hex(sha1));
+
+ if (object->flags & SHOWN)
+ error("Object %s already has a mark", sha1);
+
+ mark_object(object, mark);
+ if (last_idnum < mark)
+ last_idnum = mark;
+
+ object->flags |= SHOWN;
+ }
+ fclose(f);
+}
+
int cmd_fast_export(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
struct object_array commits = { 0, 0, NULL };
struct path_list extra_refs = { NULL, 0, 0, 0 };
struct commit *commit;
+ char *export_filename = NULL, *import_filename = NULL;
struct option options[] = {
OPT_INTEGER(0, "progress", &progress,
"show progress after <n> objects"),
OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
"select handling of signed tags",
parse_opt_signed_tag_mode),
+ OPT_STRING(0, "export-marks", &export_filename, "FILE",
+ "Dump marks to this file"),
+ OPT_STRING(0, "import-marks", &import_filename, "FILE",
+ "Import marks from this file"),
OPT_END()
};
if (argc > 1)
usage_with_options (fast_export_usage, options);
+ if (import_filename)
+ import_marks(import_filename);
+
get_tags_and_duplicates(&revs.pending, &extra_refs);
if (prepare_revision_walk(&revs))
handle_tags_and_duplicates(&extra_refs);
+ if (export_filename)
+ export_marks(export_filename);
+
return 0;
}
#include "parse-options.h"
static const char * const git_update_ref_usage[] = {
- "git-update-ref [options] -d <refname> <oldval>",
+ "git-update-ref [options] -d <refname> [<oldval>]",
"git-update-ref [options] <refname> <newval> [<oldval>]",
NULL
};
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
- const char *refname, *value, *oldval, *msg=NULL;
+ const char *refname, *oldval, *msg=NULL;
unsigned char sha1[20], oldsha1[20];
int delete = 0, no_deref = 0;
struct option options[] = {
if (msg && !*msg)
die("Refusing to perform update with empty message.");
- if (argc < 2 || argc > 3)
- usage_with_options(git_update_ref_usage, options);
- refname = argv[0];
- value = argv[1];
- oldval = argv[2];
-
- if (get_sha1(value, sha1))
- die("%s: not a valid SHA1", value);
-
if (delete) {
- if (oldval)
+ if (argc < 1 || argc > 2)
+ usage_with_options(git_update_ref_usage, options);
+ refname = argv[0];
+ oldval = argv[1];
+ } else {
+ const char *value;
+ if (argc < 2 || argc > 3)
usage_with_options(git_update_ref_usage, options);
- return delete_ref(refname, sha1);
+ refname = argv[0];
+ value = argv[1];
+ oldval = argv[2];
+ if (get_sha1(value, sha1))
+ die("%s: not a valid SHA1", value);
}
- hashclr(oldsha1);
+ hashclr(oldsha1); /* all-zero hash in case oldval is the empty string */
if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
- return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
- no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
+ if (delete)
+ return delete_ref(refname, oldval ? oldsha1 : NULL);
+ else
+ return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
+ no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
}
}
const char *make_absolute_path(const char *path);
const char *make_nonrelative_path(const char *path);
+const char *make_relative_path(const char *abs, const char *base);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
/* bit 0 up to (N-1) are on if the parent has this line (i.e.
* we did not change it).
* bit N is used for "interesting" lines, including context.
+ * bit (N+1) is used for "do not show deletion before this".
*/
unsigned long flag;
unsigned long *p_lno;
{
unsigned long all_mask = (1UL<<num_parent) - 1;
unsigned long mark = (1UL<<num_parent);
+ unsigned long no_pre_delete = (2UL<<num_parent);
unsigned long i;
/* Two groups of interesting lines may have a short gap of
/* Paint a few lines before the first interesting line. */
while (j < i)
- sline[j++].flag |= mark;
+ sline[j++].flag |= mark | no_pre_delete;
again:
/* we know up to i is to be included. where does the
int use_color)
{
unsigned long mark = (1UL<<num_parent);
+ unsigned long no_pre_delete = (2UL<<num_parent);
int i;
unsigned long lno = 0;
const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO);
int j;
unsigned long p_mask;
sl = &sline[lno++];
- ll = sl->lost_head;
+ ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head;
while (ll) {
fputs(c_old, stdout);
for (j = 0; j < num_parent; j++) {
--pretty= --name-status --name-only --raw
--not --all
--left-right --cherry-pick
+ --graph
+ --stat --numstat --shortstat
+ --decorate --diff-filter=
+ --color-words --walk-reflogs
"
return
;;
--- /dev/null
+appp.sh is a script that is supposed to be used together with ExternalEditor
+for Mozilla Thundebird. It will let you include patches inline in e-mails
+in an easy way.
+
+Usage:
+- Generate the patch with git format-patch.
+- Start writing a new e-mail in Thunderbird.
+- Press the external editor button (or Ctrl-E) to run appp.sh
+- Select the previosly generated patch file.
+- Finish editing the e-mail.
+
+Any text that is entered into the message editor before appp.sh is called
+will be moved to the section between the --- and the diffstat.
+
+All S-O-B:s and Cc:s in the patch will be added to the CC list.
+
+To set it up, just install External Editor and tell it to use appp.sh as the
+editor.
+
+Zenity is a required dependency.
--- /dev/null
+#!/bin/bash
+# Copyright 2008 Lukas Sandström <luksan@gmail.com>
+#
+# AppendPatch - A script to be used together with ExternalEditor
+# for Mozilla Thunderbird to properly include pathes inline i e-mails.
+
+# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2
+
+CONFFILE=~/.appprc
+
+SEP="-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-"
+if [ -e "$CONFFILE" ] ; then
+ LAST_DIR=`grep -m 1 "^LAST_DIR=" "${CONFFILE}"|sed -e 's/^LAST_DIR=//'`
+ cd "${LAST_DIR}"
+else
+ cd > /dev/null
+fi
+
+PATCH=$(zenity --file-selection)
+
+if [ "$?" != "0" ] ; then
+ #zenity --error --text "No patchfile given."
+ exit 1
+fi
+
+cd - > /dev/null
+
+SUBJECT=`sed -n -e '/^Subject: /p' "${PATCH}"`
+HEADERS=`sed -e '/^'"${SEP}"'$/,$d' $1`
+BODY=`sed -e "1,/${SEP}/d" $1`
+CMT_MSG=`sed -e '1,/^$/d' -e '/^---$/,$d' "${PATCH}"`
+DIFF=`sed -e '1,/^---$/d' "${PATCH}"`
+
+CCS=`echo -e "$CMT_MSG\n$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \
+ -e 's/^Signed-off-by: \(.*\)/\1,/gp'`
+
+echo "$SUBJECT" > $1
+echo "Cc: $CCS" >> $1
+echo "$HEADERS" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1
+echo "$SEP" >> $1
+
+echo "$CMT_MSG" >> $1
+echo "---" >> $1
+if [ "x${BODY}x" != "xx" ] ; then
+ echo >> $1
+ echo "$BODY" >> $1
+ echo >> $1
+fi
+echo "$DIFF" >> $1
+
+LAST_DIR=`dirname "${PATCH}"`
+
+grep -v "^LAST_DIR=" "${CONFFILE}" > "${CONFFILE}_"
+echo "LAST_DIR=${LAST_DIR}" >> "${CONFFILE}_"
+mv "${CONFFILE}_" "${CONFFILE}"
total = add + del;
}
show_name(options->file, prefix, name, len, reset, set);
- fprintf(options->file, "%5d ", added + deleted);
+ fprintf(options->file, "%5d%s", added + deleted,
+ added + deleted ? " " : "");
show_graph(options->file, '+', add, add_c, reset);
show_graph(options->file, '-', del, del_c, reset);
fprintf(options->file, "\n");
summary (synonym to --stat)
log add list of one-line log to merge commit message
squash create a single commit instead of doing a merge
-commit perform a commit if the merge sucesses (default)
+commit perform a commit if the merge succeeds (default)
ff allow fast forward (default)
s,strategy= merge strategy to use
m,message= message to be used for the merge commit (if any)
# dispatch
my %actions = (
- "blame" => \&git_blame2,
+ "blame" => \&git_blame,
"blobdiff" => \&git_blobdiff,
"blobdiff_plain" => \&git_blobdiff_plain,
"blob" => \&git_blob,
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-sub git_project_list_body {
- my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
-
- my ($check_forks) = gitweb_check_feature('forks');
-
+# fills project list info (age, description, owner, forks) for each
+# project in the list, removing invalid projects from returned list
+# NOTE: modifies $projlist, but does not remove entries from it
+sub fill_project_list_info {
+ my ($projlist, $check_forks) = @_;
my @projects;
+
+ PROJECT:
foreach my $pr (@$projlist) {
- my (@aa) = git_get_last_activity($pr->{'path'});
- unless (@aa) {
- next;
+ my (@activity) = git_get_last_activity($pr->{'path'});
+ unless (@activity) {
+ next PROJECT;
}
- ($pr->{'age'}, $pr->{'age_string'}) = @aa;
+ ($pr->{'age'}, $pr->{'age_string'}) = @activity;
if (!defined $pr->{'descr'}) {
my $descr = git_get_project_description($pr->{'path'}) || "";
- $pr->{'descr_long'} = to_utf8($descr);
+ $descr = to_utf8($descr);
+ $pr->{'descr_long'} = $descr;
$pr->{'descr'} = chop_str($descr, $projects_list_description_width, 5);
}
if (!defined $pr->{'owner'}) {
($pname !~ /\/$/) &&
(-d "$projectroot/$pname")) {
$pr->{'forks'} = "-d $projectroot/$pname";
- }
- else {
+ } else {
$pr->{'forks'} = 0;
}
}
push @projects, $pr;
}
+ return @projects;
+}
+
+# print 'sort by' <th> element, either sorting by $key if $name eq $order
+# (changing $list), or generating 'sort by $name' replay link otherwise
+sub print_sort_th {
+ my ($str_sort, $name, $order, $key, $header, $list) = @_;
+ $key ||= $name;
+ $header ||= ucfirst($name);
+
+ if ($order eq $name) {
+ if ($str_sort) {
+ @$list = sort {$a->{$key} cmp $b->{$key}} @$list;
+ } else {
+ @$list = sort {$a->{$key} <=> $b->{$key}} @$list;
+ }
+ print "<th>$header</th>\n";
+ } else {
+ print "<th>" .
+ $cgi->a({-href => href(-replay=>1, order=>$name),
+ -class => "header"}, $header) .
+ "</th>\n";
+ }
+}
+
+sub print_sort_th_str {
+ print_sort_th(1, @_);
+}
+
+sub print_sort_th_num {
+ print_sort_th(0, @_);
+}
+
+sub git_project_list_body {
+ my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
+
+ my ($check_forks) = gitweb_check_feature('forks');
+ my @projects = fill_project_list_info($projlist, $check_forks);
+
$order ||= $default_projects_order;
$from = 0 unless defined $from;
$to = $#projects if (!defined $to || $#projects < $to);
if ($check_forks) {
print "<th></th>\n";
}
- if ($order eq "project") {
- @projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
- print "<th>Project</th>\n";
- } else {
- print "<th>" .
- $cgi->a({-href => href(project=>undef, order=>'project'),
- -class => "header"}, "Project") .
- "</th>\n";
- }
- if ($order eq "descr") {
- @projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects;
- print "<th>Description</th>\n";
- } else {
- print "<th>" .
- $cgi->a({-href => href(project=>undef, order=>'descr'),
- -class => "header"}, "Description") .
- "</th>\n";
- }
- if ($order eq "owner") {
- @projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects;
- print "<th>Owner</th>\n";
- } else {
- print "<th>" .
- $cgi->a({-href => href(project=>undef, order=>'owner'),
- -class => "header"}, "Owner") .
- "</th>\n";
- }
- if ($order eq "age") {
- @projects = sort {$a->{'age'} <=> $b->{'age'}} @projects;
- print "<th>Last Change</th>\n";
- } else {
- print "<th>" .
- $cgi->a({-href => href(project=>undef, order=>'age'),
- -class => "header"}, "Last Change") .
- "</th>\n";
- }
- print "<th></th>\n" .
+ print_sort_th_str('project', $order, 'path',
+ 'Project', \@projects);
+ print_sort_th_str('descr', $order, 'descr_long',
+ 'Description', \@projects);
+ print_sort_th_str('owner', $order, 'owner',
+ 'Owner', \@projects);
+ print_sort_th_num('age', $order, 'age',
+ 'Last Change', \@projects);
+ print "<th></th>\n" . # for links
"</tr>\n";
}
my $alternate = 1;
git_footer_html();
}
-sub git_blame2 {
+sub git_blame {
my $fd;
my $ftype;
git_footer_html();
}
-sub git_blame {
- my $fd;
-
- my ($have_blame) = gitweb_check_feature('blame');
- if (!$have_blame) {
- die_error('403 Permission denied', "Permission denied");
- }
- die_error('404 Not Found', "File name not defined") if (!$file_name);
- $hash_base ||= git_get_head_hash($project);
- die_error(undef, "Couldn't find base commit") unless ($hash_base);
- my %co = parse_commit($hash_base)
- or die_error(undef, "Reading commit failed");
- if (!defined $hash) {
- $hash = git_get_hash_by_path($hash_base, $file_name, "blob")
- or die_error(undef, "Error lookup file");
- }
- open ($fd, "-|", git_cmd(), "annotate", '-l', '-t', '-r', $file_name, $hash_base)
- or die_error(undef, "Open git-annotate failed");
- git_header_html();
- my $formats_nav =
- $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
- "blob") .
- " | " .
- $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
- "history") .
- " | " .
- $cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
- "HEAD");
- git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
- git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
- git_print_page_path($file_name, 'blob', $hash_base);
- print "<div class=\"page_body\">\n";
- print <<HTML;
-<table class="blame">
- <tr>
- <th>Commit</th>
- <th>Age</th>
- <th>Author</th>
- <th>Line</th>
- <th>Data</th>
- </tr>
-HTML
- my @line_class = (qw(light dark));
- my $line_class_len = scalar (@line_class);
- my $line_class_num = $#line_class;
- while (my $line = <$fd>) {
- my $long_rev;
- my $short_rev;
- my $author;
- my $time;
- my $lineno;
- my $data;
- my $age;
- my $age_str;
- my $age_class;
-
- chomp $line;
- $line_class_num = ($line_class_num + 1) % $line_class_len;
-
- if ($line =~ m/^([0-9a-fA-F]{40})\t\(\s*([^\t]+)\t(\d+) [+-]\d\d\d\d\t(\d+)\)(.*)$/) {
- $long_rev = $1;
- $author = $2;
- $time = $3;
- $lineno = $4;
- $data = $5;
- } else {
- print qq( <tr><td colspan="5" class="error">Unable to parse: $line</td></tr>\n);
- next;
- }
- $short_rev = substr ($long_rev, 0, 8);
- $age = time () - $time;
- $age_str = age_string ($age);
- $age_str =~ s/ / /g;
- $age_class = age_class($age);
- $author = esc_html ($author);
- $author =~ s/ / /g;
-
- $data = untabify($data);
- $data = esc_html ($data);
-
- print <<HTML;
- <tr class="$line_class[$line_class_num]">
- <td class="sha1"><a href="${\href (action=>"commit", hash=>$long_rev)}" class="text">$short_rev..</a></td>
- <td class="$age_class">$age_str</td>
- <td>$author</td>
- <td class="linenr"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td>
- <td class="pre">$data</td>
- </tr>
-HTML
- } # while (my $line = <$fd>)
- print "</table>\n\n";
- close $fd
- or print "Reading blob failed.\n";
- print "</div>";
- git_footer_html();
-}
-
sub git_tags {
my $head = git_get_head_hash($project);
git_header_html();
fprintf(stderr, "usage: %s\n", *usagestr++);
while (*usagestr && **usagestr)
fprintf(stderr, " or: %s\n", *usagestr++);
- while (*usagestr)
- fprintf(stderr, " %s\n", *usagestr++);
+ while (*usagestr) {
+ fprintf(stderr, "%s%s\n",
+ **usagestr ? " " : "",
+ *usagestr);
+ usagestr++;
+ }
if (opts->type != OPTION_GROUP)
fputc('\n', stderr);
/* We allow "recursive" symbolic links. Only within reason, though. */
#define MAXDEPTH 5
+const char *make_relative_path(const char *abs, const char *base)
+{
+ static char buf[PATH_MAX + 1];
+ int baselen;
+ if (!base)
+ return abs;
+ baselen = strlen(base);
+ if (prefixcmp(abs, base))
+ return abs;
+ if (abs[baselen] == '/')
+ baselen++;
+ else if (base[baselen - 1] != '/')
+ return abs;
+ strcpy(buf, abs + baselen);
+ return buf;
+}
+
const char *make_absolute_path(const char *path)
{
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
work_tree = get_git_work_tree();
git_dir = get_git_dir();
if (!is_absolute_path(git_dir))
- set_git_dir(make_absolute_path(git_dir));
+ git_dir = make_absolute_path(git_dir);
if (!work_tree || chdir(work_tree))
die("This operation must be run in a work tree");
+ set_git_dir(make_relative_path(git_dir, work_tree));
initialized = 1;
}
-t[0-9][0-9][0-9][0-9]-*.sh -whitespace
t[0-9][0-9][0-9][0-9]/* -whitespace
This causes the test to immediately exit upon the first
failed test.
+--long-tests::
+ This causes additional long-running tests to be run (where
+ available), for more exhaustive testing.
+
+
+Skipping Tests
+--------------
+
+In some environments, certain tests have no way of succeeding
+due to platform limitation, such as lack of 'unzip' program, or
+filesystem that do not allow arbitrary sequence of non-NUL bytes
+as pathnames.
+
+You should be able to say something like
+
+ $ GIT_SKIP_TESTS=t9200.8 sh ./t9200-git-cvsexport-commit.sh
+
+and even:
+
+ $ GIT_SKIP_TESTS='t[0-4]??? t91?? t9200.8' make
+
+to omit such tests. The value of the environment variable is a
+SP separated list of patterns that tells which tests to skip,
+and either can match the "t[0-9]{4}" part to skip the whole
+test, or t[0-9]{4} followed by ".$number" to say which
+particular test to skip.
+
+Note that some tests in the existing test suite rely on previous
+test item, so you cannot arbitrarily disable one and expect the
+remainder of test to check what the test originally was intended
+to check.
+
Naming Tests
------------
'
rm -f .git/$m
+test_expect_success "delete $m without oldvalue verification" "
+ git update-ref $m $A &&
+ test $A = \$(cat .git/$m) &&
+ git update-ref -d $m &&
+ ! test -f .git/$m
+"
+rm -f .git/$m
+
test_expect_success \
"fail to create $n" \
"touch .git/$n_dir
cat > expect.err <<EOF
usage: some-command [options] <args>...
-
+
some-command does foo and bar!
-h, --help show the help
############################################################
# 17. disallow missing tag timestamp
-cat >tag.sig <<EOF
+tr '_' ' ' >tag.sig <<EOF
object $head
type commit
tag mytag
-tagger T A Gger <tagger@example.com>
+tagger T A Gger <tagger@example.com>__
EOF
echo 4 > other-file &&
git add other-file &&
echo 5 > other-file &&
- test_must_fail git stash apply
+ test_must_fail git stash apply
'
test_expect_success 'apply stashed changes' '
sed -e "/^$/q" patch2 > hdrs2 &&
grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs2 &&
grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs2
-
+
'
test_expect_success 'extra headers without newlines' '
sed -e "/^$/q" patch3 > hdrs3 &&
grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs3 &&
grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs3
-
+
'
test_expect_success 'extra headers with multiple To:s' '
git checkout side &&
git format-patch --cover-letter --thread -o patches/ master &&
FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
- for i in patches/0001-* patches/0002-* patches/0003-*
+ for i in patches/0001-* patches/0002-* patches/0003-*
do
grep "References: $FIRST_MID" $i &&
grep "In-Reply-To: $FIRST_MID" $i || break
git update-index x
-cat << EOF > x
+tr '_' ' ' << EOF > x
whitespace at beginning
whitespace change
white space in the middle
-whitespace at end
+whitespace at end__
unchanged line
CR at end
EOF
-tr 'Q' '\015' << EOF > expect
+tr 'Q_' '\015 ' << EOF > expect
diff --git a/x b/x
index d99af23..8b32fb5 100644
--- a/x
+ whitespace at beginning
+whitespace change
+white space in the middle
-+whitespace at end
++whitespace at end__
unchanged line
-CR at endQ
+CR at end
'
cat >expect <<\EOF
- pathname.1 => "Rpathname\twith HT.0" | 0
- pathname.3 => "Rpathname\nwith LF.0" | 0
- "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0
- pathname.2 => Rpathname with SP.0 | 0
- "pathname\twith HT.2" => Rpathname with SP.1 | 0
- pathname.0 => Rpathname.0 | 0
- "pathname\twith HT.0" => Rpathname.1 | 0
+ pathname.1 => "Rpathname\twith HT.0" | 0
+ pathname.3 => "Rpathname\nwith LF.0" | 0
+ "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0
+ pathname.2 => Rpathname with SP.0 | 0
+ "pathname\twith HT.2" => Rpathname with SP.1 | 0
+ pathname.0 => Rpathname.0 | 0
+ "pathname\twith HT.0" => Rpathname.1 | 0
7 files changed, 0 insertions(+), 0 deletions(-)
EOF
test_expect_success 'git diff --stat -M HEAD' '
'
. ./test-lib.sh
-# setup
-
-cat > patch1.patch <<\EOF
-diff --git a/main.c b/main.c
-new file mode 100644
---- /dev/null
-+++ b/main.c
-@@ -0,0 +1,23 @@
-+#include <stdio.h>
-+
-+int func(int num);
-+void print_int(int num);
-+
-+int main() {
-+ int i;
-+
-+ for (i = 0; i < 10; i++) {
-+ print_int(func(i));
-+ }
-+
-+ return 0;
-+}
-+
-+int func(int num) {
-+ return num * num;
-+}
-+
-+void print_int(int num) {
-+ printf("%d", num);
-+}
-+
-EOF
-cat > patch2.patch <<\EOF
-diff --git a/main.c b/main.c
---- a/main.c
-+++ b/main.c
-@@ -1,7 +1,9 @@
-+#include <stdlib.h>
- #include <stdio.h>
-
- int func(int num);
- void print_int(int num);
-+void print_ln();
-
- int main() {
- int i;
-@@ -10,6 +12,8 @@
- print_int(func(i));
- }
-
-+ print_ln();
-+
- return 0;
- }
-
-@@ -21,3 +25,7 @@
- printf("%d", num);
- }
-
-+void print_ln() {
-+ printf("\n");
-+}
-+
-EOF
-cat > patch3.patch <<\EOF
-diff --git a/main.c b/main.c
---- a/main.c
-+++ b/main.c
-@@ -1,9 +1,7 @@
--#include <stdlib.h>
- #include <stdio.h>
-
- int func(int num);
- void print_int(int num);
--void print_ln();
-
- int main() {
- int i;
-@@ -12,8 +10,6 @@
- print_int(func(i));
- }
-
-- print_ln();
--
- return 0;
- }
-
-@@ -25,7 +21,3 @@
- printf("%d", num);
- }
-
--void print_ln() {
-- printf("\n");
--}
--
-EOF
-cat > patch4.patch <<\EOF
-diff --git a/main.c b/main.c
---- a/main.c
-+++ b/main.c
-@@ -1,13 +1,14 @@
- #include <stdio.h>
-
- int func(int num);
--void print_int(int num);
-+int func2(int num);
-
- int main() {
- int i;
-
- for (i = 0; i < 10; i++) {
-- print_int(func(i));
-+ printf("%d", func(i));
-+ printf("%d", func3(i));
- }
-
- return 0;
-@@ -17,7 +18,7 @@
- return num * num;
- }
-
--void print_int(int num) {
-- printf("%d", num);
-+int func2(int num) {
-+ return num * num * num;
- }
-
-EOF
+cp ../t4109/patch1.patch .
+cp ../t4109/patch2.patch .
+cp ../t4109/patch3.patch .
+cp ../t4109/patch4.patch .
test_expect_success "S = git apply (1)" \
'git apply patch1.patch patch2.patch'
--- /dev/null
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,23 @@
++#include <stdio.h>
++
++int func(int num);
++void print_int(int num);
++
++int main() {
++ int i;
++
++ for (i = 0; i < 10; i++) {
++ print_int(func(i));
++ }
++
++ return 0;
++}
++
++int func(int num) {
++ return num * num;
++}
++
++void print_int(int num) {
++ printf("%d", num);
++}
++
--- /dev/null
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,7 +1,9 @@
++#include <stdlib.h>
+ #include <stdio.h>
+
+ int func(int num);
+ void print_int(int num);
++void print_ln();
+
+ int main() {
+ int i;
+@@ -10,6 +12,8 @@
+ print_int(func(i));
+ }
+
++ print_ln();
++
+ return 0;
+ }
+
+@@ -21,3 +25,7 @@
+ printf("%d", num);
+ }
+
++void print_ln() {
++ printf("\n");
++}
++
--- /dev/null
+cat > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,9 +1,7 @@
+-#include <stdlib.h>
+ #include <stdio.h>
+
+ int func(int num);
+ void print_int(int num);
+-void print_ln();
+
+ int main() {
+ int i;
+@@ -12,8 +10,6 @@
+ print_int(func(i));
+ }
+
+- print_ln();
+-
+ return 0;
+ }
+
+@@ -25,7 +21,3 @@
+ printf("%d", num);
+ }
+
+-void print_ln() {
+- printf("\n");
+-}
+-
--- /dev/null
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,13 +1,14 @@
+ #include <stdio.h>
+
+ int func(int num);
+-void print_int(int num);
++int func2(int num);
+
+ int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+- print_int(func(i));
++ printf("%d", func(i));
++ printf("%d", func3(i));
+ }
+
+ return 0;
+@@ -17,7 +18,7 @@
+ return num * num;
+ }
+
+-void print_int(int num) {
+- printf("%d", num);
++int func2(int num) {
++ return num * num * num;
+ }
+
'
# Also handcraft GNU diff output; note this has trailing whitespace.
-cat >gpatch.file <<\EOF &&
+tr '_' ' ' >gpatch.file <<\EOF &&
--- file1 2007-02-21 01:04:24.000000000 -0800
+++ file1+ 2007-02-21 01:07:44.000000000 -0800
@@ -1 +1 @@
-A
-+B
++B_
EOF
sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file &&
GIT_AUTHOR_NAME="Another Thor"
GIT_AUTHOR_EMAIL="a.thor@example.com"
-GIT_COMMITTER_NAME="Co M Miter"
+GIT_COMMITTER_NAME="Co M Miter"
GIT_COMMITTER_EMAIL="c.miter@example.com"
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
cd - &&
mv test_repo.git $HTTPD_DOCUMENT_ROOT_PATH
'
-
+
test_expect_success 'clone remote repository' '
cd "$ROOT_PATH" &&
git clone $HTTPD_URL/test_repo.git test_repo_clone
# Must fail due to conflict
test_must_fail git cherry-pick -n master &&
echo "editor not started" >.git/result &&
- test_must_fail GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" git commit &&
+ (
+ GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" &&
+ export GIT_EDITOR &&
+ test_must_fail git commit
+ ) &&
test "$(cat .git/result)" = "editor not started"
'
'
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+#
+# modified: dir1/modified
+#
+# Untracked files not listed (use -u option to show untracked files)
+EOF
+test_expect_success 'status -uno' '
+ mkdir dir3 &&
+ : > dir3/untracked1 &&
+ : > dir3/untracked2 &&
+ git status -uno >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status (status.showUntrackedFiles no)' '
+ git config status.showuntrackedfiles no
+ git status >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# dir3/
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'status -unormal' '
+ git status -unormal >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status (status.showUntrackedFiles normal)' '
+ git config status.showuntrackedfiles normal
+ git status >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# dir3/untracked1
+# dir3/untracked2
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'status -uall' '
+ git status -uall >output &&
+ test_cmp expect output
+'
+test_expect_success 'status (status.showUntrackedFiles all)' '
+ git config status.showuntrackedfiles all
+ git status >output &&
+ rm -rf dir3 &&
+ git config --unset status.showuntrackedfiles &&
+ test_cmp expect output
+'
+
cat > expect << \EOF
# On branch master
# Changes to be committed:
git fast-import &&
git cat-file commit i18n | grep "Áéí óú")
+'
+test_expect_success 'import/export-marks' '
+
+ git checkout -b marks master &&
+ git fast-export --export-marks=tmp-marks HEAD &&
+ test -s tmp-marks &&
+ cp tmp-marks ~ &&
+ test $(wc -l < tmp-marks) -eq 3 &&
+ test $(
+ git fast-export --import-marks=tmp-marks\
+ --export-marks=tmp-marks HEAD |
+ grep ^commit |
+ wc -l) \
+ -eq 0 &&
+ echo change > file &&
+ git commit -m "last commit" file &&
+ test $(
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks HEAD |
+ grep ^commit\ |
+ wc -l) \
+ -eq 1 &&
+ test $(wc -l < tmp-marks) -eq 4
+
'
cat > signed-tag-import << EOF
debug=t; shift ;;
-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
immediate=t; shift ;;
+ -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
+ export GIT_TEST_LONG=t; shift ;;
-h|--h|--he|--hel|--help)
help=t; shift ;;
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
"use \"git add/rm <file>...\" to update what will be committed";
static const char use_add_to_include_msg[] =
"use \"git add <file>...\" to include in what will be committed";
+enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
static int parse_status_slot(const char *var, int offset)
{
wt_status_print_changed(s);
if (wt_status_submodule_summary)
wt_status_print_submodule_summary(s);
- wt_status_print_untracked(s);
+ if (show_untracked_files)
+ wt_status_print_untracked(s);
+ else if (s->commitable)
+ fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
if (s->verbose && !s->is_initial)
wt_status_print_verbose(s);
printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
else if (s->is_initial)
printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
+ else if (!show_untracked_files)
+ printf("nothing to commit (use -u to show untracked files)\n");
else
printf("nothing to commit (working directory clean)\n");
}
wt_status_relative_paths = git_config_bool(k, v);
return 0;
}
+ if (!strcmp(k, "status.showuntrackedfiles")) {
+ if (!v)
+ return config_error_nonbool(v);
+ else if (!strcmp(v, "no"))
+ show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+ else if (!strcmp(v, "normal"))
+ show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ else if (!strcmp(v, "all"))
+ show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+ else
+ return error("Invalid untracked files mode '%s'", v);
+ return 0;
+ }
return git_color_default_config(k, v, cb);
}
WT_STATUS_NOBRANCH,
};
+enum untracked_status_type {
+ SHOW_NO_UNTRACKED_FILES,
+ SHOW_NORMAL_UNTRACKED_FILES,
+ SHOW_ALL_UNTRACKED_FILES
+};
+extern enum untracked_status_type show_untracked_files;
+
struct wt_status {
int is_initial;
char *branch;