Make "git diff --graph" work better with submodule log output.
* jk/diff-graph-submodule-summary:
submodule: print graph output next to submodule log
Fixes since v1.8.1.5
--------------------
+ * An earlier change to the attribute system introduced at v1.8.1.2 by
+ mistake stopped a pattern "dir" (without trailing slash) from
+ matching a directory "dir" (it only wanted to allow pattern "dir/"
+ to also match).
+
* The code to keep track of what directory names are known to Git on
platforms with case insensitive filesystems can get confused upon a
hash collision between these pathnames and looped forever.
Fixes since v1.8.2
------------------
+ * An earlier change to the attribute system introduced at v1.8.1.2 by
+ mistake stopped a pattern "dir" (without trailing slash) from
+ matching a directory "dir" (it only wanted to allow pattern "dir/"
+ to also match).
+
* Verification of signed tags were not done correctly when not in C
or en/US locale.
UI, Workflows & Features
+ * "git help" learned "-g" option to show the list of guides just like
+ list of commands are given with "-a".
+
+ * A triangular "pull from one place, push to another place" workflow
+ is supported better by new remote.pushdefault (overrides the
+ "origin" thing) and branch.*.pushremote (overrides the
+ branch.*.remote) configuration variables.
+
+ * "git status" learned to report that you are in the middle of a
+ revert session, just like it does for a cherry-pick and a bisect
+ session.
+
+ * The handling by "git branch --set-upstream-to" against various forms
+ of erroneous inputs was suboptimal and has been improved.
+
* When the interactive access to git-shell is not enabled, it issues
- a message meant to help the system admininstrator to enable it.
+ a message meant to help the system administrator to enable it.
An explicit way to help the end users who connect to the service by
issuing custom messages to refuse such an access has been added.
* The new "--follow-tags" option tells "git push" to push relevant
annotated tags when pushing branches out.
+ * "git merge" and "git pull" can optionally be told to inspect and
+ reject when merging a commit that does not carry a trusted GPG
+ signature.
+
* "git mergetool" now feeds files to the "p4merge" backend in the
order that matches the p4 convention, where "theirs" is usually
shown on the left side, which is the opposite from other backend
* Updates for building under msvc.
+ * The logic to coalesce the same lines removed from the parents in
+ the output from "diff -c/--cc" has been updated, but with an O(n^2)
+ complexity, so this might turn out to be undesirable.
+
+ * The code to enforce permission bits on files in $GIT_DIR/ for
+ shared repositories have been simplified.
+
* A few codepaths knew how much data they need to put in the
hashtables they use upfront, but still started from a small table
repeatedly growing and rehashing.
which have been consolidated. Also a bug in "git checkout dir/"
that is started from an unmerged index has been fixed.
+ * A few bugfixes to "git rerere" working on corner case merge
+ conflicts have been applied.
+
Also contains minor documentation updates and code clean-ups.
track are contained in this release (see release notes to them for
details).
+ * "git log -S/-G" started paying attention to textconv filter, but
+ there was no way to disable this. Make it honor --no-textconv
+ option.
+ (merge 61690bf sr/log-SG-no-textconv later to maint).
+
+ * When used with "-d temporary-directory" option, "git filter-branch"
+ failed to come back to the original working tree to perform the
+ final clean-up procedure.
+ (merge 9727601 jk/filter-branch-come-back-to-original later to maint).
+
+ * "git merge $(git rev-parse v1.8.2)" behaved quite differently from
+ "git merge v1.8.2", as if v1.8.2 were written as v1.8.2^0 and did
+ not pay much attention to the annotated tag payload. Make the code
+ notice the type of the tag object, in addition to the dwim_ref()
+ based classification the current code uses (i.e. the name appears
+ in refs/tags/) to decide when to special case merging of tags.
+ (merge a38d3d7 jc/merge-tag-object later to maint).
+
* Fix 1.8.1.x regression that stopped matching "dir" (without
trailing slash) to a directory "dir".
(merge efa5f82 jc/directory-attrs-regression-fix later to maint-1.8.1).
"--option=value" form.
(merge 862ae6c rs/submodule-summary-limit later to maint).
- * "index-pack --fix-thin" used uninitialize value to compute delta
- depths of objects it appends to the resulting pack.
+ * "index-pack --fix-thin" used an uninitialized value to compute
+ delta depths of objects it appends to the resulting pack.
(merge 57165db jk/index-pack-correct-depth-fix later to maint).
* "index-pack --verify-stat" used a few counters outside protection
alphabetical order.
* "git submodule update", when recursed into sub-submodules, did not
- acccumulate the prefix paths.
+ accumulate the prefix paths.
push @menu, $1;
}
s/\(\@pxref{\[(URLS|REMOTES)\]}\)//;
+ s/\@anchor\{[^{}]*\}//g;
print TMP;
}
close TMP;
This option defaults to never.
branch.<name>.remote::
- When in branch <name>, it tells 'git fetch' and 'git push' which
- remote to fetch from/push to. It defaults to `origin` if no remote is
- configured. `origin` is also used if you are not on any branch.
+ When on branch <name>, it tells 'git fetch' and 'git push'
+ which remote to fetch from/push to. The remote to push to
+ may be overridden with `remote.pushdefault` (for all branches).
+ The remote to push to, for the current branch, may be further
+ overridden by `branch.<name>.pushremote`. If no remote is
+ configured, or if you are not on any branch, it defaults to
+ `origin` for fetching and `remote.pushdefault` for pushing.
+
+branch.<name>.pushremote::
+ When on branch <name>, it overrides `branch.<name>.remote` for
+ pushing. It also overrides `remote.pushdefault` for pushing
+ from branch <name>. When you pull from one place (e.g. your
+ upstream) and push to another place (e.g. your own publishing
+ repository), you would want to set `remote.pushdefault` to
+ specify the remote to push to for all branches, and use this
+ option to override it for a specific branch.
branch.<name>.merge::
Defines, together with branch.<name>.remote, the upstream branch
If set to true, git-receive-pack will run git-update-server-info
after receiving data from git-push and updating refs.
+remote.pushdefault::
+ The remote to push to by default. Overrides
+ `branch.<name>.remote` for all branches, and is overridden by
+ `branch.<name>.pushremote` for specific branches.
+
remote.<name>.url::
The URL of a remote repository. See linkgit:git-fetch[1] or
linkgit:git-push[1].
Write the archive to <file> instead of stdout.
--worktree-attributes::
- Look for attributes in .gitattributes in working directory too.
+ Look for attributes in .gitattributes files in the working tree
+ as well (see <<ATTRIBUTES>>).
<extra>::
This can be any options that the archiver backend understands.
user-defined formats, but true for the "tar.gz" and "tgz"
formats.
+[[ATTRIBUTES]]
ATTRIBUTES
----------
without changing its commit message.
--amend::
- Used to amend the tip of the current branch. Prepare the tree
- object you would want to replace the latest commit as usual
- (this includes the usual -i/-o and explicit paths), and the
- commit log editor is seeded with the commit message from the
- tip of the current branch. The commit you create replaces the
- current tip -- if it was a merge, it will have the parents of
- the current tip as parents -- so the current top commit is
- discarded.
+ Replace the tip of the current branch by creating a new
+ commit. The recorded tree is prepared as usual (including
+ the effect of the `-i` and `-o` options and explicit
+ pathspec), and the message from the original commit is used
+ as the starting point, instead of an empty message, when no
+ other message is specified from the command line via options
+ such as `-m`, `-F`, `-c`, etc. The new commit has the same
+ parents and author as the current one (the `--reset-author`
+ option can countermand this).
+
--
It is a rough equivalent for:
`https://example.com/foo.git`, we might generate the following
credential description (don't forget the blank line at the end; it
tells `git credential` that the application finished feeding all the
-infomation it has):
+information it has):
protocol=https
host=example.com
incremental runs. As <file> is only opened and truncated
at completion, the same path can also be safely given to
\--import-marks.
+ The file will not be written if no new object has been
+ marked/exported.
--import-marks=<file>::
Before processing any input, load the marks specified in
SYNOPSIS
--------
[verse]
-'git help' [-a|--all|-i|--info|-m|--man|-w|--web] [COMMAND]
+'git help' [-a|--all] [-g|--guide]
+ [-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
DESCRIPTION
-----------
-With no options and no COMMAND given, the synopsis of the 'git'
+With no options and no COMMAND or GUIDE given, the synopsis of the 'git'
command and a list of the most commonly used Git commands are printed
on the standard output.
-If the option '--all' or '-a' is given, then all available commands are
+If the option '--all' or '-a' is given, all available commands are
printed on the standard output.
-If a Git subcommand is named, a manual page for that subcommand is brought
-up. The 'man' program is used by default for this purpose, but this
-can be overridden by other options or configuration variables.
+If the option '--guide' or '-g' is given, a list of the useful
+Git guides is also printed on the standard output.
+
+If a command, or a guide, is given, a manual page for that command or
+guide is brought up. The 'man' program is used by default for this
+purpose, but this can be overridden by other options or configuration
+variables.
Note that `git --help ...` is identical to `git help ...` because the
former is internally converted into the latter.
+To display the linkgit:git[1] man page, use `git help git`.
+
+This page can be displayed with 'git help help' or `git help --help`
+
OPTIONS
-------
-a::
--all::
Prints all the available commands on the standard output. This
- option supersedes any other option.
+ option overrides any given command or guide name.
+
+-g::
+--guides::
+ Prints a list of useful guides on the standard output. This
+ option overrides any given command or guide name.
-i::
--info::
edit .ssh/config.
"ext::socat -t3600 - ABSTRACT-CONNECT:/git-server %G/somerepo"::
- Represents repository with path /somerepo accessable over
+ Represents repository with path /somerepo accessible over
git protocol at abstract namespace address /git-server.
"ext::git-server-alias foo %G/repo"::
instead.
--verify::
- The parameter given must be usable as a single, valid
- object name. Otherwise barf and abort.
+ Verify that exactly one parameter is provided, and that it
+ can be turned into a raw 20-byte SHA-1 that can be used to
+ access the object database. If so, emit it to the standard
+ output; otherwise, error out.
++
+If you want to make sure that the output actually names an object in
+your object database and/or can be used as a specific type of object
+you require, you can add "^{type}" peeling operator to the parmeter.
+For example, `git rev-parse "$VAR^{commit}"` will make sure `$VAR`
+names an existing object that is a commit-ish (i.e. a commit, or an
+annotated tag that points at a commit). To make sure that `$VAR`
+names an existing object of any type, `git rev-parse "$VAR^{object}"`
+can be used.
-q::
--quiet::
* Print the commit object name from the revision in the $REV shell variable:
+
------------
-$ git rev-parse --verify $REV
+$ git rev-parse --verify $REV^{commit}
------------
+
This will error out if $REV is empty or not a valid revision.
-* Same as above:
+* Similar to above:
+
------------
$ git rev-parse --default master --verify $REV
patch), "all" (accept all patches), or "quit".
+
'git svn dcommit' returns immediately if answer if "no" or "quit", without
- commiting anything to SVN.
+ committing anything to SVN.
'branch'::
Create a branch in the SVN repository.
------------------------
If 'git svn' is configured to fetch branches (and --follow-branches
is in effect), it sometimes creates multiple Git branches for one
-SVN branch, where the addtional branches have names of the form
+SVN branch, where the additional branches have names of the form
'branchname@nnn' (with nnn an SVN revision number). These additional
branches are created if 'git svn' cannot find a parent commit for the
first commit in an SVN branch, to connect the branch to the history of
- *git.el* (contrib/)
- This is an Emacs interface for Git. The user interface is modeled on
+ This is an Emacs interface for Git. The user interface is modelled on
pcl-cvs. It has been developed on Emacs 21 and will probably need some
tweaking to work on XEmacs.
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.8.2/git.html[documentation for release 1.8.2]
+* link:v1.8.2.1/git.html[documentation for release 1.8.2.1]
* release notes for
+ link:RelNotes/1.8.2.1.txt[1.8.2.1].
link:RelNotes/1.8.2.txt[1.8.2].
-* link:v1.8.1.5/git.html[documentation for release 1.8.1.5]
+* link:v1.8.1.6/git.html[documentation for release 1.8.1.6]
* release notes for
+ link:RelNotes/1.8.1.6.txt[1.8.1.6],
link:RelNotes/1.8.1.5.txt[1.8.1.5],
link:RelNotes/1.8.1.4.txt[1.8.1.4],
link:RelNotes/1.8.1.3.txt[1.8.1.3],
This capability can be advertised multiple times. The first
applicable refspec takes precedence. The left-hand of refspecs
advertised with this capability must cover all refs reported by
-the list command. If no 'refspec' capability is advertised,
-there is an implied `refspec *:*`.
+the list command. If a helper does not need a specific 'refspec'
+capability then it should advertise `refspec *:*`.
'bidi-import'::
This modifies the 'import' capability.
Pass merge strategy specific option through to the merge
strategy.
+--verify-signatures::
+--no-verify-signatures::
+ Verify that the commits being merged have good and trusted GPG signatures
+ and abort the merge in case they do not.
+
--summary::
--no-summary::
Synonyms to --stat and --no-stat; these are deprecated and will be
this when the branches to be merged have diverged wildly.
See also linkgit:git-diff[1] `--patience`.
+diff-algorithm=[patience|minimal|histogram|myers];;
+ Tells 'merge-recursive' to use a different diff algorithm, which
+ can help avoid mismerges that occur due to unimportant matching
+ lines (such as braces from distinct functions). See also
+ linkgit:git-diff[1] `--diff-algorithm`.
+
ignore-space-change;;
ignore-all-space;;
ignore-space-at-eol;;
- '%B': raw body (unwrapped subject and body)
- '%N': commit notes
- '%GG': raw verification message from GPG for a signed commit
-- '%G?': show either "G" for Good or "B" for Bad for a signed commit
+- '%G?': show "G" for a Good signature, "B" for a Bad signature, "U" for a good,
+ untrusted signature and "N" for no signature
- '%GS': show the name of the signer for a signed commit
- '%GK': show the key used to sign a signed commit
- '%gD': reflog selector, e.g., `refs/stash@{1}`
+
Note that any of the 'refs/*' cases above may come either from
the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file.
-While the ref name encoding is unspecified, UTF-8 is prefered as
+While the ref name encoding is unspecified, UTF-8 is preferred as
some output processing may assume ref names in UTF-8.
'<refname>@\{<date>\}', e.g. 'master@\{yesterday\}', 'HEAD@\{5 minutes ago\}'::
initial, empty state.
`argv_array_detach`::
- Detach the argv array from the `struct argv_array`, transfering
+ Detach the argv array from the `struct argv_array`, transferring
ownership of the allocated array and strings.
`argv_array_free_detached`::
break;
default:
/*
- * Some other error occured. We don't know if the
+ * Some other error occurred. We don't know if the
* credential is good or bad, so report nothing to the
* credential subsystem.
*/
* `head_ref_submodule()`, `for_each_ref_submodule()`,
`for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`,
`for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
- do the same as the functions descibed above but for a specified
+ do the same as the functions described above but for a specified
submodule.
* `for_each_rawref()` can be used to learn about broken ref and symref.
(deltified representation)
n-byte type and length (3-bit type, (n-1)*7+4-bit length)
- 20-byte base object name
+ 20-byte base object name if OBJ_REF_DELTA or a negative relative
+ offset from the delta object's position in the pack if this
+ is an OBJ_OFS_DELTA object
compressed delta data
Observation: length of each object is encoded in a variable
- "openssl" library is used by git-imap-send to use IMAP over SSL.
If you don't need it, use NO_OPENSSL.
- By default, git uses OpenSSL for SHA1 but it will use it's own
+ By default, git uses OpenSSL for SHA1 but it will use its own
library (inspired by Mozilla's) with either NO_OPENSSL or
BLK_SHA1. Also included is a version optimized for PowerPC
(PPC_SHA1).
int advice_resolve_conflict = 1;
int advice_implicit_identity = 1;
int advice_detached_head = 1;
+int advice_set_upstream_failure = 1;
static struct {
const char *name;
{ "resolveconflict", &advice_resolve_conflict },
{ "implicitidentity", &advice_implicit_identity },
{ "detachedhead", &advice_detached_head },
+ { "setupstreamfailure", &advice_set_upstream_failure },
/* make this an alias for backward compatibility */
{ "pushnonfastforward", &advice_push_update_rejected }
extern int advice_resolve_conflict;
extern int advice_implicit_identity;
extern int advice_detached_head;
+extern int advice_set_upstream_failure;
int git_default_advice_config(const char *var, const char *value);
void advise(const char *advice, ...);
* is increased by one between each call, but that should not matter
* for this application.
*/
-static int get_prn(int count) {
+static unsigned get_prn(unsigned count) {
count = count * 1103515245 + 12345;
- return ((unsigned)(count/65536) % PRN_MODULO);
+ return (count/65536) % PRN_MODULO;
}
/*
return 1;
}
+static const char upstream_not_branch[] =
+N_("Cannot setup tracking information; starting point '%s' is not a branch.");
+static const char upstream_missing[] =
+N_("the requested upstream branch '%s' does not exist");
+static const char upstream_advice[] =
+N_("\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push.");
+
void create_branch(const char *head,
const char *name, const char *start_name,
int force, int reflog, int clobber_head,
}
real_ref = NULL;
- if (get_sha1(start_name, sha1))
+ if (get_sha1(start_name, sha1)) {
+ if (explicit_tracking) {
+ if (advice_set_upstream_failure) {
+ error(_(upstream_missing), start_name);
+ advise(_(upstream_advice));
+ exit(1);
+ }
+ die(_(upstream_missing), start_name);
+ }
die("Not a valid object name: '%s'.", start_name);
+ }
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
case 0:
/* Not branching from any existing branch */
if (explicit_tracking)
- die("Cannot setup tracking information; starting point is not a branch.");
+ die(_(upstream_not_branch), start_name);
break;
case 1:
/* Unique completion -- good, only if it is a real branch */
if (prefixcmp(real_ref, "refs/heads/") &&
prefixcmp(real_ref, "refs/remotes/")) {
if (explicit_tracking)
- die("Cannot setup tracking information; starting point is not a branch.");
+ die(_(upstream_not_branch), start_name);
else
real_ref = NULL;
}
}
/*
- * Read the patch text in "buffer" taht extends for "size" bytes; stop
+ * Read the patch text in "buffer" that extends for "size" bytes; stop
* reading after seeing a single patch (i.e. changes to a single file).
* Create fragments (i.e. patch hunks) and hang them to the given patch.
* Return the number of bytes consumed, so that the caller can call us
*
* The latter is needed to deal with a case where two paths A and B
* are swapped by first renaming A to B and then renaming B to A;
- * moving A to B should not be prevented due to presense of B as we
+ * moving A to B should not be prevented due to presence of B as we
* will remove it in a later patch.
*/
#define PATH_TO_BE_DELETED ((struct patch *) -2)
*
* A patch to swap-rename between A and B would first rename A
* to B and then rename B to A. While applying the first one,
- * the presense of B should not stop A from getting renamed to
+ * the presence of B should not stop A from getting renamed to
* B; ask to_be_deleted() about the later rename. Removal of
* B and rename from A to B is handled the same way by asking
* was_deleted().
else if (!strcmp(arg, "strip"))
signed_tag_mode = STRIP;
else
- return error("Unknown signed-tag mode: %s", arg);
+ return error("Unknown signed-tags mode: %s", arg);
return 0;
}
switch(signed_tag_mode) {
case ABORT:
die ("Encountered signed tag %s; use "
- "--signed-tag=<mode> to handle it.",
+ "--signed-tags=<mode> to handle it.",
sha1_to_hex(tag->object.sha1));
case WARN:
warning ("Exporting signed tag %s",
|| *mark_end != ' ' || get_sha1(mark_end + 1, sha1))
die("corrupt mark line: %s", line);
+ if (last_idnum < mark)
+ last_idnum = mark;
+
object = parse_object(sha1);
if (!object)
- die ("Could not read blob %s", sha1_to_hex(sha1));
+ continue;
if (object->flags & SHOWN)
error("Object %s already has a mark", sha1_to_hex(sha1));
continue;
mark_object(object, mark);
- if (last_idnum < mark)
- last_idnum = mark;
object->flags |= SHOWN;
}
struct string_list extra_refs = STRING_LIST_INIT_NODUP;
struct commit *commit;
char *export_filename = NULL, *import_filename = NULL;
+ uint32_t lastimportid;
struct option options[] = {
OPT_INTEGER(0, "progress", &progress,
N_("show progress after <n> objects")),
if (import_filename)
import_marks(import_filename);
+ lastimportid = last_idnum;
if (import_filename && revs.prune_data.nr)
full_tree = 1;
handle_tags_and_duplicates(&extra_refs);
- if (export_filename)
+ if (export_filename && lastimportid != last_idnum)
export_marks(export_filename);
if (use_done_feature)
const char *me;
if (kind == 'a') {
- label = "\n# By ";
+ label = "By";
me = git_author_info(IDENT_NO_DATE);
} else {
- label = "\n# Via ";
+ label = "Via";
me = git_committer_info(IDENT_NO_DATE);
}
(me = skip_prefix(me, them->items->string)) != NULL &&
skip_prefix(me, " <")))
return;
- strbuf_addstr(out, label);
+ strbuf_addf(out, "\n%c %s ", comment_line_char, label);
add_people_count(out, them);
}
} else {
if (tag_number == 2) {
struct strbuf tagline = STRBUF_INIT;
- strbuf_addf(&tagline, "\n# %s\n",
- origins.items[first_tag].string);
+ strbuf_addch(&tagline, '\n');
+ strbuf_add_commented_lines(&tagline,
+ origins.items[first_tag].string,
+ strlen(origins.items[first_tag].string));
strbuf_insert(&tagbuf, 0, tagline.buf,
tagline.len);
strbuf_release(&tagline);
}
- strbuf_addf(&tagbuf, "\n# %s\n",
- origins.items[i].string);
+ strbuf_addch(&tagbuf, '\n');
+ strbuf_add_commented_lines(&tagbuf,
+ origins.items[i].string,
+ strlen(origins.items[i].string));
fmt_tag_signature(&tagbuf, &sig, buf, len);
}
strbuf_release(&sig);
static const char *html_path;
static int show_all = 0;
+static int show_guides = 0;
static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
static struct option builtin_help_options[] = {
- OPT_BOOLEAN('a', "all", &show_all, N_("print all available commands")),
+ OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
+ OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
HELP_FORMAT_WEB),
};
static const char * const builtin_help_usage[] = {
- N_("git help [--all] [--man|--web|--info] [command]"),
+ N_("git help [--all] [--guides] [--man|--web|--info] [command]"),
NULL
};
open_html(page_path.buf);
}
+static struct {
+ const char *name;
+ const char *help;
+} common_guides[] = {
+ { "attributes", N_("Defining attributes per path") },
+ { "glossary", N_("A Git glossary") },
+ { "ignore", N_("Specifies intentionally untracked files to ignore") },
+ { "modules", N_("Defining submodule properties") },
+ { "revisions", N_("Specifying revisions and ranges for Git") },
+ { "tutorial", N_("A tutorial introduction to Git (for version 1.5.1 or newer)") },
+ { "workflows", N_("An overview of recommended workflows with Git") },
+};
+
+static void list_common_guides_help(void)
+{
+ int i, longest = 0;
+
+ for (i = 0; i < ARRAY_SIZE(common_guides); i++) {
+ if (longest < strlen(common_guides[i].name))
+ longest = strlen(common_guides[i].name);
+ }
+
+ puts(_("The common Git guides are:\n"));
+ for (i = 0; i < ARRAY_SIZE(common_guides); i++) {
+ printf(" %s ", common_guides[i].name);
+ mput_char(' ', longest - strlen(common_guides[i].name));
+ puts(_(common_guides[i].help));
+ }
+ putchar('\n');
+}
+
int cmd_help(int argc, const char **argv, const char *prefix)
{
int nongit;
git_config(git_help_config, NULL);
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
list_commands(colopts, &main_cmds, &other_cmds);
+ }
+
+ if (show_guides)
+ list_common_guides_help();
+
+ if (show_all || show_guides) {
printf("%s\n", _(git_more_info_string));
+ /*
+ * We're done. Ignore any remaining args
+ */
return 0;
}
static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = 1, allow_fast_forward = 1;
static int fast_forward_only, option_edit = -1;
-static int allow_trivial = 1, have_message;
+static int allow_trivial = 1, have_message, verify_signatures;
static int overwrite_ignore = 1;
static struct strbuf merge_msg = STRBUF_INIT;
static struct strategy **use_strategies;
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
N_("abort if fast-forward is not possible")),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+ OPT_BOOL(0, "verify-signatures", &verify_signatures,
+ N_("Verify that the named commit has a valid GPG signature")),
OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
N_("merge strategy to use"), option_parse_strategy),
OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
strbuf_release(&line);
goto cleanup;
}
+
+ if (remote_head->util) {
+ struct merge_remote_desc *desc;
+ desc = merge_remote_util(remote_head);
+ if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
+ strbuf_addf(msg, "%s\t\t%s '%s'\n",
+ sha1_to_hex(desc->obj->sha1),
+ typename(desc->obj->type),
+ remote);
+ goto cleanup;
+ }
+ }
+
strbuf_addf(msg, "%s\t\tcommit '%s'\n",
sha1_to_hex(remote_head->object.sha1), remote);
cleanup:
usage_with_options(builtin_merge_usage,
builtin_merge_options);
+ if (verify_signatures) {
+ for (p = remoteheads; p; p = p->next) {
+ struct commit *commit = p->item;
+ char hex[41];
+ struct signature_check signature_check;
+ memset(&signature_check, 0, sizeof(signature_check));
+
+ check_commit_signature(commit, &signature_check);
+
+ strcpy(hex, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+ switch (signature_check.result) {
+ case 'G':
+ break;
+ case 'U':
+ die(_("Commit %s has an untrusted GPG signature, "
+ "allegedly by %s."), hex, signature_check.signer);
+ case 'B':
+ die(_("Commit %s has a bad GPG signature "
+ "allegedly by %s."), hex, signature_check.signer);
+ default: /* 'N' */
+ die(_("Commit %s does not have a GPG signature."), hex);
+ }
+ if (verbosity >= 0 && signature_check.result == 'G')
+ printf(_("Commit %s has a good GPG signature by %s\n"),
+ hex, signature_check.signer);
+
+ free(signature_check.gpg_output);
+ free(signature_check.gpg_status);
+ free(signature_check.signer);
+ free(signature_check.key);
+ }
+ }
+
strbuf_addstr(&buf, "merge");
for (p = remoteheads; p; p = p->next)
strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
static int do_push(const char *repo, int flags)
{
int i, errs;
- struct remote *remote = remote_get(repo);
+ struct remote *remote = pushremote_get(repo);
const char **url;
int url_nr;
ce = active_cache[pos];
if (lstat(ce->name, &st) < 0) {
- if (errno != ENOENT)
+ if (errno != ENOENT && errno != ENOTDIR)
warning("'%s': %s", ce->name, strerror(errno));
/* It already vanished from the working tree */
continue;
nth = 0;
while (parents) {
struct commit *p = parents->item;
- char newname[1000], *en;
+ struct strbuf newname = STRBUF_INIT;
parents = parents->next;
nth++;
if (p->util)
continue;
- en = newname;
switch (n->generation) {
case 0:
- en += sprintf(en, "%s", n->head_name);
+ strbuf_addstr(&newname, n->head_name);
break;
case 1:
- en += sprintf(en, "%s^", n->head_name);
+ strbuf_addf(&newname, "%s^", n->head_name);
break;
default:
- en += sprintf(en, "%s~%d",
- n->head_name, n->generation);
+ strbuf_addf(&newname, "%s~%d",
+ n->head_name, n->generation);
break;
}
if (nth == 1)
- en += sprintf(en, "^");
+ strbuf_addch(&newname, '^');
else
- en += sprintf(en, "^%d", nth);
- name_commit(p, xstrdup(newname), 0);
+ strbuf_addf(&newname, "^%d", nth);
+ name_commit(p, strbuf_detach(&newname, NULL), 0);
i++;
name_first_parent_chain(p);
}
* followed by SP and subject line.
*/
if (get_sha1_hex(buf.buf, sha1) ||
- (40 <= buf.len && !isspace(buf.buf[40])) ||
+ (buf.len > 40 && !isspace(buf.buf[40])) ||
(!is_prereq && buf.len <= 40)) {
if (report_path)
error(_("unrecognized header: %s%s (%d)"),
PERM_EVERYBODY = 0664
};
int git_config_perm(const char *var, const char *value);
-int set_shared_perm(const char *path, int mode);
-#define adjust_shared_perm(path) set_shared_perm((path), 0)
+int adjust_shared_perm(const char *path);
int safe_create_leading_directories(char *path);
int safe_create_leading_directories_const(const char *path);
int mkdir_in_gitdir(const char *path);
/* Lines lost from parent */
struct lline {
- struct lline *next;
+ struct lline *next, *prev;
int len;
unsigned long parent_map;
char line[FLEX_ARRAY];
};
+/* Lines lost from current parent (before coalescing) */
+struct plost {
+ struct lline *lost_head, *lost_tail;
+ int len;
+};
+
/* Lines surviving in the merge result */
struct sline {
- struct lline *lost_head, **lost_tail;
- struct lline *next_lost;
+ /* Accumulated and coalesced lost lines */
+ struct lline *lost;
+ int lenlost;
+ struct plost plost;
char *bol;
int len;
/* bit 0 up to (N-1) are on if the parent has this line (i.e.
unsigned long *p_lno;
};
-static char *grab_blob(const unsigned char *sha1, unsigned int mode,
- unsigned long *size, struct userdiff_driver *textconv,
- const char *path)
-{
- char *blob;
- enum object_type type;
-
- if (S_ISGITLINK(mode)) {
- blob = xmalloc(100);
- *size = snprintf(blob, 100,
- "Subproject commit %s\n", sha1_to_hex(sha1));
- } else if (is_null_sha1(sha1)) {
- /* deleted blob */
- *size = 0;
- return xcalloc(1, 1);
- } else if (textconv) {
- struct diff_filespec *df = alloc_filespec(path);
- fill_filespec(df, sha1, 1, mode);
- *size = fill_textconv(textconv, df, &blob);
- free_filespec(df);
- } else {
- blob = read_sha1_file(sha1, &type, size);
- if (type != OBJ_BLOB)
- die("object '%s' is not a blob!", sha1_to_hex(sha1));
- }
- return blob;
-}
-
static int match_string_spaces(const char *line1, int len1,
const char *line2, int len2,
long flags)
return 0;
}
-static void append_lost(struct sline *sline, int n, const char *line, int len, long flags)
+enum coalesce_direction { MATCH, BASE, NEW };
+
+/* Coalesce new lines into base by finding LCS */
+static struct lline *coalesce_lines(struct lline *base, int *lenbase,
+ struct lline *new, int lennew,
+ unsigned long parent, long flags)
{
- struct lline *lline;
- unsigned long this_mask = (1UL<<n);
- if (line[len-1] == '\n')
- len--;
+ int **lcs;
+ enum coalesce_direction **directions;
+ struct lline *baseend, *newend = NULL;
+ int i, j, origbaselen = *lenbase;
- /* Check to see if we can squash things */
- if (sline->lost_head) {
- lline = sline->next_lost;
- while (lline) {
- if (match_string_spaces(lline->line, lline->len,
- line, len, flags)) {
- lline->parent_map |= this_mask;
- sline->next_lost = lline->next;
- return;
+ if (new == NULL)
+ return base;
+
+ if (base == NULL) {
+ *lenbase = lennew;
+ return new;
+ }
+
+ /*
+ * Coalesce new lines into base by finding the LCS
+ * - Create the table to run dynamic programing
+ * - Compute the LCS
+ * - Then reverse read the direction structure:
+ * - If we have MATCH, assign parent to base flag, and consume
+ * both baseend and newend
+ * - Else if we have BASE, consume baseend
+ * - Else if we have NEW, insert newend lline into base and
+ * consume newend
+ */
+ lcs = xcalloc(origbaselen + 1, sizeof(int*));
+ directions = xcalloc(origbaselen + 1, sizeof(enum coalesce_direction*));
+ for (i = 0; i < origbaselen + 1; i++) {
+ lcs[i] = xcalloc(lennew + 1, sizeof(int));
+ directions[i] = xcalloc(lennew + 1, sizeof(enum coalesce_direction));
+ directions[i][0] = BASE;
+ }
+ for (j = 1; j < lennew + 1; j++)
+ directions[0][j] = NEW;
+
+ for (i = 1, baseend = base; i < origbaselen + 1; i++) {
+ for (j = 1, newend = new; j < lennew + 1; j++) {
+ if (match_string_spaces(baseend->line, baseend->len,
+ newend->line, newend->len, flags)) {
+ lcs[i][j] = lcs[i - 1][j - 1] + 1;
+ directions[i][j] = MATCH;
+ } else if (lcs[i][j - 1] >= lcs[i - 1][j]) {
+ lcs[i][j] = lcs[i][j - 1];
+ directions[i][j] = NEW;
+ } else {
+ lcs[i][j] = lcs[i - 1][j];
+ directions[i][j] = BASE;
+ }
+ if (newend->next)
+ newend = newend->next;
+ }
+ if (baseend->next)
+ baseend = baseend->next;
+ }
+
+ for (i = 0; i < origbaselen + 1; i++)
+ free(lcs[i]);
+ free(lcs);
+
+ /* At this point, baseend and newend point to the end of each lists */
+ i--;
+ j--;
+ while (i != 0 || j != 0) {
+ if (directions[i][j] == MATCH) {
+ baseend->parent_map |= 1<<parent;
+ baseend = baseend->prev;
+ newend = newend->prev;
+ i--;
+ j--;
+ } else if (directions[i][j] == NEW) {
+ struct lline *lline;
+
+ lline = newend;
+ /* Remove lline from new list and update newend */
+ if (lline->prev)
+ lline->prev->next = lline->next;
+ else
+ new = lline->next;
+ if (lline->next)
+ lline->next->prev = lline->prev;
+
+ newend = lline->prev;
+ j--;
+
+ /* Add lline to base list */
+ if (baseend) {
+ lline->next = baseend->next;
+ lline->prev = baseend;
+ if (lline->prev)
+ lline->prev->next = lline;
}
- lline = lline->next;
+ else {
+ lline->next = base;
+ base = lline;
+ }
+ (*lenbase)++;
+
+ if (lline->next)
+ lline->next->prev = lline;
+
+ } else {
+ baseend = baseend->prev;
+ i--;
}
}
+ newend = new;
+ while (newend) {
+ struct lline *lline = newend;
+ newend = newend->next;
+ free(lline);
+ }
+
+ for (i = 0; i < origbaselen + 1; i++)
+ free(directions[i]);
+ free(directions);
+
+ return base;
+}
+
+static char *grab_blob(const unsigned char *sha1, unsigned int mode,
+ unsigned long *size, struct userdiff_driver *textconv,
+ const char *path)
+{
+ char *blob;
+ enum object_type type;
+
+ if (S_ISGITLINK(mode)) {
+ blob = xmalloc(100);
+ *size = snprintf(blob, 100,
+ "Subproject commit %s\n", sha1_to_hex(sha1));
+ } else if (is_null_sha1(sha1)) {
+ /* deleted blob */
+ *size = 0;
+ return xcalloc(1, 1);
+ } else if (textconv) {
+ struct diff_filespec *df = alloc_filespec(path);
+ fill_filespec(df, sha1, 1, mode);
+ *size = fill_textconv(textconv, df, &blob);
+ free_filespec(df);
+ } else {
+ blob = read_sha1_file(sha1, &type, size);
+ if (type != OBJ_BLOB)
+ die("object '%s' is not a blob!", sha1_to_hex(sha1));
+ }
+ return blob;
+}
+
+static void append_lost(struct sline *sline, int n, const char *line, int len)
+{
+ struct lline *lline;
+ unsigned long this_mask = (1UL<<n);
+ if (line[len-1] == '\n')
+ len--;
+
lline = xmalloc(sizeof(*lline) + len + 1);
lline->len = len;
lline->next = NULL;
+ lline->prev = sline->plost.lost_tail;
+ if (lline->prev)
+ lline->prev->next = lline;
+ else
+ sline->plost.lost_head = lline;
+ sline->plost.lost_tail = lline;
+ sline->plost.len++;
lline->parent_map = this_mask;
memcpy(lline->line, line, len);
lline->line[len] = 0;
- *sline->lost_tail = lline;
- sline->lost_tail = &lline->next;
- sline->next_lost = NULL;
}
struct combine_diff_state {
int n;
struct sline *sline;
struct sline *lost_bucket;
- long flags;
};
static void consume_line(void *state_, char *line, unsigned long len)
xcalloc(state->num_parent,
sizeof(unsigned long));
state->sline[state->nb-1].p_lno[state->n] = state->ob;
- state->lost_bucket->next_lost = state->lost_bucket->lost_head;
return;
}
if (!state->lost_bucket)
return; /* not in any hunk yet */
switch (line[0]) {
case '-':
- append_lost(state->lost_bucket, state->n, line+1, len-1, state->flags);
+ append_lost(state->lost_bucket, state->n, line+1, len-1);
break;
case '+':
state->sline[state->lno-1].flag |= state->nmask;
xpp.flags = flags;
memset(&xecfg, 0, sizeof(xecfg));
memset(&state, 0, sizeof(state));
- state.flags = flags;
state.nmask = nmask;
state.sline = sline;
state.lno = 1;
struct lline *ll;
sline[lno].p_lno[n] = p_lno;
+ /* Coalesce new lines */
+ if (sline[lno].plost.lost_head) {
+ struct sline *sl = &sline[lno];
+ sl->lost = coalesce_lines(sl->lost, &sl->lenlost,
+ sl->plost.lost_head,
+ sl->plost.len, n, flags);
+ sl->plost.lost_head = sl->plost.lost_tail = NULL;
+ sl->plost.len = 0;
+ }
+
/* How many lines would this sline advance the p_lno? */
- ll = sline[lno].lost_head;
+ ll = sline[lno].lost;
while (ll) {
if (ll->parent_map & nmask)
p_lno++; /* '-' means parent had it */
/* If some parents lost lines here, or if we have added to
* some parent, it is interesting.
*/
- return ((sline->flag & all_mask) || sline->lost_head);
+ return ((sline->flag & all_mask) || sline->lost);
}
static unsigned long adjust_hunk_tail(struct sline *sline,
has_interesting = 0;
for (j = i; j < hunk_end && !has_interesting; j++) {
unsigned long this_diff = sline[j].flag & all_mask;
- struct lline *ll = sline[j].lost_head;
+ struct lline *ll = sline[j].lost;
if (this_diff) {
/* This has some changes. Is it the
* same as others?
int j;
unsigned long p_mask;
struct sline *sl = &sline[lno++];
- ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head;
+ ll = (sl->flag & no_pre_delete) ? NULL : sl->lost;
while (ll) {
printf("%s%s", line_prefix, c_old);
for (j = 0; j < num_parent; j++) {
jmask = (1UL<<j);
for (lno = 0; lno <= cnt; lno++) {
- struct lline *ll = sline->lost_head;
+ struct lline *ll = sline->lost;
sline->p_lno[i] = sline->p_lno[j];
while (ll) {
if (ll->parent_map & jmask)
sline = xcalloc(cnt+2, sizeof(*sline));
sline[0].bol = result;
- for (lno = 0; lno <= cnt + 1; lno++) {
- sline[lno].lost_tail = &sline[lno].lost_head;
- sline[lno].flag = 0;
- }
for (lno = 0, cp = result; cp < result + result_size; cp++) {
if (*cp == '\n') {
sline[lno].len = cp - sline[lno].bol;
free(result);
for (lno = 0; lno < cnt; lno++) {
- if (sline[lno].lost_head) {
- struct lline *ll = sline[lno].lost_head;
+ if (sline[lno].lost) {
+ struct lline *ll = sline[lno].lost;
while (ll) {
struct lline *tmp = ll;
ll = ll->next;
}
/*
- * Is "commit" a decendant of one of the elements on the "with_commit" list?
+ * Is "commit" a descendant of one of the elements on the "with_commit" list?
*/
int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
{
free(buf);
}
+static struct {
+ char result;
+ const char *check;
+} sigcheck_gpg_status[] = {
+ { 'G', "\n[GNUPG:] GOODSIG " },
+ { 'B', "\n[GNUPG:] BADSIG " },
+ { 'U', "\n[GNUPG:] TRUST_NEVER" },
+ { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
+};
+
+static void parse_gpg_output(struct signature_check *sigc)
+{
+ const char *buf = sigc->gpg_status;
+ int i;
+
+ /* Iterate over all search strings */
+ for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
+ const char *found, *next;
+
+ if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) {
+ /* At the very beginning of the buffer */
+ found = buf + strlen(sigcheck_gpg_status[i].check + 1);
+ } else {
+ found = strstr(buf, sigcheck_gpg_status[i].check);
+ if (!found)
+ continue;
+ found += strlen(sigcheck_gpg_status[i].check);
+ }
+ sigc->result = sigcheck_gpg_status[i].result;
+ /* The trust messages are not followed by key/signer information */
+ if (sigc->result != 'U') {
+ sigc->key = xmemdupz(found, 16);
+ found += 17;
+ next = strchrnul(found, '\n');
+ sigc->signer = xmemdupz(found, next - found);
+ }
+ }
+}
+
+void check_commit_signature(const struct commit* commit, struct signature_check *sigc)
+{
+ struct strbuf payload = STRBUF_INIT;
+ struct strbuf signature = STRBUF_INIT;
+ struct strbuf gpg_output = STRBUF_INIT;
+ struct strbuf gpg_status = STRBUF_INIT;
+ int status;
+
+ sigc->result = 'N';
+
+ if (parse_signed_commit(commit->object.sha1,
+ &payload, &signature) <= 0)
+ goto out;
+ status = verify_signed_buffer(payload.buf, payload.len,
+ signature.buf, signature.len,
+ &gpg_output, &gpg_status);
+ if (status && !gpg_output.len)
+ goto out;
+ sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
+ sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
+ parse_gpg_output(sigc);
+
+ out:
+ strbuf_release(&gpg_status);
+ strbuf_release(&gpg_output);
+ strbuf_release(&payload);
+ strbuf_release(&signature);
+}
+
+
+
void append_merge_tag_headers(struct commit_list *parents,
struct commit_extra_header ***tail)
{
#include "tree.h"
#include "strbuf.h"
#include "decorate.h"
+#include "gpg-interface.h"
struct commit_list {
struct commit *item;
extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
-/* largest postive number a signed 32-bit integer can contain */
+/* largest positive number a signed 32-bit integer can contain */
#define INFINITE_DEPTH 0x7fffffff
extern int register_shallow(const unsigned char *sha1);
const char *format_cur,
const char *format_last);
+/*
+ * Check the signature of the given commit. The result of the check is stored
+ * in sig->check_result, 'G' for a good signature, 'U' for a good signature
+ * from an untrusted signer, 'B' for a bad signature and 'N' for no signature
+ * at all. This may allocate memory for sig->gpg_output, sig->gpg_status,
+ * sig->signer and sig->key.
+ */
+extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
+
#endif /* COMMIT_H */
void dlfree(void* mem) {
/*
- Consolidate freed chunks with preceeding or succeeding bordering
+ Consolidate freed chunks with preceding or succeeding bordering
free chunks, if they exist, and then place in a bin. Intermixed
with special cases for top, dv, mmapped chunks, and usage errors.
*/
Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
* Use last_remainder in more cases.
* Pack bins using idea from colin@nyx10.cs.du.edu
- * Use ordered bins instead of best-fit threshhold
+ * Use ordered bins instead of best-fit threshold
* Eliminate block-local decls to simplify tracing and debugging.
* Support another case of realloc via move into top
- * Fix error occuring when initial sbrk_base not word-aligned.
+ * Fix error occurring when initial sbrk_base not word-aligned.
* Rely on page size for units instead of SBRK_UNIT to
avoid surprises about sbrk alignment conventions.
* Add mallinfo, mallopt. Thanks to Raymond Nijssen
#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A)))
-/* Similiar to _BPTR_ALIGN (B, P, A), except optimize the common case
+/* Similar to _BPTR_ALIGN (B, P, A), except optimize the common case
where pointers can be converted to integers, aligned as integers,
and converted back again. If PTR_INT_TYPE is narrower than a
pointer (e.g., the AS/400), play it safe and compute the alignment
if (prec_dir->ic_precompose == (iconv_t)-1) {
die("iconv_open(%s,%s) failed, but needed:\n"
" precomposed unicode is not supported.\n"
- " If you wnat to use decomposed unicode, run\n"
+ " If you want to use decomposed unicode, run\n"
" \"git config core.precomposeunicode false\"\n",
repo_encoding, path_encoding);
} else {
/* Entry point of the parser.
Parse the regular expression REGEXP and return the structure tree.
- If an error is occured, ERR is set by error code, and return NULL.
+ If an error has occurred, ERR is set by error code, and return NULL.
This function build the following tree, from regular expression <reg_exp>:
CAT
/ \
/* This is intended for the expressions like "a{1,3}".
Fetch a number from `input', and return the number.
Return -1, if the number field is empty like "{,1}".
- Return -2, If an error is occured. */
+ Return -2, if an error has occurred. */
static int
fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
#include "config.h"
#endif
-/* Make sure noone compiles this code with a C++ compiler. */
+/* Make sure no one compiles this code with a C++ compiler. */
#ifdef __cplusplus
# error "This is C code, use a C compiler"
#endif
/* Insert the new element ELEM to the re_node_set* SET.
SET should not already have ELEM.
- return -1 if an error is occured, return 1 otherwise. */
+ return -1 if an error has occurred, return 1 otherwise. */
static int
internal_function
/* Insert the new element ELEM to the re_node_set* SET.
SET should not already have any element greater than or equal to ELEM.
- Return -1 if an error is occured, return 1 otherwise. */
+ Return -1 if an error has occurred, return 1 otherwise. */
static int
internal_function
\f
/* Add the token TOKEN to dfa->nodes, and return the index of the token.
- Or return -1, if an error will be occured. */
+ Or return -1, if an error has occurred. */
static int
internal_function
use constant SLASH_REPLACEMENT => "%2F";
# It's not always possible to delete pages (may require some
-# priviledges). Deleted pages are replaced with this content.
+# privileges). Deleted pages are replaced with this content.
use constant DELETED_CONTENT => "[[Category:Deleted]]\n";
# It's not possible to create empty pages. New empty files in Git are
if ($fetch_from == 1 && $n == 0) {
print STDERR "You appear to have cloned an empty MediaWiki.\n";
# Something has to be done remote-helper side. If nothing is done, an error is
- # thrown saying that HEAD is refering to unknown object 0000000000000000000
+ # thrown saying that HEAD is referring to unknown object 0000000000000000000
# and the clone fails.
}
}
my $file_content;
if ($page_deleted) {
# Deleting a page usually requires
- # special priviledges. A common
+ # special privileges. A common
# convention is to replace the page
# with this content instead:
$file_content = DELETED_CONTENT;
The test environment makes it easy to install and manipulate one or
several MediaWiki instances. To allow developers to run the testsuite
-easily, the environment does not require root priviledge (except to
+easily, the environment does not require root privilege (except to
install the required packages if needed). It starts a webserver
instance on the user's account (using lighttpd greatly helps for
that), and does not need a separate database daemon (thanks to the use
** `test_check_wiki_precond`:
Check if the tests must be skipped or not. Please use this function
-at the beggining of each new test file.
+at the beginning of each new test file.
** `wiki_getpage`:
Fetch a given page from the wiki and puts its content in the
** `wiki_reset`:
Reset the wiki, i.e. flush the database. Use this function at the
-begining of each new test, except if the test re-uses the same wiki
+beginning of each new test, except if the test re-uses the same wiki
(and history) as the previous test.
How to write a new test
## Set $wgCacheDirectory to a writable directory on the web server
## to make your wiki go slightly faster. The directory should not
-## be publically accessible from the web.
+## be publicly accessible from the web.
#$wgCacheDirectory = "$IP/cache";
# Site language code, should be one of the list in ./languages/Names.php
'
-test_expect_failure 'capital at the begining of file names' '
+test_expect_failure 'capital at the beginning of file names' '
wiki_reset &&
git clone mediawiki::'"$WIKI_URL"' mw_dir_10 &&
(
'
-test_expect_failure 'special character at the begining of file name from mw to git' '
+test_expect_failure 'special character at the beginning of file name from mw to git' '
wiki_reset &&
git clone mediawiki::'"$WIKI_URL"' mw_dir_11 &&
wiki_editpage {char_1 "expect to be renamed {char_1" false &&
wiki_page_exist NotANameSpace:Page
'
-test_expect_success 'test of correct formating for file name from mw to git' '
+test_expect_success 'test of correct formatting for file name from mw to git' '
wiki_reset &&
git clone mediawiki::'"$WIKI_URL"' mw_dir_12 &&
wiki_editpage char_%_7b_1 "expect to be renamed char{_1" false &&
'
-test_expect_failure 'test of correct formating for file name begining with special character' '
+test_expect_failure 'test of correct formatting for file name beginning with special character' '
wiki_reset &&
git clone mediawiki::'"$WIKI_URL"' mw_dir_13 &&
(
echo "my new file {char_1" >\{char_1.mw &&
echo "my new file [char_2" >\[char_2.mw &&
git add . &&
- git commit -am "commiting some exotic file name..." &&
+ git commit -am "committing some exotic file name..." &&
git push &&
git pull
) &&
'
-test_expect_success 'test of correct formating for file name from git to mw' '
+test_expect_success 'test of correct formatting for file name from git to mw' '
wiki_reset &&
git clone mediawiki::'"$WIKI_URL"' mw_dir_14 &&
(
echo "my new file char{_1" >Char\{_1.mw &&
echo "my new file char[_2" >Char\[_2.mw &&
git add . &&
- git commit -m "commiting some exotic file name..." &&
+ git commit -m "committing some exotic file name..." &&
git push
) &&
wiki_getallpage ref_page_14 &&
export T := $(addprefix $(CURDIR)/,$(TESTS))
export MAKE := $(MAKE) -e
export PATH := $(CURDIR):$(PATH)
+export TEST_LINT := test-lint-executable test-lint-shell-syntax
test:
$(MAKE) -C ../../t $@
import bzrlib.generate_ids
import bzrlib.transport
+import bzrlib.errors
import sys
import os
changes = cur.changes_from(prev)
+ def u(s):
+ return s.encode('utf-8')
+
for path, fid, kind in changes.added:
- modified[path] = fid
+ modified[u(path)] = fid
for path, fid, kind in changes.removed:
- removed[path] = None
+ removed[u(path)] = None
for path, fid, kind, mod, _ in changes.modified:
- modified[path] = fid
+ modified[u(path)] = fid
for oldpath, newpath, fid, kind, mod, _ in changes.renamed:
- removed[oldpath] = None
- modified[newpath] = fid
+ removed[u(oldpath)] = None
+ if kind == 'directory':
+ lst = cur.list_files(from_dir=newpath, recursive=True)
+ for path, file_class, kind, fid, entry in lst:
+ if kind != 'directory':
+ modified[u(newpath + '/' + path)] = fid
+ else:
+ modified[u(newpath)] = fid
return modified, removed
tz = rev.timezone
committer = rev.committer.encode('utf-8')
committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz))
- author = committer
+ authors = rev.get_apparent_authors()
+ if authors:
+ author = authors[0].encode('utf-8')
+ author = "%s %u %s" % (fixup_user(author), time, gittz(tz))
+ else:
+ author = committer
msg = rev.message.encode('utf-8')
msg += '\n'
else:
print "merge :%s" % m
+ for f in removed:
+ print "D %s" % (f,)
for f in modified_final:
print "M %s :%u %s" % f
- for f in removed:
- print "D %s" % (f)
print
count += 1
def export_tag(repo, name):
global tags
- try:
- print "reset refs/tags/%s" % name
- print "from :%u" % rev_to_mark(tags[name])
- print
- except KeyError:
- warn("TODO: fetch tag '%s'" % name)
+ print "reset refs/tags/%s" % name
+ print "from :%u" % rev_to_mark(tags[name])
+ print
def do_import(parser):
global dirname
def get_symlink_target(self, file_id):
return self.updates[file_id]['data']
+def c_style_unescape(string):
+ if string[0] == string[-1] == '"':
+ return string.decode('string-escape')[1:-1]
+ return string
+
def parse_commit(parser):
global marks, blob_marks, bmarks, parsed_refs
global mode
f = { 'deleted' : True }
else:
die('Unknown file command: %s' % line)
+ path = c_style_unescape(path).decode('utf-8')
files[path] = f
repo = parser.repo
peer.import_last_revision_info_and_tags(repo, revno, revid)
else:
peer.import_last_revision_info(repo.repository, revno, revid)
- wt = peer.bzrdir.open_workingtree()
else:
wt = repo.bzrdir.open_workingtree()
- wt.update()
+ wt.update()
print "ok %s" % ref
print
print
+def ref_is_valid(name):
+ return not True in [c in name for c in '~^: \\']
+
def do_list(parser):
global tags
print "? refs/heads/%s" % 'master'
- for tag, revid in parser.repo.tags.get_tag_dict().items():
+
+ branch = parser.repo
+ branch.lock_read()
+ for tag, revid in branch.tags.get_tag_dict().items():
+ try:
+ branch.revision_id_to_dotted_revno(revid)
+ except bzrlib.errors.NoSuchRevision:
+ continue
+ if not ref_is_valid(tag):
+ continue
print "? refs/tags/%s" % tag
tags[tag] = revid
+ branch.unlock()
print "@refs/heads/%s HEAD" % 'master'
print
test_done
fi
-cmd='
-import bzrlib
-bzrlib.initialize()
-import bzrlib.plugin
-bzrlib.plugin.load_plugins()
-import bzrlib.plugins.fastimport
-'
-
-if ! "$PYTHON_PATH" -c "$cmd"; then
- echo "consider setting BZR_PLUGIN_PATH=$HOME/.bazaar/plugins" 1>&2
- skip_all='skipping remote-bzr tests; bzr-fastimport not available'
- test_done
-fi
-
check () {
(cd $1 &&
git log --format='%s' -1 &&
(cd gitrepo &&
git cat-file -p HEAD:link > ../actual) &&
- echo -n content > expected &&
+ printf content > expected &&
+ test_cmp expected actual
+'
+
+cat > expected <<EOF
+100644 blob 54f9d6da5c91d556e6b54340b1327573073030af content
+100755 blob 68769579c3eaadbe555379b9c3538e6628bae1eb executable
+120000 blob 6b584e8ece562ebffc15d38808cd6b98fc3d97ea link
+040000 tree 35c0caa46693cef62247ac89a680f0c5ce32b37b movedir-new
+EOF
+
+test_expect_success 'moving directory' '
+ (cd bzrrepo &&
+ mkdir movedir &&
+ echo one > movedir/one &&
+ echo two > movedir/two &&
+ bzr add movedir &&
+ bzr commit -m movedir &&
+ bzr mv movedir movedir-new &&
+ bzr commit -m movedir-new) &&
+
+ (cd gitrepo &&
+ git pull &&
+ git ls-tree HEAD > ../actual) &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'different authors' '
+ (cd bzrrepo &&
+ echo john >> content &&
+ bzr commit -m john \
+ --author "Jane Rey <jrey@example.com>" \
+ --author "John Doe <jdoe@example.com>") &&
+
+ (cd gitrepo &&
+ git pull &&
+ git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual) &&
+
+ echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'fetch utf-8 filenames' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" &&
+
+ LC_ALL=en_US.UTF-8
+ export LC_ALL
+ (
+ bzr init bzrrepo &&
+ cd bzrrepo &&
+
+ echo test >> "ærø" &&
+ bzr add "ærø" &&
+ echo test >> "ø~?" &&
+ bzr add "ø~?" &&
+ bzr commit -m add-utf-8 &&
+ echo test >> "ærø" &&
+ bzr commit -m test-utf-8 &&
+ bzr rm "ø~?" &&
+ bzr mv "ærø" "ø~?" &&
+ bzr commit -m bzr-mv-utf-8
+ ) &&
+
+ (
+ git clone "bzr::$PWD/bzrrepo" gitrepo &&
+ cd gitrepo &&
+ git -c core.quotepath=false ls-files > ../actual
+ ) &&
+ echo "ø~?" > expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'push utf-8 filenames' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" &&
+
+ LC_ALL=en_US.UTF-8
+ export LC_ALL
+
+ (
+ bzr init bzrrepo &&
+ cd bzrrepo &&
+
+ echo one >> content &&
+ bzr add content &&
+ bzr commit -m one
+ ) &&
+
+ (
+ git clone "bzr::$PWD/bzrrepo" gitrepo &&
+ cd gitrepo &&
+
+ echo test >> "ærø" &&
+ git add "ærø" &&
+ git commit -m utf-8 &&
+
+ git push
+ ) &&
+
+ (cd bzrrepo && bzr ls > ../actual) &&
+ printf "content\nærø\n" > expected &&
test_cmp expected actual
'
git_clone_$x hgrepo-$x gitrepo2-$x &&
git_log gitrepo2-$x > log-$x
done &&
- cp -r log-* output-* /tmp/foo/ &&
test_cmp output-hg output-git &&
test_cmp log-hg log-git
git push
) &&
- hg -R hgrepo bookmarks | grep "devel\s\+3:"
+ hg -R hgrepo bookmarks | egrep "devel[ ]+3:"
'
test_done
test_expect_success 'split for main-sub5 without --onto' '
# also test that we still can split out an entirely new subtree
# if the parent of the first commit in the tree is not empty,
- # then the new subtree has accidently been attached to something
+ # then the new subtree has accidentally been attached to something
git subtree split --prefix subdir2 --branch mainsub5 &&
check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
'
* Binary files are displayed with "Bin XXX -> YYY bytes"
* instead of the change count and graph. This part is treated
* similarly to the graph part, except that it is not
- * "scaled". If total width is too small to accomodate the
+ * "scaled". If total width is too small to accommodate the
* guaranteed minimum width of the filename part and the
* separators and this message, this message will "overflow"
* making the line longer than the maximum width.
options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
else if (!strcmp(arg, "--histogram"))
options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
- else if (!prefixcmp(arg, "--diff-algorithm=")) {
- long value = parse_algorithm_value(arg+17);
+ else if ((argcount = parse_long_opt("diff-algorithm", av, &optarg))) {
+ long value = parse_algorithm_value(optarg);
if (value < 0)
return error("option diff-algorithm accepts \"myers\", "
"\"minimal\", \"patience\" and \"histogram\"");
DIFF_XDL_CLR(options, NEED_MINIMAL);
options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
options->xdl_opts |= value;
+ return argcount;
}
/* flags options */
if (max_size < MINIMUM_BREAK_SIZE)
return 0; /* we do not break too small filepair */
+ if (!src->size)
+ return 0; /* we do not let empty files get renamed */
+
if (diffcore_count_changes(src, dst,
&src->cnt_data, &dst->cnt_data,
0,
#include "xdiff-interface.h"
#include "kwset.h"
-typedef int (*pickaxe_fn)(struct diff_filepair *p, struct diff_options *o, regex_t *regexp, kwset_t kws);
+typedef int (*pickaxe_fn)(mmfile_t *one, mmfile_t *two,
+ struct diff_options *o,
+ regex_t *regexp, kwset_t kws);
+
+static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
+ regex_t *regexp, kwset_t kws, pickaxe_fn fn);
static void pickaxe(struct diff_queue_struct *q, struct diff_options *o,
regex_t *regexp, kwset_t kws, pickaxe_fn fn)
/* Showing the whole changeset if needle exists */
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- if (fn(p, o, regexp, kws))
+ if (pickaxe_match(p, o, regexp, kws, fn))
return; /* do not munge the queue */
}
/* Showing only the filepairs that has the needle */
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- if (fn(p, o, regexp, kws))
+ if (pickaxe_match(p, o, regexp, kws, fn))
diff_q(&outq, p);
else
diff_free_filepair(p);
line[len] = hold;
}
-static void fill_one(struct diff_filespec *one,
- mmfile_t *mf, struct userdiff_driver **textconv)
-{
- if (DIFF_FILE_VALID(one)) {
- *textconv = get_textconv(one);
- mf->size = fill_textconv(*textconv, one, &mf->ptr);
- } else {
- memset(mf, 0, sizeof(*mf));
- }
-}
-
-static int diff_grep(struct diff_filepair *p, struct diff_options *o,
+static int diff_grep(mmfile_t *one, mmfile_t *two,
+ struct diff_options *o,
regex_t *regexp, kwset_t kws)
{
regmatch_t regmatch;
- struct userdiff_driver *textconv_one = NULL;
- struct userdiff_driver *textconv_two = NULL;
- mmfile_t mf1, mf2;
- int hit;
+ struct diffgrep_cb ecbdata;
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
- if (diff_unmodified_pair(p))
- return 0;
+ if (!one)
+ return !regexec(regexp, two->ptr, 1, ®match, 0);
+ if (!two)
+ return !regexec(regexp, one->ptr, 1, ®match, 0);
- fill_one(p->one, &mf1, &textconv_one);
- fill_one(p->two, &mf2, &textconv_two);
-
- if (!mf1.ptr) {
- if (!mf2.ptr)
- return 0; /* ignore unmerged */
- /* created "two" -- does it have what we are looking for? */
- hit = !regexec(regexp, mf2.ptr, 1, ®match, 0);
- } else if (!mf2.ptr) {
- /* removed "one" -- did it have what we are looking for? */
- hit = !regexec(regexp, mf1.ptr, 1, ®match, 0);
- } else {
- /*
- * We have both sides; need to run textual diff and see if
- * the pattern appears on added/deleted lines.
- */
- struct diffgrep_cb ecbdata;
- xpparam_t xpp;
- xdemitconf_t xecfg;
-
- memset(&xpp, 0, sizeof(xpp));
- memset(&xecfg, 0, sizeof(xecfg));
- ecbdata.regexp = regexp;
- ecbdata.hit = 0;
- xecfg.ctxlen = o->context;
- xecfg.interhunkctxlen = o->interhunkcontext;
- xdi_diff_outf(&mf1, &mf2, diffgrep_consume, &ecbdata,
- &xpp, &xecfg);
- hit = ecbdata.hit;
- }
- if (textconv_one)
- free(mf1.ptr);
- if (textconv_two)
- free(mf2.ptr);
- return hit;
+ /*
+ * We have both sides; need to run textual diff and see if
+ * the pattern appears on added/deleted lines.
+ */
+ memset(&xpp, 0, sizeof(xpp));
+ memset(&xecfg, 0, sizeof(xecfg));
+ ecbdata.regexp = regexp;
+ ecbdata.hit = 0;
+ xecfg.ctxlen = o->context;
+ xecfg.interhunkctxlen = o->interhunkcontext;
+ xdi_diff_outf(one, two, diffgrep_consume, &ecbdata,
+ &xpp, &xecfg);
+ return ecbdata.hit;
}
static void diffcore_pickaxe_grep(struct diff_options *o)
return cnt;
}
-static int has_changes(struct diff_filepair *p, struct diff_options *o,
+static int has_changes(mmfile_t *one, mmfile_t *two,
+ struct diff_options *o,
regex_t *regexp, kwset_t kws)
{
- struct userdiff_driver *textconv_one = get_textconv(p->one);
- struct userdiff_driver *textconv_two = get_textconv(p->two);
+ if (!one)
+ return contains(two, o, regexp, kws) != 0;
+ if (!two)
+ return contains(one, o, regexp, kws) != 0;
+ return contains(one, o, regexp, kws) != contains(two, o, regexp, kws);
+}
+
+static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
+ regex_t *regexp, kwset_t kws, pickaxe_fn fn)
+{
+ struct userdiff_driver *textconv_one = NULL;
+ struct userdiff_driver *textconv_two = NULL;
mmfile_t mf1, mf2;
int ret;
if (!o->pickaxe[0])
return 0;
+ /* ignore unmerged */
+ if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))
+ return 0;
+
+ if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+ textconv_one = get_textconv(p->one);
+ textconv_two = get_textconv(p->two);
+ }
+
/*
* If we have an unmodified pair, we know that the count will be the
* same and don't even have to load the blobs. Unless textconv is in
if (textconv_one == textconv_two && diff_unmodified_pair(p))
return 0;
- fill_one(p->one, &mf1, &textconv_one);
- fill_one(p->two, &mf2, &textconv_two);
+ mf1.size = fill_textconv(textconv_one, p->one, &mf1.ptr);
+ mf2.size = fill_textconv(textconv_two, p->two, &mf2.ptr);
- if (!mf1.ptr) {
- if (!mf2.ptr)
- ret = 0; /* ignore unmerged */
- /* created */
- ret = contains(&mf2, o, regexp, kws) != 0;
- }
- else if (!mf2.ptr) /* removed */
- ret = contains(&mf1, o, regexp, kws) != 0;
- else
- ret = contains(&mf1, o, regexp, kws) !=
- contains(&mf2, o, regexp, kws);
+ ret = fn(DIFF_FILE_VALID(p->one) ? &mf1 : NULL,
+ DIFF_FILE_VALID(p->two) ? &mf2 : NULL,
+ o, regexp, kws);
if (textconv_one)
free(mf1.ptr);
{
char *slash;
- if (unlink(name) && errno != ENOENT)
+ if (unlink(name) && errno != ENOENT && errno != ENOTDIR)
return -1;
slash = strrchr(name, '/');
# Print a one-line summary of each hunk in the array ref in
-# the first argument, starting wih the index in the 2nd.
+# the first argument, starting with the index in the 2nd.
sub display_hunks {
my ($hunks, $i) = @_;
my $ctr = 0;
}
# Cleanup various junk in filename (try to canonicalize it), and
-# add prependdir to accomodate running CVS client from a
+# add prependdir to accommodate running CVS client from a
# subdirectory (so the output is relative to top directory of the project).
sub filecleanup
{
# the numerical value of the corresponding byte plus
# 100.
# - "plus 100" avoids "0"s, and also reduces the
- # likelyhood of a collision in the case that someone someday
+ # likelihood of a collision in the case that someone someday
# writes an import tool that tries to preserve original
# CVS revision numbers, and the original CVS data had done
# lots of branches off of branches and other strangeness to
test -d "$tempdir" &&
die "$tempdir already exists, please remove it"
esac
+orig_dir=$(pwd)
mkdir -p "$tempdir/t" &&
tempdir="$(cd "$tempdir"; pwd)" &&
cd "$tempdir/t" &&
die ""
# Remove tempdir on exit
-trap 'cd ../..; rm -rf "$tempdir"' 0
+trap 'cd "$orig_dir"; rm -rf "$tempdir"' 0
ORIG_GIT_DIR="$GIT_DIR"
ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
done
fi
-cd ../..
+cd "$orig_dir"
rm -rf "$tempdir"
trap - 0
test -f "$GIT_DIR/MERGE_HEAD" && die_merge
strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
-log_arg= verbosity= progress= recurse_submodules=
+log_arg= verbosity= progress= recurse_submodules= verify_signatures=
merge_args= edit=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short="${curr_branch#refs/heads/}"
--no-recurse-submodules)
recurse_submodules=--no-recurse-submodules
;;
+ --verify-signatures)
+ verify_signatures=--verify-signatures
+ ;;
+ --no-verify-signatures)
+ verify_signatures=--no-verify-signatures
+ ;;
--d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
dry_run=--dry-run
;;
eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
;;
*)
- eval="git-merge $diffstat $no_commit $edit $squash $no_ff $ff_only"
+ eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
eval="$eval \"\$merge_name\" HEAD $merge_head"
;;
tmp_info="$tmp_dir/info"
-# Find the intial commit
+# Find the initial commit
commit=$(git rev-parse HEAD)
mkdir $tmp_dir || exit 2
($sender) = expand_aliases($sender) if defined $sender;
-# returns 1 if the conflict must be solved using it as a format-patch argument
-sub check_file_rev_conflict($) {
+# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if
+# $f is a revision list specification to be passed to format-patch.
+sub is_format_patch_arg {
return unless $repo;
my $f = shift;
try {
* Giving --format-patch option if you mean a range.
EOF
} catch Git::Error::Command with {
+ # Not a valid revision. Treat it as a filename.
return 0;
}
}
if ($f eq "--") {
push @rev_list_opts, "--", @ARGV;
@ARGV = ();
- } elsif (-d $f and !check_file_rev_conflict($f)) {
+ } elsif (-d $f and !is_format_patch_arg($f)) {
opendir my $dh, $f
or die "Failed to opendir $f: $!";
push @files, grep { -f $_ } map { catfile($f, $_) }
sort readdir $dh;
closedir $dh;
- } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
+ } elsif ((-f $f or -p $f) and !is_format_patch_arg($f)) {
push @files, $f;
} else {
push @rev_list_opts, $f;
}
}
}
- return undef;
+ return;
}
my %broken_encoding;
# less robust/correct than the monster regexp in Email::Valid,
# but still does a 99% job, and one less dependency
return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/;
- return undef;
+ return;
}
sub extract_valid_address_or_die {
my $sanitized_sender = sanitize_address($sender);
my @addresses = ();
- open my $fh, "$cmd \Q$file\E |"
+ open my $fh, "-|", "$cmd \Q$file\E"
or die "($prefix) Could not execute '$cmd'";
while (my $address = <$fh>) {
$address =~ s/^\s*//g;
return "$.: patch contains a line longer than 998 characters";
}
}
- return undef;
+ return;
}
sub file_has_nonascii {
if test -z "$force"
then
- git rm -n "$sm_path" ||
+ git rm -qn "$sm_path" ||
die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
fi
- rm -rf "$sm_path" || say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
+ rm -rf "$sm_path" &&
+ say "$(eval_gettext "Cleared directory '\$sm_path'")" ||
+ say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
fi
mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
" <command> [<args>]";
const char git_more_info_string[] =
- N_("See 'git help <command>' for more information on a specific command.");
+ N_("'git help -a' and 'git help -g' lists available subcommands and some\n"
+ "concept guides. See 'git help <command>' or 'git help <concept>'\n"
+ "to read about a specific subcommand or concept.");
static struct startup_info git_startup_info;
static int use_pager = -1;
through the GITWEB_CONFIG_SYSTEM environment variable.
Note that if per-instance configuration file exists, then system-wide
- configuration is _not used at all_. This is quite untypical and suprising
+ configuration is _not used at all_. This is quite untypical and surprising
behavior. On the other hand changing current behavior would break backwards
compatibility and can lead to unexpected changes in gitweb behavior.
Therefore gitweb also looks for common system-wide configuration file,
our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
- # Protect agains duplications of file names, to not read config twice.
+ # Protect against duplications of file names, to not read config twice.
# Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
# there possibility of duplication of filename there doesn't matter.
$GITWEB_CONFIG = "" if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
# to avoid infinite loop where error occurs in die_error,
# change handler to default handler, disabling handle_errors_html
- set_message("Error occured when inside die_error:\n$msg");
+ set_message("Error occurred when inside die_error:\n$msg");
# you cannot jump out of die_error when called as error handler;
# the subroutine set via CGI::Carp::set_message is called _after_
system(git_cmd(), "cat-file", '-e', $hash_base) == 0
or die_error(404, "Base object does not exist");
- # here errors should not hapen
+ # here errors should not happen
open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
or die_error(500, "Open git-ls-tree failed");
my $line = <$fd>;
#ifndef GPG_INTERFACE_H
#define GPG_INTERFACE_H
+struct signature_check {
+ char *gpg_output;
+ char *gpg_status;
+ char result; /* 0 (not checked),
+ * N (checked but no further result),
+ * U (untrusted good),
+ * G (good)
+ * B (bad) */
+ char *signer;
+ char *key;
+};
+
extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output, struct strbuf *gpg_status);
extern int git_gpg_config(const char *, const char *, void *);
ret = 0;
break;
case HTTP_ERROR:
- http_error(url, HTTP_ERROR);
+ error("unable to access '%s': %s", url, curl_errorstr);
default:
ret = -1;
}
int handle_curl_result(struct slot_results *results)
{
+ /*
+ * If we see a failing http code with CURLE_OK, we have turned off
+ * FAILONERROR (to keep the server's custom error response), and should
+ * translate the code into failure here.
+ */
+ if (results->curl_result == CURLE_OK &&
+ results->http_code >= 400) {
+ results->curl_result = CURLE_HTTP_RETURNED_ERROR;
+ /*
+ * Normally curl will already have put the "reason phrase"
+ * from the server into curl_errorstr; unfortunately without
+ * FAILONERROR it is lost, so we can give only the numeric
+ * status code.
+ */
+ snprintf(curl_errorstr, sizeof(curl_errorstr),
+ "The requested URL returned error: %ld",
+ results->http_code);
+ }
+
if (results->curl_result == CURLE_OK) {
credential_approve(&http_auth);
return HTTP_OK;
strbuf_addstr(&buf, "Pragma:");
if (options & HTTP_NO_CACHE)
strbuf_addstr(&buf, " no-cache");
+ if (options & HTTP_KEEP_ERROR)
+ curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
headers = curl_slist_append(headers, buf.buf);
run_active_slot(slot);
ret = handle_curl_result(&results);
} else {
- error("Unable to start HTTP request for %s", url);
+ snprintf(curl_errorstr, sizeof(curl_errorstr),
+ "failed to start HTTP request");
ret = HTTP_START_FAILED;
}
int ret = http_request(url, type, result, target, options);
if (ret != HTTP_REAUTH)
return ret;
+
+ /*
+ * If we are using KEEP_ERROR, the previous request may have
+ * put cruft into our output stream; we should clear it out before
+ * making our next request. We only know how to do this for
+ * the strbuf case, but that is enough to satisfy current callers.
+ */
+ if (options & HTTP_KEEP_ERROR) {
+ switch (target) {
+ case HTTP_REQUEST_STRBUF:
+ strbuf_reset(result);
+ break;
+ default:
+ die("BUG: HTTP_KEEP_ERROR is only supported with strbufs");
+ }
+ }
return http_request(url, type, result, target, options);
}
return ret;
}
-int http_error(const char *url, int ret)
-{
- /* http_request has already handled HTTP_START_FAILED. */
- if (ret != HTTP_START_FAILED)
- error("%s while accessing %s", curl_errorstr, url);
-
- return ret;
-}
-
int http_fetch_ref(const char *base, struct ref *ref)
{
char *url;
/* Options for http_request_*() */
#define HTTP_NO_CACHE 1
+#define HTTP_KEEP_ERROR 2
/* Return values for http_request_*() */
#define HTTP_OK 0
*/
int http_get_strbuf(const char *url, struct strbuf *content_type, struct strbuf *result, int options);
-/*
- * Prints an error message using error() containing url and curl_errorstr,
- * and returns ret.
- */
-int http_error(const char *url, int ret);
-
extern int http_fetch_ref(const char *base, struct ref *ref);
/* Helpers for fetching packs */
The author may be reached (Email) at the address mike@ai.mit.edu,
or (US mail) as Mike Haertel c/o Free Software Foundation. */
-/* The algorithm implemented by these routines bears a startling resemblence
+/* The algorithm implemented by these routines bears a startling resemblance
to one discovered by Beate Commentz-Walter, although it is not identical.
See "A String Matching Algorithm Fast on the Average," Technical Report,
IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900
/* Update the delta table for the descendents of this node. */
treedelta(curr->links, curr->depth, delta);
- /* Compute the failure function for the decendents of this node. */
+ /* Compute the failure function for the descendants of this node. */
treefails(curr->links, curr->fail, kwset->trie);
/* Update the shifts at each node in the current node's chain
/*
- * I'm tired of doing "vsnprintf()" etc just to open a
- * file, so here's a "return static buffer with printf"
- * interface for paths.
- *
- * It's obviously not thread-safe. Sue me. But it's quite
- * useful for doing things like
- *
- * f = open(mkpath("%s/%s.git", base, name), O_RDONLY);
- *
- * which is what it's designed for.
+ * Utilities for paths and pathnames
*/
#include "cache.h"
#include "strbuf.h"
return NULL;
}
-int set_shared_perm(const char *path, int mode)
+static int calc_shared_perm(int mode)
{
- int tweak, shared, orig_mode;
+ int tweak;
- if (!shared_repository) {
- if (mode)
- return chmod(path, mode & ~S_IFMT);
- return 0;
- }
- if (!mode) {
- if (get_st_mode_bits(path, &mode) < 0)
- return -1;
- orig_mode = mode;
- } else
- orig_mode = 0;
if (shared_repository < 0)
- shared = -shared_repository;
+ tweak = -shared_repository;
else
- shared = shared_repository;
- tweak = shared;
+ tweak = shared_repository;
if (!(mode & S_IWUSR))
tweak &= ~0222;
else
mode |= tweak;
- if (S_ISDIR(mode)) {
+ return mode;
+}
+
+
+int adjust_shared_perm(const char *path)
+{
+ int old_mode, new_mode;
+
+ if (!shared_repository)
+ return 0;
+ if (get_st_mode_bits(path, &old_mode) < 0)
+ return -1;
+
+ new_mode = calc_shared_perm(old_mode);
+ if (S_ISDIR(old_mode)) {
/* Copy read bits to execute bits */
- mode |= (shared & 0444) >> 2;
- mode |= FORCE_DIR_SET_GID;
+ new_mode |= (new_mode & 0444) >> 2;
+ new_mode |= FORCE_DIR_SET_GID;
}
- if (((shared_repository < 0
- ? (orig_mode & (FORCE_DIR_SET_GID | 0777))
- : (orig_mode & mode)) != mode) &&
- chmod(path, (mode & ~S_IFMT)) < 0)
+ if (((old_mode ^ new_mode) & ~S_IFMT) &&
+ chmod(path, (new_mode & ~S_IFMT)) < 0)
return -2;
return 0;
}
=item temp_acquire ( NAME )
-Attempts to retreive the temporary file mapped to the string C<NAME>. If an
+Attempts to retrieve the temporary file mapped to the string C<NAME>. If an
associated temp file has not been created this session or was closed, it is
created, cached, and set for autoflush and binmode.
if (not defined $pid) {
throw Error::Simple("open failed: $!");
} elsif ($pid == 0) {
- if (defined $opts{STDERR}) {
- close STDERR;
- }
if ($opts{STDERR}) {
open (STDERR, '>&', $opts{STDERR})
or die "dup failed: $!";
+ } elsif (defined $opts{STDERR}) {
+ open (STDERR, '>', '/dev/null')
+ or die "opening /dev/null failed: $!";
}
_cmd_exec($self, $cmd, @args);
}
print __("Welcome to Git!\n");
- printf __("The following error occured: %s\n"), $error;
+ printf __("The following error occurred: %s\n"), $error;
=head1 DESCRIPTION
=head2 CONSTRUCTORS
The C<Error> object is implemented as a HASH. This HASH is initialized
-with the arguments that are passed to it's constructor. The elements
+with the arguments that are passed to its constructor. The elements
that are used by, or are retrievable by the C<Error> class are listed
below, other classes may add to these.
=item Error::Simple
-This class can be used to hold simple error strings and values. It's
+This class can be used to hold simple error strings and values. Its
constructor takes two arguments. The first is a text value, the second
is a numeric value. These values are what will be returned by the
overload methods.
If the text value ends with C<at file line 1> as $@ strings do, then
-this infomation will be used to set the C<-file> and C<-line> arguments
+this information will be used to set the C<-file> and C<-line> arguments
of the error object.
This class is used internally if an eval'd block die's with an error
# To interpolate variables:
details="oh noes"
- eval_gettext "An error occured: \$details"; echo
+ eval_gettext "An error occurred: \$details"; echo
In addition we have wrappers for messages that end with a trailing
newline. I.e. you could write the above as:
# To interpolate variables:
details="oh noes"
- eval_gettextln "An error occured: \$details"
+ eval_gettextln "An error occurred: \$details"
More documentation about the interface is available in the GNU info
page: `info '(gettext)sh'`. Looking at git-am.sh (the first shell
use Git::I18N;
print __("Welcome to Git!\n");
- printf __("The following error occured: %s\n"), $error;
+ printf __("The following error occurred: %s\n"), $error;
Run `perldoc perl/Git/I18N.pm` for more info.
const struct pretty_print_context *pretty_ctx;
unsigned commit_header_parsed:1;
unsigned commit_message_parsed:1;
- unsigned commit_signature_parsed:1;
- struct {
- char *gpg_output;
- char *gpg_status;
- char good_bad;
- char *signer;
- char *key;
- } signature;
+ struct signature_check signature_check;
char *message;
size_t width, indent1, indent2;
c->indent2 = new_indent2;
}
-static struct {
- char result;
- const char *check;
-} signature_check[] = {
- { 'G', "\n[GNUPG:] GOODSIG " },
- { 'B', "\n[GNUPG:] BADSIG " },
-};
-
-static void parse_signature_lines(struct format_commit_context *ctx)
-{
- const char *buf = ctx->signature.gpg_status;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(signature_check); i++) {
- const char *found = strstr(buf, signature_check[i].check);
- const char *next;
- if (!found)
- continue;
- ctx->signature.good_bad = signature_check[i].result;
- found += strlen(signature_check[i].check);
- ctx->signature.key = xmemdupz(found, 16);
- found += 17;
- next = strchrnul(found, '\n');
- ctx->signature.signer = xmemdupz(found, next - found);
- break;
- }
-}
-
-static void parse_commit_signature(struct format_commit_context *ctx)
-{
- struct strbuf payload = STRBUF_INIT;
- struct strbuf signature = STRBUF_INIT;
- struct strbuf gpg_output = STRBUF_INIT;
- struct strbuf gpg_status = STRBUF_INIT;
- int status;
-
- ctx->commit_signature_parsed = 1;
-
- if (parse_signed_commit(ctx->commit->object.sha1,
- &payload, &signature) <= 0)
- goto out;
- status = verify_signed_buffer(payload.buf, payload.len,
- signature.buf, signature.len,
- &gpg_output, &gpg_status);
- if (status && !gpg_output.len)
- goto out;
- ctx->signature.gpg_output = strbuf_detach(&gpg_output, NULL);
- ctx->signature.gpg_status = strbuf_detach(&gpg_status, NULL);
- parse_signature_lines(ctx);
-
- out:
- strbuf_release(&gpg_status);
- strbuf_release(&gpg_output);
- strbuf_release(&payload);
- strbuf_release(&signature);
-}
-
-
static int format_reflog_person(struct strbuf *sb,
char part,
struct reflog_walk_info *log,
}
if (placeholder[0] == 'G') {
- if (!c->commit_signature_parsed)
- parse_commit_signature(c);
+ if (!c->signature_check.result)
+ check_commit_signature(c->commit, &(c->signature_check));
switch (placeholder[1]) {
case 'G':
- if (c->signature.gpg_output)
- strbuf_addstr(sb, c->signature.gpg_output);
+ if (c->signature_check.gpg_output)
+ strbuf_addstr(sb, c->signature_check.gpg_output);
break;
case '?':
- switch (c->signature.good_bad) {
+ switch (c->signature_check.result) {
case 'G':
case 'B':
- strbuf_addch(sb, c->signature.good_bad);
+ case 'U':
+ case 'N':
+ strbuf_addch(sb, c->signature_check.result);
}
break;
case 'S':
- if (c->signature.signer)
- strbuf_addstr(sb, c->signature.signer);
+ if (c->signature_check.signer)
+ strbuf_addstr(sb, c->signature_check.signer);
break;
case 'K':
- if (c->signature.key)
- strbuf_addstr(sb, c->signature.key);
+ if (c->signature_check.key)
+ strbuf_addstr(sb, c->signature_check.key);
break;
}
return 2;
rewrap_message_tail(sb, &context, 0, 0, 0);
logmsg_free(context.message, commit);
- free(context.signature.gpg_output);
- free(context.signature.signer);
+ free(context.signature_check.gpg_output);
+ free(context.signature_check.signer);
}
static void pp_header(const struct pretty_print_context *pp,
}
}
+static int show_http_message(struct strbuf *type, struct strbuf *msg)
+{
+ const char *p, *eol;
+
+ /*
+ * We only show text/plain parts, as other types are likely
+ * to be ugly to look at on the user's terminal.
+ *
+ * TODO should handle "; charset=XXX", and re-encode into
+ * logoutputencoding
+ */
+ if (strcasecmp(type->buf, "text/plain"))
+ return -1;
+
+ strbuf_trim(msg);
+ if (!msg->len)
+ return -1;
+
+ p = msg->buf;
+ do {
+ eol = strchrnul(p, '\n');
+ fprintf(stderr, "remote: %.*s\n", (int)(eol - p), p);
+ p = eol + 1;
+ } while(*eol);
+ return 0;
+}
+
static struct discovery* discover_refs(const char *service, int for_push)
{
struct strbuf exp = STRBUF_INIT;
}
refs_url = strbuf_detach(&buffer, NULL);
- http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
+ http_ret = http_get_strbuf(refs_url, &type, &buffer,
+ HTTP_NO_CACHE | HTTP_KEEP_ERROR);
switch (http_ret) {
case HTTP_OK:
break;
case HTTP_MISSING_TARGET:
- die("%s not found: did you run git update-server-info on the"
- " server?", refs_url);
+ show_http_message(&type, &buffer);
+ die("repository '%s' not found", url);
case HTTP_NOAUTH:
- die("Authentication failed");
+ show_http_message(&type, &buffer);
+ die("Authentication failed for '%s'", url);
default:
- http_error(refs_url, http_ret);
- die("HTTP request failed");
+ show_http_message(&type, &buffer);
+ die("unable to access '%s': %s", url, curl_errorstr);
}
last= xcalloc(1, sizeof(*last_discovery));
static struct branch *current_branch;
static const char *default_remote_name;
+static const char *pushremote_name;
static int explicit_default_remote_name;
static struct rewrites rewrites;
return 0;
branch = make_branch(name, subkey - name);
if (!strcmp(subkey, ".remote")) {
- if (!value)
- return config_error_nonbool(key);
- branch->remote_name = xstrdup(value);
+ if (git_config_string(&branch->remote_name, key, value))
+ return -1;
if (branch == current_branch) {
default_remote_name = branch->remote_name;
explicit_default_remote_name = 1;
}
+ } else if (!strcmp(subkey, ".pushremote")) {
+ if (branch == current_branch)
+ if (git_config_string(&pushremote_name, key, value))
+ return -1;
} else if (!strcmp(subkey, ".merge")) {
if (!value)
return config_error_nonbool(key);
add_instead_of(rewrite, xstrdup(value));
}
}
+
if (prefixcmp(key, "remote."))
return 0;
name = key + 7;
+
+ /* Handle remote.* variables */
+ if (!strcmp(name, "pushdefault"))
+ return git_config_string(&pushremote_name, key, value);
+
+ /* Handle remote.<name>.* variables */
if (*name == '/') {
warning("Config remote shorthand cannot begin with '/': %s",
name);
return !strchr(name, '/'); /* no slash */
}
-struct remote *remote_get(const char *name)
+static struct remote *remote_get_1(const char *name, const char *pushremote_name)
{
struct remote *ret;
int name_given = 0;
- read_config();
if (name)
name_given = 1;
else {
- name = default_remote_name;
- name_given = explicit_default_remote_name;
+ if (pushremote_name) {
+ name = pushremote_name;
+ name_given = 1;
+ } else {
+ name = default_remote_name;
+ name_given = explicit_default_remote_name;
+ }
}
ret = make_remote(name, 0);
return ret;
}
+struct remote *remote_get(const char *name)
+{
+ read_config();
+ return remote_get_1(name, NULL);
+}
+
+struct remote *pushremote_get(const char *name)
+{
+ read_config();
+ return remote_get_1(name, pushremote_name);
+}
+
int remote_is_configured(const char *name)
{
int i;
};
struct remote *remote_get(const char *name);
+struct remote *pushremote_get(const char *name);
int remote_is_configured(const char *name);
typedef int each_remote_fn(struct remote *remote, void *priv);
strbuf_release(sb);
if (!io->input.len)
return -1;
- ep = strchrnul(io->input.buf, '\n');
- if (*ep == '\n')
+ ep = memchr(io->input.buf, '\n', io->input.len);
+ if (!ep)
+ ep = io->input.buf + io->input.len;
+ else if (*ep == '\n')
ep++;
len = ep - io->input.buf;
strbuf_add(sb, io->input.buf, len);
static int handle_cache(const char *path, unsigned char *sha1, const char *output)
{
- mmfile_t mmfile[3];
+ mmfile_t mmfile[3] = {{NULL}};
mmbuffer_t result = {NULL, 0};
struct cache_entry *ce;
int pos, len, i, hunk_no;
for (i = 0; i < 3; i++) {
enum object_type type;
unsigned long size;
+ int j;
- mmfile[i].size = 0;
- mmfile[i].ptr = NULL;
if (active_nr <= pos)
break;
ce = active_cache[pos++];
- if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
- || ce_stage(ce) != i + 1)
- break;
- mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
- mmfile[i].size = size;
+ if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
+ continue;
+ j = ce_stage(ce) - 1;
+ mmfile[j].ptr = read_sha1_file(ce->sha1, &type, &size);
+ mmfile[j].size = size;
}
for (i = 0; i < 3; i++) {
if (!mmfile[i].ptr && !mmfile[i].size)
return st;
}
-static void remove_treesame_parents(struct commit *commit)
-{
- struct commit_list **pp, *p;
-
- pp = &commit->parents;
- while ((p = *pp) != NULL) {
- struct commit *parent = p->item;
- if (parent->object.flags & TREESAME) {
- *pp = p->next;
- free(p);
- continue;
- }
- pp = &p->next;
- }
-}
-
static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
{
struct commit_list *p;
break;
}
- if (revs->first_parent_only) {
+ if (revs->first_parent_only)
cnt = 1;
- } else {
- /*
- * A merge with a tree-same parent is useless
- */
- if (commit->parents && commit->parents->next)
- remove_treesame_parents(commit);
-
+ else
cnt = remove_duplicate_parents(commit);
- }
/*
* It is possible that we are a merge and one side branch
if (msg) {
fprintf(stderr, "%s\n", msg);
/*
- * A conflict has occured but the porcelain
+ * A conflict has occurred but the porcelain
* (typically rebase --interactive) wants to take care
* of the commit itself so remove CHERRY_PICK_HEAD
*/
--immediate::
This causes the test to immediately exit upon the first
- failed test.
+ failed test. Cleanup commands requested with
+ test_when_finished are not executed if the test failed,
+ in order to keep the state for inspection by the tester
+ to diagnose the bug.
--long-tests::
This causes additional long-running tests to be run (where
The process retains the same pid across exec(2). See fb9a2bea for
details.
+ - PIPE
+
+ The filesystem we're on supports creation of FIFOs (named pipes)
+ via mkfifo(1).
+
- SYMLINKS
The filesystem we're on supports symbolic links. E.g. a FAT
run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
-commit_message="Intial commit"
+commit_message="Initial commit"
commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
-commit_size=176
+commit_size=177
commit_content="tree $tree_sha1
author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
test_expect_success setup '
mkdir fi &&
+ printf "a\0a" >binary &&
+ git add binary &&
test_commit initial fi/le first &&
git branch side &&
git branch another &&
+ printf "a\0b" >binary &&
+ git add binary &&
test_commit second fi/le second &&
git checkout side &&
test_commit third fi/le third &&
+ git branch add-add &&
git checkout another &&
test_commit fourth fi/le fourth &&
+ git checkout add-add &&
+ test_commit fifth add-differently &&
git checkout master
'
test_cmp expect actual
'
+test_expect_success 'rerere forget (binary)' '
+ git checkout -f side &&
+ printf "a\0c" >binary &&
+ git commit -a -m binary &&
+ test_must_fail git merge second &&
+ git rerere forget binary
+'
+
+test_expect_success 'rerere forget (add-add conflict)' '
+ git checkout -f master &&
+ echo master >add-differently &&
+ git add add-differently &&
+ git commit -m "add differently" &&
+ test_must_fail git merge fifth &&
+ git rerere forget add-differently 2>actual &&
+ test_i18ngrep "no remembered" actual
+'
+
test_done
git checkout -
'
+test_expect_success '--set-upstream-to fails on a missing dst branch' '
+ test_must_fail git branch --set-upstream-to master does-not-exist
+'
+
+test_expect_success '--set-upstream-to fails on a missing src branch' '
+ test_must_fail git branch --set-upstream-to does-not-exist master
+'
+
+test_expect_success '--set-upstream-to fails on a non-ref' '
+ test_must_fail git branch --set-upstream-to HEAD^{}
+'
+
test_expect_success 'use --set-upstream-to modify HEAD' '
test_config branch.master.remote foo &&
test_config branch.master.merge foo &&
rm -rf submod
'
+test_expect_success 'rm of d/f when d has become a non-directory' '
+ rm -rf d &&
+ mkdir d &&
+ >d/f &&
+ git add d &&
+ rm -rf d &&
+ >d &&
+ git rm d/f &&
+ test_must_fail git rev-parse --verify :d/f &&
+ test_path_is_file d
+'
+
+test_expect_success SYMLINKS 'rm of d/f when d has become a dangling symlink' '
+ rm -rf d &&
+ mkdir d &&
+ >d/f &&
+ git add d &&
+ rm -rf d &&
+ ln -s nonexistent d &&
+ git rm d/f &&
+ test_must_fail git rev-parse --verify :d/f &&
+ test -h d &&
+ test_path_is_missing d
+'
+
+test_expect_success 'rm of file when it has become a directory' '
+ rm -rf d &&
+ >d &&
+ git add d &&
+ rm -f d &&
+ mkdir d &&
+ >d/f &&
+ test_must_fail git rm d &&
+ git rev-parse --verify :d &&
+ test_path_is_file d/f
+'
+
+test_expect_success SYMLINKS 'rm across a symlinked leading path (no index)' '
+ rm -rf d e &&
+ mkdir e &&
+ echo content >e/f &&
+ ln -s e d &&
+ git add -A e d &&
+ git commit -m "symlink d to e, e/f exists" &&
+ test_must_fail git rm d/f &&
+ git rev-parse --verify :d &&
+ git rev-parse --verify :e/f &&
+ test -h d &&
+ test_path_is_file e/f
+'
+
+test_expect_failure SYMLINKS 'rm across a symlinked leading path (w/ index)' '
+ rm -rf d e &&
+ mkdir d &&
+ echo content >d/f &&
+ git add -A e d &&
+ git commit -m "d/f exists" &&
+ mv d e &&
+ ln -s e d &&
+ test_must_fail git rm d/f &&
+ git rev-parse --verify :d/f &&
+ test -h d &&
+ test_path_is_file e/f
+'
+
test_done
# times to get out.
#
# 2. Correct version applies the (not)edited version, and asks
- # about the next hunk, against wich we say q and program
+ # about the next hunk, against which we say q and program
# exits.
for a in s e q n q q
do
test 2 = $(grep "my sig" output | wc -l)
'
-test_expect_success 'format.signature="" supresses signatures' '
+test_expect_success 'format.signature="" suppresses signatures' '
git config format.signature "" &&
git format-patch --stdout -1 >output &&
check_patch output &&
! grep "^-- \$" output
'
-test_expect_success 'format-patch --no-signature supresses signatures' '
+test_expect_success 'format-patch --no-signature suppresses signatures' '
git config --unset-all format.signature &&
git format-patch --stdout --no-signature -1 >output &&
check_patch output &&
! grep "^-- \$" output
'
-test_expect_success 'format-patch --signature="" supresses signatures' '
+test_expect_success 'format-patch --signature="" suppresses signatures' '
git format-patch --stdout --signature="" -1 >output &&
check_patch output &&
! grep "^-- \$" output
compare_diff_patch expected actual
'
+test_expect_success 'combine diff coalesce simple' '
+ >test &&
+ git add test &&
+ git commit -m initial &&
+ test_seq 4 >test &&
+ git commit -a -m empty1 &&
+ git branch side1 &&
+ git checkout HEAD^ &&
+ test_seq 5 >test &&
+ git commit -a -m empty2 &&
+ test_must_fail git merge side1 &&
+ >test &&
+ git commit -a -m merge &&
+ git show >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ --1
+ --2
+ --3
+ --4
+ - 5
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'combine diff coalesce tricky' '
+ >test &&
+ git add test &&
+ git commit -m initial --allow-empty &&
+ cat <<-\EOF >test &&
+ 3
+ 1
+ 2
+ 3
+ 4
+ EOF
+ git commit -a -m empty1 &&
+ git branch -f side1 &&
+ git checkout HEAD^ &&
+ cat <<-\EOF >test &&
+ 1
+ 3
+ 5
+ 4
+ EOF
+ git commit -a -m empty2 &&
+ git branch -f side2 &&
+ test_must_fail git merge side1 &&
+ >test &&
+ git commit -a -m merge &&
+ git show >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ -3
+ --1
+ -2
+ --3
+ - 5
+ --4
+ EOF
+ compare_diff_patch expected actual &&
+ git checkout -f side1 &&
+ test_must_fail git merge side2 &&
+ >test &&
+ git commit -a -m merge &&
+ git show >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ - 3
+ --1
+ - 2
+ --3
+ -5
+ --4
+ EOF
+ compare_diff_patch expected actual
+'
+
+test_expect_failure 'combine diff coalesce three parents' '
+ >test &&
+ git add test &&
+ git commit -m initial --allow-empty &&
+ cat <<-\EOF >test &&
+ 3
+ 1
+ 2
+ 3
+ 4
+ EOF
+ git commit -a -m empty1 &&
+ git checkout -B side1 &&
+ git checkout HEAD^ &&
+ cat <<-\EOF >test &&
+ 1
+ 3
+ 7
+ 5
+ 4
+ EOF
+ git commit -a -m empty2 &&
+ git branch -f side2 &&
+ git checkout HEAD^ &&
+ cat <<-\EOF >test &&
+ 3
+ 1
+ 6
+ 5
+ 4
+ EOF
+ git commit -a -m empty3 &&
+ >test &&
+ git add test &&
+ TREE=$(git write-tree) &&
+ COMMIT=$(git commit-tree -p HEAD -p side1 -p side2 -m merge $TREE) &&
+ git show $COMMIT >actual.tmp &&
+ sed -e "1,/^@@@/d" < actual.tmp >actual &&
+ tr -d Q <<-\EOF >expected &&
+ -- 3
+ ---1
+ - 6
+ - 2
+ --3
+ -7
+ - -5
+ ---4
+ EOF
+ compare_diff_patch expected actual
+'
+
test_done
# find touched lines
$DIFF file target | sed -n -e "s/^> //p" >fixed
- # the changed lines are all expeced to change
+ # the changed lines are all expected to change
fixed_cnt=$(wc -l <fixed)
case "$1" in
'') expect_cnt=$fixed_cnt ;;
test_cmp expect actual
'
+test_expect_success 'log -G --textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ test_must_fail git -c diff.test.textconv=missing log -Gfoo &&
+ rm .gitattributes
+'
+
+test_expect_success 'log -G --no-textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ git -c diff.test.textconv=missing log -Gfoo --no-textconv >actual &&
+ >expect &&
+ test_cmp expect actual &&
+ rm .gitattributes
+'
+
test_expect_success 'log -S (nomatch)' '
git log -Spicked --format=%H >actual &&
>expect &&
test_cmp expect actual
'
+test_expect_success 'log -S --textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ test_must_fail git -c diff.test.textconv=missing log -Sfoo &&
+ rm .gitattributes
+'
+
+test_expect_success 'log -S --no-textconv (missing textconv tool)' '
+ echo "* diff=test" >.gitattributes &&
+ git -c diff.test.textconv=missing log -Sfoo --no-textconv >actual &&
+ >expect &&
+ test_cmp expect actual &&
+ rm .gitattributes
+'
+
test_done
#!/bin/sh
-test_description='fetching and pushing, with or without wildcard'
+test_description='Basic fetch/push functionality.
+
+This test checks the following functionality:
+
+* command-line syntax
+* refspecs
+* fast-forward detection, and overriding it
+* configuration
+* hooks
+* --porcelain output format
+* hiderefs
+'
. ./test-lib.sh
D=`pwd`
mk_empty () {
- rm -fr testrepo &&
- mkdir testrepo &&
+ repo_name="$1"
+ rm -fr "$repo_name" &&
+ mkdir "$repo_name" &&
(
- cd testrepo &&
+ cd "$repo_name" &&
git init &&
git config receive.denyCurrentBranch warn &&
mv .git/hooks .git/hooks-disabled
}
mk_test () {
- mk_empty &&
+ repo_name="$1"
+ shift
+
+ mk_empty "$repo_name" &&
(
for ref in "$@"
do
- git push testrepo $the_first_commit:refs/$ref ||
+ git push "$repo_name" $the_first_commit:refs/$ref ||
exit
done &&
- cd testrepo &&
+ cd "$repo_name" &&
for ref in "$@"
do
echo "$the_first_commit" >expect &&
}
mk_test_with_hooks() {
+ repo_name=$1
mk_test "$@" &&
(
- cd testrepo &&
+ cd "$repo_name" &&
mkdir .git/hooks &&
cd .git/hooks &&
}
mk_child() {
- rm -rf "$1" &&
- git clone testrepo "$1"
+ rm -rf "$2" &&
+ git clone "$1" "$2"
}
check_push_result () {
+ repo_name="$1"
+ shift
+
(
- cd testrepo &&
+ cd "$repo_name" &&
echo "$1" >expect &&
shift &&
for ref in "$@"
'
test_expect_success 'fetch without wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
(
cd testrepo &&
git fetch .. refs/heads/master:refs/remotes/origin/master &&
'
test_expect_success 'fetch with wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
(
cd testrepo &&
git config remote.up.url .. &&
'
test_expect_success 'fetch with insteadOf' '
- mk_empty &&
+ mk_empty testrepo &&
(
TRASH=$(pwd)/ &&
cd testrepo &&
'
test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
- mk_empty &&
+ mk_empty testrepo &&
(
TRASH=$(pwd)/ &&
cd testrepo &&
'
test_expect_success 'push without wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(
'
test_expect_success 'push with wildcard' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo "refs/heads/*:refs/remotes/origin/*" &&
(
'
test_expect_success 'push with insteadOf' '
- mk_empty &&
+ mk_empty testrepo &&
TRASH="$(pwd)/" &&
test_config "url.$TRASH.insteadOf" trash/ &&
git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
'
test_expect_success 'push with pushInsteadOf' '
- mk_empty &&
+ mk_empty testrepo &&
TRASH="$(pwd)/" &&
test_config "url.$TRASH.pushInsteadOf" trash/ &&
git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
'
test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
- mk_empty &&
+ mk_empty testrepo &&
test_config "url.trash2/.pushInsteadOf" testrepo/ &&
test_config "url.trash3/.pusnInsteadOf" trash/wrong &&
test_config remote.r.url trash/wrong &&
test_expect_success 'push with matching heads' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'push with matching heads on the command line' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'failed (non-fast-forward) push with matching heads' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
test_must_fail git push testrepo &&
- check_push_result $the_commit heads/master &&
+ check_push_result testrepo $the_commit heads/master &&
git reset --hard $the_commit
'
test_expect_success 'push --force with matching heads' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
git push --force testrepo &&
- ! check_push_result $the_commit heads/master &&
+ ! check_push_result testrepo $the_commit heads/master &&
git reset --hard $the_commit
'
test_expect_success 'push with matching heads and forced update' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo : &&
git commit --amend -massaged &&
git push testrepo +: &&
- ! check_push_result $the_commit heads/master &&
+ ! check_push_result testrepo $the_commit heads/master &&
git reset --hard $the_commit
'
test_expect_success 'push with no ambiguity (1)' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git push testrepo master:master &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'push with no ambiguity (2)' '
- mk_test remotes/origin/master &&
+ mk_test testrepo remotes/origin/master &&
git push testrepo master:origin/master &&
- check_push_result $the_commit remotes/origin/master
+ check_push_result testrepo $the_commit remotes/origin/master
'
test_expect_success 'push with colon-less refspec, no ambiguity' '
- mk_test heads/master heads/t/master &&
+ mk_test testrepo heads/master heads/t/master &&
git branch -f t/master master &&
git push testrepo master &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit heads/t/master
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit heads/t/master
'
test_expect_success 'push with weak ambiguity (1)' '
- mk_test heads/master remotes/origin/master &&
+ mk_test testrepo heads/master remotes/origin/master &&
git push testrepo master:master &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit remotes/origin/master
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit remotes/origin/master
'
test_expect_success 'push with weak ambiguity (2)' '
- mk_test heads/master remotes/origin/master remotes/another/master &&
+ mk_test testrepo heads/master remotes/origin/master remotes/another/master &&
git push testrepo master:master &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit remotes/origin/master remotes/another/master
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit remotes/origin/master remotes/another/master
'
test_expect_success 'push with ambiguity' '
- mk_test heads/frotz tags/frotz &&
+ mk_test testrepo heads/frotz tags/frotz &&
test_must_fail git push testrepo master:frotz &&
- check_push_result $the_first_commit heads/frotz tags/frotz
+ check_push_result testrepo $the_first_commit heads/frotz tags/frotz
'
test_expect_success 'push with colon-less refspec (1)' '
- mk_test heads/frotz tags/frotz &&
+ mk_test testrepo heads/frotz tags/frotz &&
git branch -f frotz master &&
git push testrepo frotz &&
- check_push_result $the_commit heads/frotz &&
- check_push_result $the_first_commit tags/frotz
+ check_push_result testrepo $the_commit heads/frotz &&
+ check_push_result testrepo $the_first_commit tags/frotz
'
test_expect_success 'push with colon-less refspec (2)' '
- mk_test heads/frotz tags/frotz &&
+ mk_test testrepo heads/frotz tags/frotz &&
if git show-ref --verify -q refs/heads/frotz
then
git branch -D frotz
fi &&
git tag -f frotz &&
git push -f testrepo frotz &&
- check_push_result $the_commit tags/frotz &&
- check_push_result $the_first_commit heads/frotz
+ check_push_result testrepo $the_commit tags/frotz &&
+ check_push_result testrepo $the_first_commit heads/frotz
'
test_expect_success 'push with colon-less refspec (3)' '
- mk_test &&
+ mk_test testrepo &&
if git show-ref --verify -q refs/tags/frotz
then
git tag -d frotz
fi &&
git branch -f frotz master &&
git push testrepo frotz &&
- check_push_result $the_commit heads/frotz &&
+ check_push_result testrepo $the_commit heads/frotz &&
test 1 = $( cd testrepo && git show-ref | wc -l )
'
test_expect_success 'push with colon-less refspec (4)' '
- mk_test &&
+ mk_test testrepo &&
if git show-ref --verify -q refs/heads/frotz
then
git branch -D frotz
fi &&
git tag -f frotz &&
git push testrepo frotz &&
- check_push_result $the_commit tags/frotz &&
+ check_push_result testrepo $the_commit tags/frotz &&
test 1 = $( cd testrepo && git show-ref | wc -l )
'
test_expect_success 'push head with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
git push testrepo master:branch &&
- check_push_result $the_commit heads/branch
+ check_push_result testrepo $the_commit heads/branch
'
test_expect_success 'push tag with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
git tag -f v1.0 &&
git push testrepo v1.0:tag &&
- check_push_result $the_commit tags/tag
+ check_push_result testrepo $the_commit tags/tag
'
test_expect_success 'push sha1 with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
test_must_fail git push testrepo `git rev-parse master`:foo
'
test_expect_success 'push ref expression with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
test_must_fail git push testrepo master^:branch
'
test_expect_success 'push with HEAD' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout master &&
git push testrepo HEAD &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
'
test_expect_success 'push with HEAD nonexisting at remote' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout -b local master &&
git push testrepo HEAD &&
- check_push_result $the_commit heads/local
+ check_push_result testrepo $the_commit heads/local
'
test_expect_success 'push with +HEAD' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout master &&
git branch -D local &&
git checkout -b local &&
git push testrepo master local &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_commit heads/local &&
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_commit heads/local &&
# Without force rewinding should fail
git reset --hard HEAD^ &&
test_must_fail git push testrepo HEAD &&
- check_push_result $the_commit heads/local &&
+ check_push_result testrepo $the_commit heads/local &&
# With force rewinding should succeed
git push testrepo +HEAD &&
- check_push_result $the_first_commit heads/local
+ check_push_result testrepo $the_first_commit heads/local
'
test_expect_success 'push HEAD with non-existent, incomplete dest' '
- mk_test &&
+ mk_test testrepo &&
git checkout master &&
git push testrepo HEAD:branch &&
- check_push_result $the_commit heads/branch
+ check_push_result testrepo $the_commit heads/branch
'
test_expect_success 'push with config remote.*.push = HEAD' '
- mk_test heads/local &&
+ mk_test testrepo heads/local &&
git checkout master &&
git branch -f local $the_commit &&
(
test_config remote.there.push HEAD &&
test_config branch.master.remote there &&
git push &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit heads/local
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit heads/local
+'
+
+test_expect_success 'push with remote.pushdefault' '
+ mk_test up_repo heads/master &&
+ mk_test down_repo heads/master &&
+ test_config remote.up.url up_repo &&
+ test_config remote.down.url down_repo &&
+ test_config branch.master.remote up &&
+ test_config remote.pushdefault down &&
+ git push &&
+ check_push_result up_repo $the_first_commit heads/master &&
+ check_push_result down_repo $the_commit heads/master
'
test_expect_success 'push with config remote.*.pushurl' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git checkout master &&
test_config remote.there.url test2repo &&
test_config remote.there.pushurl testrepo &&
git push there &&
- check_push_result $the_commit heads/master
+ check_push_result testrepo $the_commit heads/master
+'
+
+test_expect_success 'push with config branch.*.pushremote' '
+ mk_test up_repo heads/master &&
+ mk_test side_repo heads/master &&
+ mk_test down_repo heads/master &&
+ test_config remote.up.url up_repo &&
+ test_config remote.pushdefault side_repo &&
+ test_config remote.down.url down_repo &&
+ test_config branch.master.remote up &&
+ test_config branch.master.pushremote down &&
+ git push &&
+ check_push_result up_repo $the_first_commit heads/master &&
+ check_push_result side_repo $the_first_commit heads/master &&
+ check_push_result down_repo $the_commit heads/master
'
test_expect_success 'push with dry-run' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(
cd testrepo &&
old_commit=$(git show-ref -s --verify refs/heads/master)
) &&
git push --dry-run testrepo &&
- check_push_result $old_commit heads/master
+ check_push_result testrepo $old_commit heads/master
'
test_expect_success 'push updates local refs' '
- mk_test heads/master &&
- mk_child child &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child &&
(
cd child &&
git pull .. master &&
test_expect_success 'push updates up-to-date local refs' '
- mk_test heads/master &&
- mk_child child1 &&
- mk_child child2 &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
(cd child1 && git pull .. master && git push) &&
(
cd child2 &&
test_expect_success 'push preserves up-to-date packed refs' '
- mk_test heads/master &&
- mk_child child &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child &&
(
cd child &&
git push &&
test_expect_success 'push does not update local refs on failure' '
- mk_test heads/master &&
- mk_child child &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child &&
mkdir testrepo/.git/hooks &&
echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
chmod +x testrepo/.git/hooks/pre-receive &&
test_expect_success 'allow deleting an invalid remote ref' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
rm -f testrepo/.git/objects/??/* &&
git push testrepo :refs/heads/master &&
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
'
test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' '
- mk_test_with_hooks heads/master heads/next &&
+ mk_test_with_hooks testrepo heads/master heads/next &&
orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
newmaster=$(git show-ref -s --verify refs/heads/master) &&
orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
'
test_expect_success 'deleting dangling ref triggers hooks with correct args' '
- mk_test_with_hooks heads/master &&
+ mk_test_with_hooks testrepo heads/master &&
rm -f testrepo/.git/objects/??/* &&
git push testrepo :refs/heads/master &&
(
'
test_expect_success 'deletion of a non-existent ref is not fed to post-receive and post-update hooks' '
- mk_test_with_hooks heads/master &&
+ mk_test_with_hooks testrepo heads/master &&
orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
newmaster=$(git show-ref -s --verify refs/heads/master) &&
git push testrepo master :refs/heads/nonexistent &&
'
test_expect_success 'deletion of a non-existent ref alone does trigger post-receive and post-update hooks' '
- mk_test_with_hooks heads/master &&
+ mk_test_with_hooks testrepo heads/master &&
git push testrepo :refs/heads/nonexistent &&
(
cd testrepo/.git &&
'
test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks with correct input' '
- mk_test_with_hooks heads/master heads/next heads/pu &&
+ mk_test_with_hooks testrepo heads/master heads/next heads/pu &&
orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
newmaster=$(git show-ref -s --verify refs/heads/master) &&
orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
'
test_expect_success 'allow deleting a ref using --delete' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(cd testrepo && git config receive.denyDeleteCurrent warn) &&
git push testrepo --delete master &&
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
'
test_expect_success 'allow deleting a tag using --delete' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
git tag -a -m dummy_message deltag heads/master &&
git push testrepo --tags &&
(cd testrepo && git rev-parse --verify -q refs/tags/deltag) &&
'
test_expect_success 'push --delete without args aborts' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
test_must_fail git push testrepo --delete
'
test_expect_success 'push --delete refuses src:dest refspecs' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
test_must_fail git push testrepo --delete master:foo
'
test_expect_success 'warn on push to HEAD of non-bare repository' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
'
test_expect_success 'deny push to HEAD of non-bare repository' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
'
test_expect_success 'allow push to HEAD of bare repository (bare)' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
'
test_expect_success 'allow push to HEAD of non-bare repository (config)' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
(
cd testrepo &&
git checkout master &&
'
test_expect_success 'fetch with branches' '
- mk_empty &&
+ mk_empty testrepo &&
git branch second $the_first_commit &&
git checkout second &&
echo ".." > testrepo/.git/branches/branch1 &&
'
test_expect_success 'fetch with branches containing #' '
- mk_empty &&
+ mk_empty testrepo &&
echo "..#second" > testrepo/.git/branches/branch2 &&
(
cd testrepo &&
'
test_expect_success 'push with branches' '
- mk_empty &&
+ mk_empty testrepo &&
git checkout second &&
echo "testrepo" > .git/branches/branch1 &&
git push branch1 &&
'
test_expect_success 'push with branches containing #' '
- mk_empty &&
+ mk_empty testrepo &&
echo "testrepo#branch3" > .git/branches/branch2 &&
git push branch2 &&
(
'
test_expect_success 'push into aliased refs (consistent)' '
- mk_test heads/master &&
- mk_child child1 &&
- mk_child child2 &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
(
cd child1 &&
git branch foo &&
'
test_expect_success 'push into aliased refs (inconsistent)' '
- mk_test heads/master &&
- mk_child child1 &&
- mk_child child2 &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
(
cd child1 &&
git branch foo &&
'
test_expect_success 'push requires --force to update lightweight tag' '
- mk_test heads/master &&
- mk_child child1 &&
- mk_child child2 &&
+ mk_test testrepo heads/master &&
+ mk_child testrepo child1 &&
+ mk_child testrepo child2 &&
(
cd child1 &&
git tag Tag &&
'
test_expect_success 'push --porcelain' '
- mk_empty &&
+ mk_empty testrepo &&
echo >.git/foo "To testrepo" &&
echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new branch]" &&
echo >>.git/foo "Done" &&
'
test_expect_success 'push --porcelain bad url' '
- mk_empty &&
+ mk_empty testrepo &&
test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
test_must_fail grep -q Done .git/bar
'
test_expect_success 'push --porcelain rejected' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(cd testrepo &&
git reset --hard origin/master^
'
test_expect_success 'push --porcelain --dry-run rejected' '
- mk_empty &&
+ mk_empty testrepo &&
git push testrepo refs/heads/master:refs/remotes/origin/master &&
(cd testrepo &&
git reset --hard origin/master
'
test_expect_success 'push --prune' '
- mk_test heads/master heads/second heads/foo heads/bar &&
+ mk_test testrepo heads/master heads/second heads/foo heads/bar &&
git push --prune testrepo &&
- check_push_result $the_commit heads/master &&
- check_push_result $the_first_commit heads/second &&
- ! check_push_result $the_first_commit heads/foo heads/bar
+ check_push_result testrepo $the_commit heads/master &&
+ check_push_result testrepo $the_first_commit heads/second &&
+ ! check_push_result testrepo $the_first_commit heads/foo heads/bar
'
test_expect_success 'push --prune refspec' '
- mk_test tmp/master tmp/second tmp/foo tmp/bar &&
+ mk_test testrepo tmp/master tmp/second tmp/foo tmp/bar &&
git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
- check_push_result $the_commit tmp/master &&
- check_push_result $the_first_commit tmp/second &&
- ! check_push_result $the_first_commit tmp/foo tmp/bar
+ check_push_result testrepo $the_commit tmp/master &&
+ check_push_result testrepo $the_first_commit tmp/second &&
+ ! check_push_result testrepo $the_first_commit tmp/foo tmp/bar
'
for configsection in transfer receive
do
test_expect_success "push to update a ref hidden by $configsection.hiderefs" '
- mk_test heads/master hidden/one hidden/two hidden/three &&
+ mk_test testrepo heads/master hidden/one hidden/two hidden/three &&
(
cd testrepo &&
git config $configsection.hiderefs refs/hidden
# push to unhidden ref succeeds normally
git push testrepo master:refs/heads/master &&
- check_push_result $the_commit heads/master &&
+ check_push_result testrepo $the_commit heads/master &&
# push to update a hidden ref should fail
test_must_fail git push testrepo master:refs/hidden/one &&
- check_push_result $the_first_commit hidden/one &&
+ check_push_result testrepo $the_first_commit hidden/one &&
# push to delete a hidden ref should fail
test_must_fail git push testrepo :refs/hidden/two &&
- check_push_result $the_first_commit hidden/two &&
+ check_push_result testrepo $the_first_commit hidden/two &&
# idempotent push to update a hidden ref should fail
test_must_fail git push testrepo $the_first_commit:refs/hidden/three &&
- check_push_result $the_first_commit hidden/three
+ check_push_result testrepo $the_first_commit hidden/three
'
done
test_expect_success 'fetch exact SHA1' '
- mk_test heads/master hidden/one &&
+ mk_test testrepo heads/master hidden/one &&
git push testrepo master:refs/hidden/one &&
(
cd testrepo &&
git config transfer.hiderefs refs/hidden
) &&
- check_push_result $the_commit hidden/one &&
+ check_push_result testrepo $the_commit hidden/one &&
- mk_child child &&
+ mk_child testrepo child &&
(
cd child &&
'
test_expect_success 'fetch follows tags by default' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
rm -fr src dst &&
git init src &&
(
'
test_expect_success 'push does not follow tags by default' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
rm -fr src dst &&
git init src &&
git init --bare dst &&
'
test_expect_success 'push --follow-tag only pushes relevant tags' '
- mk_test heads/master &&
+ mk_test testrepo heads/master &&
rm -fr src dst &&
git init src &&
git init --bare dst &&
grep "^-[0-9a-f]\\{40\\} " boundary
'
+test_expect_success 'prerequisites with an empty commit message' '
+ : >file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit --allow-empty-message -m "" &&
+ test_commit file2 &&
+ git bundle create bundle HEAD^.. &&
+ git bundle verify bundle
+'
+
test_done
FMT='tformat:%P %H | %s'
-check_result () {
+check_outcome () {
+ outcome=$1
+ shift
for c in $1
do
echo "$c"
done >expect &&
shift &&
param="$*" &&
- test_expect_success "log $param" '
+ test_expect_$outcome "log $param" '
git log --pretty="$FMT" --parents $param |
unnote >actual &&
sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
'
}
+check_result () {
+ check_outcome success "$@"
+}
+
check_result 'L K J I H G F E D C B A' --full-history
check_result 'K I H E C B A' --full-history -- file
check_result 'K I H E C B A' --full-history --topo-order -- file
check_result 'K I H E C B A' --full-history --date-order -- file
-check_result 'I E C B A' --simplify-merges -- file
+check_outcome failure 'I E C B A' --simplify-merges -- file
check_result 'I B A' -- file
check_result 'I B A' --topo-order -- file
check_result 'H' --first-parent -- another-file
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
# but $HASH2 is bad,
# so we should find $HASH2 as the first bad commit
-test_expect_success 'bisect skip: successfull result' '
+test_expect_success 'bisect skip: successful result' '
git bisect reset &&
git bisect start $HASH4 $HASH1 &&
git bisect skip &&
Common #1
EOF
- git config merge.log true &&
- test_might_fail git config --unset-all merge.summary &&
+ test_config merge.log true &&
+ test_unconfig merge.summary &&
git checkout master &&
test_tick &&
git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary true &&
+ test_unconfig merge.log &&
+ test_config merge.summary true &&
git checkout master &&
test_tick &&
test_cmp expected actual2
'
-test_expect_success 'setup: clear [merge] configuration' '
- test_might_fail git config --unset-all merge.log &&
- test_might_fail git config --unset-all merge.summary
-'
-
test_expect_success 'setup FETCH_HEAD' '
git checkout master &&
test_tick &&
test_cmp expected actual
'
+test_expect_success '--log=5 with custom comment character' '
+ cat >expected <<-EOF &&
+ Merge branch ${apos}left${apos}
+
+ / By Another Author (3) and A U Thor (2)
+ / Via Another Committer
+ * left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+ EOF
+
+ git -c core.commentchar="/" fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'merge.log=0 disables shortlog' '
echo "Merge branch ${apos}left${apos}" >expected
git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
Common #1
EOF
- test_might_fail git config --unset merge.log &&
- test_might_fail git config --unset merge.summary &&
+ test_unconfig merge.log &&
+ test_unconfig merge.summary &&
git checkout master &&
git fetch "$(pwd)" left &&
git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual &&
git fmt-merge-msg --log -m "Sync with left" \
<.git/FETCH_HEAD >actual.log &&
- git config merge.log true &&
+ test_config merge.log true &&
git fmt-merge-msg -m "Sync with left" \
<.git/FETCH_HEAD >actual.log-config &&
git fmt-merge-msg --no-log -m "Sync with left" \
'
test_expect_success 'shortlog for two branches' '
- git config merge.log true &&
- test_might_fail git config --unset-all merge.summary &&
+ test_config merge.log true &&
+ test_unconfig merge.summary &&
git checkout master &&
test_tick &&
git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary true &&
+ test_unconfig merge.log &&
+ test_config merge.summary true &&
git checkout master &&
test_tick &&
git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
- git config merge.log yes &&
- test_might_fail git config --unset-all merge.summary &&
+ test_config merge.log yes &&
+ test_unconfig merge.summary &&
git checkout master &&
test_tick &&
git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual3 &&
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
git fetch . left right &&
'
test_expect_success 'merge-msg -F' '
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
git fetch . left right &&
'
test_expect_success 'merge-msg -F in subdirectory' '
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
git fetch . left right &&
'
test_expect_success 'merge-msg with nothing to merge' '
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
>empty &&
Common #1
EOF
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
Common #1
EOF
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
Common #1
EOF
- test_might_fail git config --unset-all merge.log &&
- git config merge.summary yes &&
+ test_unconfig merge.log &&
+ test_config merge.summary yes &&
git checkout master &&
test_tick &&
echo " ..."
} >expected &&
+ test_config merge.summary yes &&
+
git checkout master &&
test_tick &&
git fetch . long &&
test_cmp expected actual
'
+test_expect_success 'merge-msg with "merging" an annotated tag' '
+ test_config merge.log true &&
+
+ git checkout master^0 &&
+ git commit --allow-empty -m "One step ahead" &&
+ git tag -a -m "An annotated one" annote HEAD &&
+
+ git checkout master &&
+ git fetch . annote &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ {
+ cat <<-\EOF
+ Merge tag '\''annote'\''
+
+ An annotated one
+
+ * tag '\''annote'\'':
+ One step ahead
+ EOF
+ } >expected &&
+ test_cmp expected actual &&
+
+ test_when_finished "git reset --hard" &&
+ annote=$(git rev-parse annote) &&
+ git merge --no-commit $annote &&
+ {
+ cat <<-EOF
+ Merge tag '\''$annote'\''
+
+ An annotated one
+
+ * tag '\''$annote'\'':
+ One step ahead
+ EOF
+ } >expected &&
+ test_cmp expected .git/MERGE_MSG
+'
+
test_done
grep drepo "$TRASHDIR/backup-refs"
'
+test_expect_success 'tree-filter works with -d' '
+ git init drepo-tree &&
+ (
+ cd drepo-tree &&
+ test_commit one &&
+ git filter-branch -d "$TRASHDIR/dfoo" \
+ --tree-filter "echo changed >one.t" &&
+ echo changed >expect &&
+ git cat-file blob HEAD:one.t >actual &&
+ test_cmp expect actual &&
+ test_cmp one.t actual
+ )
+'
+
test_expect_success 'Fail if commit filter fails' '
test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD
'
git config submodule.example.foo bar &&
git config submodule.example2.frotz nitfol &&
test_must_fail git submodule deinit &&
- git submodule deinit . &&
+ git submodule deinit . >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
+ test_i18ngrep "Cleared directory .example2" actual &&
rmdir init example2
'
test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' '
git submodule update --init &&
rm -rf init example2/* example2/.git &&
- git submodule deinit init example2 &&
+ git submodule deinit init example2 >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+ test_i18ngrep ! "Cleared directory .init" actual &&
+ test_i18ngrep "Cleared directory .example2" actual &&
rmdir init
'
test_must_fail git submodule deinit init &&
test -n "$(git config --get-regexp "submodule\.example\.")" &&
test -f example2/.git &&
- git submodule deinit -f init &&
+ git submodule deinit -f init >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
rmdir init
'
test_must_fail git submodule deinit init &&
test -n "$(git config --get-regexp "submodule\.example\.")" &&
test -f example2/.git &&
- git submodule deinit -f init &&
+ git submodule deinit -f init >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
rmdir init
'
test_must_fail git submodule deinit init &&
test -n "$(git config --get-regexp "submodule\.example\.")" &&
test -f example2/.git &&
- git submodule deinit -f init &&
+ git submodule deinit -f init >actual &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test_i18ngrep "Cleared directory .init" actual &&
rmdir init
'
git submodule update --init &&
git submodule deinit init >actual &&
test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
git submodule deinit init >actual &&
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
git submodule deinit . >actual &&
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
git submodule deinit . >actual &&
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+ test_i18ngrep "Cleared directory .init" actual &&
rmdir init example2
'
test_i18ncmp expected actual
'
+test_expect_success 'status while reverting commit (conflicts)' '
+ git checkout master &&
+ echo before >to-revert.txt &&
+ test_commit before to-revert.txt &&
+ echo old >to-revert.txt &&
+ test_commit old to-revert.txt &&
+ echo new >to-revert.txt &&
+ test_commit new to-revert.txt &&
+ TO_REVERT=$(git rev-parse --short HEAD^) &&
+ test_must_fail git revert $TO_REVERT &&
+ cat >expected <<-EOF
+ # On branch master
+ # You are currently reverting commit $TO_REVERT.
+ # (fix conflicts and run "git revert --continue")
+ # (use "git revert --abort" to cancel the revert operation)
+ #
+ # Unmerged paths:
+ # (use "git reset HEAD <file>..." to unstage)
+ # (use "git add <file>..." to mark resolution)
+ #
+ # both modified: to-revert.txt
+ #
+ no changes added to commit (use "git add" and/or "git commit -a")
+ EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status while reverting commit (conflicts resolved)' '
+ echo reverted >to-revert.txt &&
+ git add to-revert.txt &&
+ cat >expected <<-EOF
+ # On branch master
+ # You are currently reverting commit $TO_REVERT.
+ # (all conflicts fixed: run "git revert --continue")
+ # (use "git revert --abort" to cancel the revert operation)
+ #
+ # Changes to be committed:
+ # (use "git reset HEAD <file>..." to unstage)
+ #
+ # modified: to-revert.txt
+ #
+ # Untracked files not listed (use -u option to show untracked files)
+ EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'status after reverting commit' '
+ git revert --continue &&
+ cat >expected <<-\EOF
+ # On branch master
+ nothing to commit (use -u to show untracked files)
+ EOF
+ git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual
+'
+
test_done
'
# First do the merge with resolve and recursive then verify that
-# recusive is choosen.
+# recusive is chosen.
test_expect_success 'merge picks up the best result' '
git config --unset-all pull.twohead &&
git submodule update -N &&
test_must_fail git merge master &&
- #shouldnt need these lines
+ #should not need these lines
#( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
#( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
#( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
--- /dev/null
+#!/bin/sh
+
+test_description='merge signature verification tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create signed commits' '
+ echo 1 >file && git add file &&
+ test_tick && git commit -m initial &&
+ git tag initial &&
+
+ git checkout -b side-signed &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -S -m "signed on side" &&
+ git checkout initial &&
+
+ git checkout -b side-unsigned &&
+ echo 3 >foo && git add foo &&
+ test_tick && git commit -m "unsigned on side" &&
+ git checkout initial &&
+
+ git checkout -b side-bad &&
+ echo 3 >bar && git add bar &&
+ test_tick && git commit -S -m "bad on side" &&
+ git cat-file commit side-bad >raw &&
+ sed -e "s/bad/forged bad/" raw >forged &&
+ git hash-object -w -t commit forged >forged.commit &&
+ git checkout initial &&
+
+ git checkout -b side-untrusted &&
+ echo 3 >baz && git add baz &&
+ test_tick && git commit -SB7227189 -m "untrusted on side"
+
+ git checkout master
+'
+
+test_expect_success GPG 'merge unsigned commit with verification' '
+ test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror &&
+ test_i18ngrep "does not have a GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge commit with bad signature with verification' '
+ test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror &&
+ test_i18ngrep "has a bad GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge commit with untrusted signature with verification' '
+ test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
+ test_i18ngrep "has an untrusted GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge signed commit with verification' '
+ git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
+ test_i18ngrep "has a good GPG signature" mergeoutput
+'
+
+test_expect_success GPG 'merge commit with bad signature without verification' '
+ git merge $(cat forged.commit)
+'
+
+test_done
test_expect_success $PREREQ 'Send patches with --envelope-sender' '
clean_fake_sendmail &&
- git send-email --envelope-sender="Patch Contributer <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+ git send-email --envelope-sender="Patch Contributor <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
'
test_expect_success $PREREQ 'setup expect' '
test $ret = "0"
'
-test_expect_success $PREREQ 'confirm doesnt loop forever' '
+test_expect_success $PREREQ 'confirm does not loop forever' '
CONFIRM=$(git config --get sendemail.confirm) &&
git config sendemail.confirm auto &&
GIT_SEND_EMAIL_NOTTY=1 &&
>empty
-test_expect_success 'setup: have pipes?' '
- rm -f frob &&
- if mkfifo frob
- then
- test_set_prereq PIPE
- fi
-'
-
test_expect_success PIPE 'empty dump' '
reinit_git &&
echo "SVN-fs-dump-format-version: 2" >input &&
>empty
-test_expect_success 'setup: have pipes?' '
- rm -f frob &&
- if mkfifo frob
- then
- test_set_prereq PIPE
- fi
-'
-
###
### series A
###
# Failure cases for config:
# Save and restore STDERR; we will probably extract this into a
# "dies_ok" method and possibly move the STDERR handling to Git.pm.
-open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR;
+open our $tmpstderr, ">&STDERR" or die "cannot save STDERR";
+open STDERR, ">", "/dev/null" or die "cannot redirect STDERR to /dev/null";
is($r->config("test.dupstring"), "value2", "config: multivar");
eval { $r->config_bool("test.boolother") };
ok($@, "config_bool: non-boolean values fail");
fi
}
+test_lazy_prereq PIPE '
+ # test whether the filesystem supports FIFOs
+ rm -f testfifo && mkfifo testfifo
+'
+
test_lazy_prereq SYMLINKS '
# test whether the filesystem supports symbolic links
ln -s x y && test -h y
int src_is_sock;
/* Is destination socket? */
int dest_is_sock;
- /* Transfer state (TRANSFERING/FLUSHING/FINISHED) */
+ /* Transfer state (TRANSFERRING/FLUSHING/FINISHED) */
int state;
/* Buffer. */
char buf[BUFFERSIZE];
const char *executable, int fd[2]);
/** get_refs_list(), fetch(), and push_refs() can keep
- * resources (such as a connection) reserved for futher
+ * resources (such as a connection) reserved for further
* use. disconnect() releases these resources.
**/
int (*disconnect)(struct transport *connection);
wt_status_print_trailer(s);
}
+static void show_revert_in_progress(struct wt_status *s,
+ struct wt_status_state *state,
+ const char *color)
+{
+ status_printf_ln(s, color, _("You are currently reverting commit %s."),
+ find_unique_abbrev(state->revert_head_sha1, DEFAULT_ABBREV));
+ if (advice_status_hints) {
+ if (has_unmerged(s))
+ status_printf_ln(s, color,
+ _(" (fix conflicts and run \"git revert --continue\")"));
+ else
+ status_printf_ln(s, color,
+ _(" (all conflicts fixed: run \"git revert --continue\")"));
+ status_printf_ln(s, color,
+ _(" (use \"git revert --abort\" to cancel the revert operation)"));
+ }
+ wt_status_print_trailer(s);
+}
+
static void show_bisect_in_progress(struct wt_status *s,
struct wt_status_state *state,
const char *color)
int get_detached_from)
{
struct stat st;
+ unsigned char sha1[20];
if (!stat(git_path("MERGE_HEAD"), &st)) {
state->merge_in_progress = 1;
state->bisect_in_progress = 1;
state->branch = read_and_strip_branch("BISECT_START");
}
+ if (!stat(git_path("REVERT_HEAD"), &st) &&
+ !get_sha1("REVERT_HEAD", sha1)) {
+ state->revert_in_progress = 1;
+ hashcpy(state->revert_head_sha1, sha1);
+ }
if (get_detached_from)
wt_status_get_detached_from(state);
show_rebase_in_progress(s, state, state_color);
else if (state->cherry_pick_in_progress)
show_cherry_pick_in_progress(s, state, state_color);
+ else if (state->revert_in_progress)
+ show_revert_in_progress(s, state, state_color);
if (state->bisect_in_progress)
show_bisect_in_progress(s, state, state_color);
}
if (advice_status_u_option && 2000 < s->untracked_in_ms) {
status_printf_ln(s, GIT_COLOR_NORMAL, "");
status_printf_ln(s, GIT_COLOR_NORMAL,
- _("It took %.2f seconds to enumerate untracked files."
- " 'status -uno'"),
- s->untracked_in_ms / 1000.0);
- status_printf_ln(s, GIT_COLOR_NORMAL,
- _("may speed it up, but you have to be careful not"
- " to forget to add"));
- status_printf_ln(s, GIT_COLOR_NORMAL,
- _("new files yourself (see 'git help status')."));
+ _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
+ "may speed it up, but you have to be careful not to forget to add\n"
+ "new files yourself (see 'git help status')."),
+ s->untracked_in_ms / 1000.0);
}
} else if (s->commitable)
status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
int rebase_interactive_in_progress;
int cherry_pick_in_progress;
int bisect_in_progress;
+ int revert_in_progress;
char *branch;
char *onto;
char *detached_from;
unsigned char detached_sha1[20];
+ unsigned char revert_head_sha1[20];
};
void wt_status_prepare(struct wt_status *s);
/*
* Try to move back the possibly merged group of changes, to match
- * the recorded postion in the other file.
+ * the recorded position in the other file.
*/
while (ixref < ix) {
rchg[--ixs] = 1;
struct record {
unsigned int ptr, cnt;
struct record *next;
- } **records, /* an ocurrence */
+ } **records, /* an occurrence */
**line_map; /* map of line to record chain */
chastore_t rcha;
unsigned int *next_ptrs;