Merge commit 'mingw/master' into devel

Conflicts:

	builtin-tag.c
This commit is contained in:
Steffen Prohaska
2008-03-01 10:06:38 +01:00
80 changed files with 1493 additions and 393 deletions

View File

@@ -489,6 +489,13 @@ color.status.<slot>::
commit.template::
Specify a file to use as the template for new commit messages.
color.ui::
When set to `always`, always use colors in all git commands which
are capable of colored output. When false (or `never`), never. When
set to `true` or `auto`, use colors only when the output is to the
terminal. When more specific variables of color.* are set, they always
take precedence over this setting. Defaults to false.
diff.autorefreshindex::
When using `git diff` to compare with work tree
files, do not consider stat-only change as changed.

View File

@@ -74,8 +74,8 @@ OPTIONS
Update only files that git already knows about. This is similar
to what "git commit -a" does in preparation for making a commit,
except that the update is limited to paths specified on the
command line. If no paths are specified, all tracked files are
updated.
command line. If no paths are specified, all tracked files in the
current directory and its subdirectories are updated.
\--refresh::
Don't add the file(s), but only refresh their stat()

View File

@@ -75,9 +75,11 @@ OPTIONS
-n::
Prefix the line number to matching lines.
-l | --files-with-matches | -L | --files-without-match::
-l | --files-with-matches | --name-only | -L | --files-without-match::
Instead of showing every matched line, show only the
names of files that contain (or do not contain) matches.
For better compatability with git-diff, --name-only is a
synonym for --files-with-matches.
-c | --count::
Instead of showing every matched line, show the number of

View File

@@ -8,7 +8,7 @@ git-merge-index - Run a merge for files needing merging
SYNOPSIS
--------
'git-merge-index' [-o] [-q] <merge-program> (-a | \-- | <file>\*)
'git-merge-index' [-o] [-q] <merge-program> (-a | [--] <file>\*)
DESCRIPTION
-----------

View File

@@ -15,6 +15,7 @@ DESCRIPTION
-----------
Runs `git-fetch` with the given parameters, and calls `git-merge`
to merge the retrieved head(s) into the current branch.
With `--rebase`, calls `git-rebase` instead of `git-merge`.
Note that you can use `.` (current directory) as the
<repository> to pull from the local repository -- this is useful
@@ -26,19 +27,14 @@ OPTIONS
include::merge-options.txt[]
:git-pull: 1
include::fetch-options.txt[]
include::pull-fetch-param.txt[]
include::urls-remotes.txt[]
include::merge-strategies.txt[]
\--rebase::
Instead of a merge, perform a rebase after fetching. If
there is a remote ref for the upstream branch, and this branch
was rebased since last fetched, the rebase uses that information
to avoid rebasing non-local changes.
to avoid rebasing non-local changes. To make this the default
for branch `<name>`, set configuration `branch.<name>.rebase`
to `true`.
+
*NOTE:* This is a potentially _dangerous_ mode of operation.
It rewrites history, which does not bode well when you
@@ -48,6 +44,14 @@ unless you have read linkgit:git-rebase[1] carefully.
\--no-rebase::
Override earlier \--rebase.
include::fetch-options.txt[]
include::pull-fetch-param.txt[]
include::urls-remotes.txt[]
include::merge-strategies.txt[]
DEFAULT BEHAVIOUR
-----------------

View File

@@ -47,9 +47,9 @@ even if it does not result in a fast forward update.
+
Note: If no explicit refspec is found, (that is neither
on the command line nor in any Push line of the
corresponding remotes file---see below), then all the
heads that exist both on the local side and on the remote
side are updated.
corresponding remotes file---see below), then "matching" heads are
pushed: for every head that exists on the local side, the remote side is
updated if a head of the same name already exists on the remote side.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+
@@ -108,6 +108,55 @@ the remote repository.
include::urls-remotes.txt[]
OUTPUT
------
The output of "git push" depends on the transport method used; this
section describes the output when pushing over the git protocol (either
locally or via ssh).
The status of the push is output in tabular form, with each line
representing the status of a single ref. Each line is of the form:
-------------------------------
<flag> <summary> <from> -> <to> (<reason>)
-------------------------------
flag::
A single character indicating the status of the ref. This is
blank for a successfully pushed ref, `!` for a ref that was
rejected or failed to push, and '=' for a ref that was up to
date and did not need pushing (note that the status of up to
date refs is shown only when `git push` is running verbosely).
summary::
For a successfully pushed ref, the summary shows the old and new
values of the ref in a form suitable for using as an argument to
`git log` (this is `<old>..<new>` in most cases, and
`<old>...<new>` for forced non-fast forward updates). For a
failed update, more details are given for the failure.
The string `rejected` indicates that git did not try to send the
ref at all (typically because it is not a fast forward). The
string `remote rejected` indicates that the remote end refused
the update; this rejection is typically caused by a hook on the
remote side. The string `remote failure` indicates that the
remote end did not report the successful update of the ref
(perhaps because of a temporary error on the remote side, a
break in the network connection, or other transient error).
from::
The name of the local ref being pushed, minus its
`refs/<type>/` prefix. In the case of deletion, the
name of the local ref is omitted.
to::
The name of the remote ref being updated, minus its
`refs/<type>/` prefix.
reason::
A human-readable explanation. In the case of successfully pushed
refs, no explanation is needed. For a failed ref, the reason for
failure is described.
Examples
--------

View File

@@ -43,7 +43,7 @@ save [<message>]::
subcommand is given. The <message> part is optional and gives
the description along with the stashed state.
list::
list [<options>]::
List the stashes that you currently have. Each 'stash' is listed
with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is
@@ -55,6 +55,9 @@ list::
stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
stash@{1}: On master: 9cc0589... Add git-stash
----------------------------------------------------------------
+
The command takes options applicable to the linkgit:git-log[1]
command to control what is shown and how.
show [<stash>]::

View File

@@ -0,0 +1,123 @@
Remotes configuration API
=========================
The API in remote.h gives access to the configuration related to
remotes. It handles all three configuration mechanisms historically
and currently used by git, and presents the information in a uniform
fashion. Note that the code also handles plain URLs without any
configuration, giving them just the default information.
struct remote
-------------
`name`::
The user's nickname for the remote
`url`::
An array of all of the url_nr URLs configured for the remote
`push`::
An array of refspecs configured for pushing, with
push_refspec being the literal strings, and push_refspec_nr
being the quantity.
`fetch`::
An array of refspecs configured for fetching, with
fetch_refspec being the literal strings, and fetch_refspec_nr
being the quantity.
`fetch_tags`::
The setting for whether to fetch tags (as a separate rule from
the configured refspecs); -1 means never to fetch tags, 0
means to auto-follow tags based on the default heuristic, 1
means to always auto-follow tags, and 2 means to fetch all
tags.
`receivepack`, `uploadpack`::
The configured helper programs to run on the remote side, for
git-native protocols.
`http_proxy`::
The proxy to use for curl (http, https, ftp, etc.) URLs.
struct remotes can be found by name with remote_get(), and iterated
through with for_each_remote(). remote_get(NULL) will return the
default remote, given the current branch and configuration.
struct refspec
--------------
A struct refspec holds the parsed interpretation of a refspec. If it
will force updates (starts with a '+'), force is true. If it is a
pattern (sides end with '*') pattern is true. src and dest are the two
sides (if a pattern, only the part outside of the wildcards); if there
is only one side, it is src, and dst is NULL; if sides exist but are
empty (i.e., the refspec either starts or ends with ':'), the
corresponding side is "".
This parsing can be done to an array of strings to give an array of
struct refpsecs with parse_ref_spec().
remote_find_tracking(), given a remote and a struct refspec with
either src or dst filled out, will fill out the other such that the
result is in the "fetch" specification for the remote (note that this
evaluates patterns and returns a single result).
struct branch
-------------
Note that this may end up moving to branch.h
struct branch holds the configuration for a branch. It can be looked
up with branch_get(name) for "refs/heads/{name}", or with
branch_get(NULL) for HEAD.
It contains:
`name`::
The short name of the branch.
`refname`::
The full path for the branch ref.
`remote_name`::
The name of the remote listed in the configuration.
`remote`::
The struct remote for that remote.
`merge_name`::
An array of the "merge" lines in the configuration.
`merge`::
An array of the struct refspecs used for the merge lines. That
is, merge[i]->dst is a local tracking ref which should be
merged into this branch by default.
`merge_nr`::
The number of merge configurations
branch_has_merge_config() returns true if the given branch has merge
configuration given.
Other stuff
-----------
There is other stuff in remote.h that is related, in general, to the
process of interacting with remotes.
(Daniel Barkalow)

View File

@@ -1,10 +1,171 @@
run-command API
===============
Talk about <run-command.h>, and things like:
The run-command API offers a versatile tool to run sub-processes with
redirected input and output as well as with a modified environment
and an alternate current directory.
* Environment the command runs with (e.g. GIT_DIR);
* File descriptors and pipes;
* Exit status;
A similar API offers the capability to run a function asynchronously,
which is primarily used to capture the output that the function
produces in the caller in order to process it.
(Hannes, Dscho, Shawn)
Functions
---------
`start_command`::
Start a sub-process. Takes a pointer to a `struct child_process`
that specifies the details and returns pipe FDs (if requested).
See below for details.
`finish_command`::
Wait for the completion of a sub-process that was started with
start_command().
`run_command`::
A convenience function that encapsulates a sequence of
start_command() followed by finish_command(). Takes a pointer
to a `struct child_process` that specifies the details.
`run_command_v_opt`, `run_command_v_opt_dir`, `run_command_v_opt_cd_env`::
Convenience functions that encapsulate a sequence of
start_command() followed by finish_command(). The argument argv
specifies the program and its arguments. The argument opt is zero
or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or
`RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members
.no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`.
The argument dir corresponds the member .dir. The argument env
corresponds to the member .env.
`start_async`::
Run a function asynchronously. Takes a pointer to a `struct
async` that specifies the details and returns a pipe FD
from which the caller reads. See below for details.
`finish_async`::
Wait for the completeion of an asynchronous function that was
started with start_async().
Data structures
---------------
* `struct child_process`
This describes the arguments, redirections, and environment of a
command to run in a sub-process.
The caller:
1. allocates and clears (memset(&chld, '0', sizeof(chld));) a
struct child_process variable;
2. initializes the members;
3. calls start_command();
4. processes the data;
5. closes file descriptors (if necessary; see below);
6. calls finish_command().
The .argv member is set up as an array of string pointers (NULL
terminated), of which .argv[0] is the program name to run (usually
without a path). If the command to run is a git command, set argv[0] to
the command name without the 'git-' prefix and set .git_cmd = 1.
The members .in, .out, .err are used to redirect stdin, stdout,
stderr as follows:
. Specify 0 to request no special redirection. No new file descriptor
is allocated. The child process simply inherits the channel from the
parent.
. Specify -1 to have a pipe allocated; start_command() replaces -1
by the pipe FD in the following way:
.in: Returns the writable pipe end into which the caller writes;
the readable end of the pipe becomes the child's stdin.
.out, .err: Returns the readable pipe end from which the caller
reads; the writable end of the pipe end becomes child's
stdout/stderr.
The caller of start_command() must close the so returned FDs
after it has completed reading from/writing to it!
. Specify a file descriptor > 0 to be used by the child:
.in: The FD must be readable; it becomes child's stdin.
.out: The FD must be writable; it becomes child's stdout.
.err > 0 is not supported.
The specified FD is closed by start_command(), even if it fails to
run the sub-process!
. Special forms of redirection are available by setting these members
to 1:
.no_stdin, .no_stdout, .no_stderr: The respective channel is
redirected to /dev/null.
.stdout_to_stderr: stdout of the child is redirected to the
parent's stderr (i.e. *not* to what .err or
.no_stderr specify).
To modify the environment of the sub-process, specify an array of
string pointers (NULL terminated) in .env:
. If the string is of the form "VAR=value", i.e. it contains '='
the variable is added to the child process's environment.
. If the string does not contain '=', it names an environement
variable that will be removed from the child process's envionment.
To specify a new initial working directory for the sub-process,
specify it in the .dir member.
* `struct async`
This describes a function to run asynchronously, whose purpose is
to produce output that the caller reads.
The caller:
1. allocates and clears (memset(&asy, '0', sizeof(asy));) a
struct async variable;
2. initializes .proc and .data;
3. calls start_async();
4. processes the data by reading from the fd in .out;
5. closes .out;
6. calls finish_async().
The function pointer in .proc has the following signature:
int proc(int fd, void *data);
. fd specifies a writable file descriptor to which the function must
write the data that it produces. The function *must* close this
descriptor before it returns.
. data is the value that the caller has specified in the .data member
of struct async.
. The return value of the function is 0 on success and non-zero
on failure. If the function indicates failure, finish_async() will
report failure as well.
There are serious restrictions on what the asynchronous function can do
because this facility is implemented by a pipe to a forked process on
UNIX, but by a thread in the same address space on Windows:
. It cannot change the program's state (global variables, environment,
etc.) in a way that the caller notices; in other words, .out is the
only communication channel to the caller.
. It must not change the program's state that the caller of the
facility also uses.

View File

@@ -3,6 +3,9 @@ all::
# Define V=1 to have a more verbose compile.
#
# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
# when attempting to read from an fopen'ed directory.
#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
# This also implies MOZILLA_SHA1.
#
@@ -660,6 +663,10 @@ endif
ifdef NO_C99_FORMAT
BASIC_CFLAGS += -DNO_C99_FORMAT
endif
ifdef FREAD_READS_DIRECTORIES
COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
COMPAT_OBJS += compat/fopen.o
endif
ifdef NO_SYMLINK_HEAD
BASIC_CFLAGS += -DNO_SYMLINK_HEAD
endif

View File

@@ -228,6 +228,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
goto finish;
}
if (*argv) {
/* Was there an invalid path? */
if (pathspec) {
int num;
for (num = 0; pathspec[num]; num++)
; /* just counting */
if (argc != num)
exit(1); /* error message already given */
} else
exit(1); /* error message already given */
}
fill_directory(&dir, pathspec, ignored_too);
if (show_only) {

View File

@@ -1894,9 +1894,7 @@ static unsigned parse_score(const char *arg)
static const char *add_prefix(const char *prefix, const char *path)
{
if (!prefix || !prefix[0])
return path;
return prefix_path(prefix, strlen(prefix), path);
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
}
/*
@@ -2369,7 +2367,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
* bottom commits we would reach while traversing as
* uninteresting.
*/
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
if (is_null_sha1(sb.final->object.sha1)) {
char *buf;

View File

@@ -31,7 +31,7 @@ static unsigned char head_sha1[20];
static int branch_track = 1;
static int branch_use_color;
static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */
"", /* PLAIN (normal) */
@@ -79,12 +79,12 @@ static int git_branch_config(const char *var, const char *value)
branch_track = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value);
return git_color_default_config(var, value);
}
static const char *branch_get_color(enum color_branch ix)
{
if (branch_use_color)
if (branch_use_color > 0)
return branch_colors[ix];
return "";
}
@@ -588,6 +588,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
};
git_config(git_branch_config);
if (branch_use_color == -1)
branch_use_color = git_use_color_default;
track = branch_track;
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
if (!!delete + !!rename + !!force_create > 1)

View File

@@ -29,7 +29,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i;
int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
int ignored_only = 0, baselen = 0, config_set = 0;
int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
struct strbuf directory;
struct dir_struct dir;
const char *path, *base;
@@ -137,12 +137,15 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (show_only && (remove_directories || matches)) {
printf("Would remove %s\n",
directory.buf + prefix_offset);
} else if (quiet && (remove_directories || matches)) {
remove_dir_recursively(&directory, 0);
} else if (remove_directories || matches) {
printf("Removing %s\n",
directory.buf + prefix_offset);
remove_dir_recursively(&directory, 0);
if (!quiet)
printf("Removing %s\n",
directory.buf + prefix_offset);
if (remove_dir_recursively(&directory, 0) != 0) {
warning("failed to remove '%s'",
directory.buf + prefix_offset);
errors++;
}
} else if (show_only) {
printf("Would not remove %s\n",
directory.buf + prefix_offset);
@@ -162,11 +165,14 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
printf("Removing %s\n",
ent->name + prefix_offset);
}
unlink(ent->name);
if (unlink(ent->name) != 0) {
warning("failed to remove '%s'", ent->name);
errors++;
}
}
}
free(seen);
strbuf_release(&directory);
return 0;
return (errors != 0);
}

View File

@@ -7,6 +7,7 @@
#include "cache.h"
#include "cache-tree.h"
#include "color.h"
#include "dir.h"
#include "builtin.h"
#include "diff.h"
@@ -771,6 +772,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
git_config(git_status_config);
if (wt_status_use_color == -1)
wt_status_use_color = git_use_color_default;
argc = parse_and_validate_options(argc, argv, builtin_status_usage);
index_file = prepare_index(argc, argv, prefix);

View File

@@ -4,6 +4,7 @@
* Copyright (c) 2006 Junio C Hamano
*/
#include "cache.h"
#include "color.h"
#include "commit.h"
#include "blob.h"
#include "tag.h"
@@ -229,6 +230,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
prefix = setup_git_directory_gently(&nongit);
git_config(git_diff_ui_config);
if (diff_use_color_default == -1)
diff_use_color_default = git_use_color_default;
init_revisions(&rev, prefix);
rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;

View File

@@ -383,7 +383,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
get_tags_and_duplicates(&revs.pending, &extra_refs);
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
revs.diffopt.format_callback = show_filemodify;
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
while ((commit = get_revision(&revs))) {

View File

@@ -187,7 +187,8 @@ static void shortlog(const char *name, unsigned char *sha1,
add_pending_object(rev, branch, name);
add_pending_object(rev, &head->object, "^HEAD");
head->object.flags |= UNINTERESTING;
prepare_revision_walk(rev);
if (prepare_revision_walk(rev))
die("revision walk setup failed");
while ((commit = get_revision(rev)) != NULL) {
char *oneline, *bol, *eol;

View File

@@ -578,6 +578,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp("-l", arg) ||
!strcmp("--name-only", arg) ||
!strcmp("--files-with-matches", arg)) {
opt.name_only = 1;
continue;

View File

@@ -5,6 +5,7 @@
* 2006 Junio Hamano
*/
#include "cache.h"
#include "color.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
@@ -197,7 +198,8 @@ static int cmd_log_walk(struct rev_info *rev)
if (rev->early_output)
setup_early_output(rev);
prepare_revision_walk(rev);
if (prepare_revision_walk(rev))
die("revision walk setup failed");
if (rev->early_output)
finish_early_output(rev);
@@ -235,6 +237,10 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
struct rev_info rev;
git_config(git_log_config);
if (diff_use_color_default == -1)
diff_use_color_default = git_use_color_default;
init_revisions(&rev, prefix);
rev.diff = 1;
rev.simplify_history = 0;
@@ -307,6 +313,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
int i, count, ret = 0;
git_config(git_log_config);
if (diff_use_color_default == -1)
diff_use_color_default = git_use_color_default;
init_revisions(&rev, prefix);
rev.diff = 1;
rev.combine_merges = 1;
@@ -367,6 +377,10 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
struct rev_info rev;
git_config(git_log_config);
if (diff_use_color_default == -1)
diff_use_color_default = git_use_color_default;
init_revisions(&rev, prefix);
init_reflog_walk(&rev.reflog_info);
rev.abbrev_commit = 1;
@@ -395,6 +409,10 @@ int cmd_log(int argc, const char **argv, const char *prefix)
struct rev_info rev;
git_config(git_log_config);
if (diff_use_color_default == -1)
diff_use_color_default = git_use_color_default;
init_revisions(&rev, prefix);
rev.always_show_header = 1;
cmd_log_init(argc, argv, prefix, &rev);
@@ -556,7 +574,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
o2->flags ^= UNINTERESTING;
add_pending_object(&check_rev, o1, "o1");
add_pending_object(&check_rev, o2, "o2");
prepare_revision_walk(&check_rev);
if (prepare_revision_walk(&check_rev))
die("revision walk setup failed");
while ((commit = get_revision(&check_rev)) != NULL) {
/* ignore merges */
@@ -781,7 +800,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!use_stdout)
realstdout = xfdopen(xdup(1), "w");
prepare_revision_walk(&rev);
if (prepare_revision_walk(&rev))
die("revision walk setup failed");
while ((commit = get_revision(&rev)) != NULL) {
/* ignore merges */
if (commit->parents && commit->parents->next)
@@ -923,7 +943,8 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
die("Unknown commit %s", limit);
/* reverse the list of commits */
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) {
/* ignore merges */
if (commit->parents && commit->parents->next)

View File

@@ -574,8 +574,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
pathspec = get_pathspec(prefix, argv + i);
/* Verify that the pathspec matches the prefix */
if (pathspec)
if (pathspec) {
if (argc != i) {
int cnt;
for (cnt = 0; pathspec[cnt]; cnt++)
;
if (cnt != (argc - i))
exit(1); /* error message already given */
}
prefix = verify_pathspec(prefix);
} else if (argc != i)
exit(1); /* error message already given */
/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {

View File

@@ -19,6 +19,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
int count, int base_name)
{
int i;
int len = prefix ? strlen(prefix) : 0;
const char **result = xmalloc((count + 1) * sizeof(const char *));
memcpy(result, pathspec, count * sizeof(const char *));
result[count] = NULL;
@@ -32,8 +33,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
if (last_slash)
result[i] = last_slash + 1;
}
result[i] = prefix_path(prefix, len, result[i]);
if (!result[i])
exit(1); /* error already given */
}
return get_pathspec(prefix, result);
return result;
}
static void show_list(const char *label, struct path_list *list)
@@ -164,7 +168,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}
dst = add_slash(dst);
dst_len = strlen(dst) - 1;
dst_len = strlen(dst);
for (j = 0; j < last - first; j++) {
const char *path =
@@ -172,7 +176,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
source[argc + j] = path;
destination[argc + j] =
prefix_path(dst, dst_len,
path + length);
path + length + 1);
modes[argc + j] = INDEX;
}
argc += last - first;

View File

@@ -2033,7 +2033,8 @@ static void get_object_list(int ac, const char **av)
die("bad revision '%s'", line);
}
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(revs.commits, &revs, show_edge);
traverse_commit_list(&revs, show_commit, show_object);

View File

@@ -90,7 +90,7 @@ static int do_push(const char *repo, int flags)
if (!err)
continue;
error("failed to push to '%s'", remote->url[i]);
error("failed to push some refs to '%s'", remote->url[i]);
errs++;
}
return !!errs;

View File

@@ -45,7 +45,7 @@ static int read_cache_unmerged(void)
continue;
cache_tree_invalidate_path(active_cache_tree, ce->name);
last = ce;
ce->ce_flags |= CE_REMOVE;
continue;
}
*dst++ = ce;
}

View File

@@ -60,6 +60,8 @@ static void show_commit(struct commit *commit)
fputs(header_prefix, stdout);
if (commit->object.flags & BOUNDARY)
putchar('-');
else if (commit->object.flags & UNINTERESTING)
putchar('^');
else if (revs.left_right) {
if (commit->object.flags & SYMMETRIC_LEFT)
putchar('<');
@@ -84,7 +86,7 @@ static void show_commit(struct commit *commit)
else
putchar('\n');
if (revs.verbose_header) {
if (revs.verbose_header && commit->buffer) {
struct strbuf buf;
strbuf_init(&buf, 0);
pretty_print_commit(revs.commit_format, commit,
@@ -609,7 +611,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (bisect_list)
revs.limited = 1;
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
if (revs.tree_objects)
mark_edges_uninteresting(revs.commits, &revs, show_edge);

View File

@@ -136,7 +136,8 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list)
{
struct commit *commit;
prepare_revision_walk(rev);
if (prepare_revision_walk(rev))
die("revision walk setup failed");
while ((commit = get_revision(rev)) != NULL) {
const char *author = NULL, *buffer;

View File

@@ -86,6 +86,9 @@ match:
sha1_to_hex(sha1));
if (obj->type == OBJ_TAG) {
obj = deref_tag(obj, refname, 0);
if (!obj)
die("git-show-ref: bad tag at ref %s (%s)", refname,
sha1_to_hex(sha1));
hex = find_unique_abbrev(obj->sha1, abbrev);
printf("%s %s^{}\n", hex, refname);
}

View File

@@ -236,9 +236,6 @@ static int do_sign(struct strbuf *buffer)
if (finish_command(&gpg) || !len || len < 0)
return error("gpg failed to sign the tag");
if (len < 0)
return error("could not read the entire signature from gpg.");
#ifdef __MINGW32__
/* strip CR from the line endings */
{

View File

@@ -128,7 +128,8 @@ int verify_bundle(struct bundle_header *header, int verbose)
add_object_array(e->item, e->name, &refs);
}
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
i = req_nr;
while (i && (commit = get_revision(&revs)))

12
color.c
View File

@@ -3,6 +3,8 @@
#define COLOR_RESET "\033[m"
int git_use_color_default = 0;
static int parse_color(const char *name, int len)
{
static const char * const color_names[] = {
@@ -143,6 +145,16 @@ int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
return 0;
}
int git_color_default_config(const char *var, const char *value)
{
if (!strcmp(var, "color.ui")) {
git_use_color_default = git_config_colorbool(var, value, -1);
return 0;
}
return git_default_config(var, value);
}
static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
va_list args, const char *trail)
{

11
color.h
View File

@@ -4,6 +4,17 @@
/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
#define COLOR_MAXLEN 24
/*
* This variable stores the value of color.ui
*/
extern int git_use_color_default;
/*
* Use this instead of git_default_config if you need the value of color.ui.
*/
int git_color_default_config(const char *var, const char *value);
int git_config_colorbool(const char *var, const char *value, int stdout_is_tty);
void color_parse(const char *var, const char *value, char *dst);
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);

View File

@@ -311,6 +311,8 @@ int parse_commit(struct commit *item)
unsigned long size;
int ret;
if (!item)
return -1;
if (item->object.parsed)
return 0;
buffer = read_sha1_file(item->object.sha1, &type, &size);
@@ -385,8 +387,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
while (parents) {
struct commit *commit = parents->item;
parse_commit(commit);
if (!(commit->object.flags & mark)) {
if (!parse_commit(commit) && !(commit->object.flags & mark)) {
commit->object.flags |= mark;
insert_by_date(commit, list);
}
@@ -552,8 +553,10 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
*/
return commit_list_insert(one, &result);
parse_commit(one);
parse_commit(two);
if (parse_commit(one))
return NULL;
if (parse_commit(two))
return NULL;
one->object.flags |= PARENT1;
two->object.flags |= PARENT2;
@@ -586,7 +589,8 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
parents = parents->next;
if ((p->object.flags & flags) == flags)
continue;
parse_commit(p);
if (parse_commit(p))
return NULL;
p->object.flags |= flags;
insert_by_date(p, &list);
}

26
compat/fopen.c Normal file
View File

@@ -0,0 +1,26 @@
#include "../git-compat-util.h"
#undef fopen
FILE *git_fopen(const char *path, const char *mode)
{
FILE *fp;
struct stat st;
if (mode[0] == 'w' || mode[0] == 'a')
return fopen(path, mode);
if (!(fp = fopen(path, mode)))
return NULL;
if (fstat(fileno(fp), &st)) {
fclose(fp);
return NULL;
}
if (S_ISDIR(st.st_mode)) {
fclose(fp);
errno = EISDIR;
return NULL;
}
return fp;
}

View File

@@ -315,12 +315,14 @@ repeat:
struct tm *gmtime_r(const time_t *timep, struct tm *result)
{
/* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
memcpy(result, gmtime(timep), sizeof(struct tm));
return result;
}
struct tm *localtime_r(const time_t *timep, struct tm *result)
{
/* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
memcpy(result, localtime(timep), sizeof(struct tm));
return result;
}
@@ -778,7 +780,7 @@ char **env_setenv(char **env, const char *name)
}
/* this is the first function to call into WS_32; initialize it */
#undef gethostbyname
#undef gethostbyname
struct hostent *mingw_gethostbyname(const char *host)
{
WSADATA wsa;
@@ -911,7 +913,7 @@ static sig_handler_t timer_fn = SIG_DFL;
* wait state every now and then, namely exactly after timer's interval
* length. At these opportunities it calls the signal handler.
*/
static __stdcall unsigned ticktack(void *dummy)
{
while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {

View File

@@ -280,11 +280,18 @@ int git_parse_ulong(const char *value, unsigned long *ret)
return 0;
}
static void die_bad_config(const char *name)
{
if (config_file_name)
die("bad config value for '%s' in %s", name, config_file_name);
die("bad config value for '%s'", name);
}
int git_config_int(const char *name, const char *value)
{
long ret;
if (!git_parse_long(value, &ret))
die("bad config value for '%s' in %s", name, config_file_name);
die_bad_config(name);
return ret;
}
@@ -292,7 +299,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
die("bad config value for '%s' in %s", name, config_file_name);
die_bad_config(name);
return ret;
}

View File

@@ -185,9 +185,8 @@ if there is already one that displays the same directory."
(defun git-call-process-env (buffer env &rest args)
"Wrapper for call-process that sets environment strings."
(if env
(apply #'call-process "env" nil buffer nil
(append (git-get-env-strings env) (list "git") args))
(let ((process-environment (append (git-get-env-strings env)
process-environment)))
(apply #'call-process "git" nil buffer nil args)))
(defun git-call-process-display-error (&rest args)
@@ -204,7 +203,7 @@ if there is already one that displays the same directory."
(defun git-call-process-env-string (env &rest args)
"Wrapper for call-process that sets environment strings,
and returns the process output as a string."
and returns the process output as a string, or nil if the git failed."
(with-temp-buffer
(and (eq 0 (apply #' git-call-process-env t env args))
(buffer-string))))

8
diff.c
View File

@@ -20,7 +20,7 @@
static int diff_detect_rename_default;
static int diff_rename_limit_default = 100;
static int diff_use_color_default;
int diff_use_color_default = -1;
static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1;
@@ -191,7 +191,7 @@ int git_diff_basic_config(const char *var, const char *value)
}
}
return git_default_config(var, value);
return git_color_default_config(var, value);
}
static char *quote_two(const char *one, const char *two)
@@ -1199,7 +1199,7 @@ static struct builtin_funcname_pattern {
"new\\|return\\|switch\\|throw\\|while\\)\n"
"^[ ]*\\(\\([ ]*"
"[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
"[ ]*([^;]*$\\)" },
"[ ]*([^;]*\\)$" },
{ "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
};
@@ -2055,7 +2055,7 @@ void diff_setup(struct diff_options *options)
options->change = diff_change;
options->add_remove = diff_addremove;
if (diff_use_color_default)
if (diff_use_color_default > 0)
DIFF_OPT_SET(options, COLOR_DIFF);
else
DIFF_OPT_CLR(options, COLOR_DIFF);

1
diff.h
View File

@@ -174,6 +174,7 @@ extern void diff_unmerge(struct diff_options *,
extern int git_diff_basic_config(const char *var, const char *value);
extern int git_diff_ui_config(const char *var, const char *value);
extern int diff_use_color_default;
extern void diff_setup(struct diff_options *);
extern int diff_opt_parse(struct diff_options *, const char **, int);
extern int diff_setup_done(struct diff_options *);

View File

@@ -82,6 +82,19 @@ sub list_untracked {
my $status_fmt = '%12s %12s %s';
my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
{
my $initial;
sub is_initial_commit {
$initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
unless defined $initial;
return $initial;
}
}
sub get_empty_tree {
return '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
}
# Returns list of hashes, contents of each of which are:
# VALUE: pathname
# BINARY: is a binary path
@@ -103,8 +116,10 @@ sub list_modified {
return if (!@tracked);
}
my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
for (run_cmd_pipe(qw(git diff-index --cached
--numstat --summary HEAD --), @tracked)) {
--numstat --summary), $reference,
'--', @tracked)) {
if (($add, $del, $file) =
/^([-\d]+) ([-\d]+) (.*)/) {
my ($change, $bin);
@@ -476,21 +491,27 @@ sub revert_cmd {
HEADER => $status_head, },
list_modified());
if (@update) {
my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
map { $_->{VALUE} } @update);
my $fh;
open $fh, '| git update-index --index-info'
or die;
for (@lines) {
print $fh $_;
if (is_initial_commit()) {
system(qw(git rm --cached),
map { $_->{VALUE} } @update);
}
close($fh);
for (@update) {
if ($_->{INDEX_ADDDEL} &&
$_->{INDEX_ADDDEL} eq 'create') {
system(qw(git update-index --force-remove --),
$_->{VALUE});
print "note: $_->{VALUE} is untracked now.\n";
else {
my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
map { $_->{VALUE} } @update);
my $fh;
open $fh, '| git update-index --index-info'
or die;
for (@lines) {
print $fh $_;
}
close($fh);
for (@update) {
if ($_->{INDEX_ADDDEL} &&
$_->{INDEX_ADDDEL} eq 'create') {
system(qw(git update-index --force-remove --),
$_->{VALUE});
print "note: $_->{VALUE} is untracked now.\n";
}
}
}
refresh();
@@ -956,7 +977,9 @@ sub diff_cmd {
HEADER => $status_head, },
@mods);
return if (!@them);
system(qw(git diff -p --cached HEAD --), map { $_->{VALUE} } @them);
my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
system(qw(git diff -p --cached), $reference, '--',
map { $_->{VALUE} } @them);
}
sub quit_cmd {

View File

@@ -331,9 +331,9 @@ bisect_visualize() {
if test $# = 0
then
case "${DISPLAY+set}" in
case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
'') set git log ;;
set) set gitk ;;
set*) set gitk ;;
esac
else
case "$1" in

View File

@@ -461,11 +461,12 @@ else
cd "$D" || exit
fi
if test -z "$bare" && test -f "$GIT_DIR/REMOTE_HEAD"
if test -z "$bare"
then
# a non-bare repository is always in separate-remote layout
remote_top="refs/remotes/$origin"
head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"`
head_sha1=
test ! -r "$GIT_DIR/REMOTE_HEAD" || head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"`
case "$head_sha1" in
'ref: refs/'*)
# Uh-oh, the remote told us (http transport done against
@@ -522,9 +523,16 @@ then
git config branch."$head_points_at".merge "refs/heads/$head_points_at"
;;
'')
# Source had detached HEAD pointing nowhere
git update-ref --no-deref HEAD "$head_sha1" &&
rm -f "refs/remotes/$origin/HEAD"
if test -z "$head_sha1"
then
# Source had nonexistent ref in HEAD
echo >&2 "Warning: Remote HEAD refers to nonexistent ref, unable to checkout."
no_checkout=t
else
# Source had detached HEAD pointing nowhere
git update-ref --no-deref HEAD "$head_sha1" &&
rm -f "refs/remotes/$origin/HEAD"
fi
;;
esac

View File

@@ -214,6 +214,11 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen);
#endif
#ifdef FREAD_READS_DIRECTORIES
#define fopen(a,b) git_fopen(a,b)
extern FILE *git_fopen(const char*, const char*);
#endif
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 1)
#define HAVE_STRCHRNUL

View File

@@ -197,15 +197,39 @@ if (@canstatusfiles) {
my @updated = xargs_safe_pipe_capture([@cvs, 'update'], @canstatusfiles);
print @updated;
}
my @cvsoutput;
@cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles);
my $matchcount = 0;
foreach my $l (@cvsoutput) {
chomp $l;
if ( $l =~ /^File:/ and $l =~ /Status: (.*)$/ ) {
$cvsstat{$canstatusfiles[$matchcount]} = $1;
$matchcount++;
# "cvs status" reorders the parameters, notably when there are multiple
# arguments with the same basename. So be precise here.
my %added = map { $_ => 1 } @afiles;
my %todo = map { $_ => 1 } @canstatusfiles;
while (%todo) {
my @canstatusfiles2 = ();
my %fullname = ();
foreach my $name (keys %todo) {
my $basename = basename($name);
$basename = "no file " . $basename if (exists($added{$basename}));
chomp($basename);
if (!exists($fullname{$basename})) {
$fullname{$basename} = $name;
push (@canstatusfiles2, $name);
delete($todo{$name});
}
}
my @cvsoutput;
@cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles2);
foreach my $l (@cvsoutput) {
chomp $l;
if ($l =~ /^File:\s+(.*\S)\s+Status: (.*)$/) {
if (!exists($fullname{$1})) {
print STDERR "Huh? Status reported for unexpected file '$1'\n";
} else {
$cvsstat{$fullname{$1}} = $2;
}
}
}
}
}

View File

@@ -170,7 +170,9 @@ my $envelope_sender;
my $repo = Git->repository();
my $term = eval {
new Term::ReadLine 'git-send-email';
$ENV{"GIT_SEND_EMAIL_NOTTY"}
? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT
: new Term::ReadLine 'git-send-email';
};
if ($@) {
$term = new FakeTerm "$@: going non-interactive";
@@ -475,9 +477,10 @@ if ($thread && !defined $initial_reply_to && $prompting) {
$initial_reply_to = $_;
}
if (defined $initial_reply_to && $_ ne "") {
$initial_reply_to =~ s/^\s*<?/</;
$initial_reply_to =~ s/>?\s*$/>/;
if (defined $initial_reply_to) {
$initial_reply_to =~ s/^\s*<?//;
$initial_reply_to =~ s/>?\s*$//;
$initial_reply_to = "<$initial_reply_to>" if $initial_reply_to ne '';
}
if (!defined $smtp_server) {

View File

@@ -127,20 +127,14 @@ get_author_ident_from_commit () {
# if we require to be in a git repository.
if test -z "$NONGIT_OK"
then
GIT_DIR=$(git rev-parse --git-dir) || exit
if [ -z "$SUBDIRECTORY_OK" ]
then
: ${GIT_DIR=.git}
test -z "$(git rev-parse --show-cdup)" || {
exit=$?
echo >&2 "You need to run this command from the toplevel of the working tree."
exit $exit
}
else
GIT_DIR=$(git rev-parse --git-dir) || {
exit=$?
echo >&2 "Failed to find a valid git directory."
exit $exit
}
fi
test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || {
echo >&2 "Unable to determine absolute path of git directory"

View File

@@ -3,7 +3,7 @@
Name: git
Version: @@VERSION@@
Release: 1%{?dist}
Summary: Git core and tools
Summary: Core git tools
License: GPL
Group: Development/Tools
URL: http://kernel.org/pub/software/scm/git/
@@ -11,80 +11,86 @@ Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, gettext %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: git-core = %{version}-%{release}
Requires: git-svn = %{version}-%{release}
Requires: git-cvs = %{version}-%{release}
Requires: git-arch = %{version}-%{release}
Requires: git-email = %{version}-%{release}
Requires: gitk = %{version}-%{release}
Requires: git-gui = %{version}-%{release}
Requires: perl-Git = %{version}-%{release}
Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat
Provides: git-core = %{version}-%{release}
Obsoletes: git-core <= 1.5.4.2
Obsoletes: git-p4
%description
Git is a fast, scalable, distributed revision control system with an
unusually rich command set that provides both high-level operations
and full access to internals.
This is a dummy package which brings in all subpackages.
The git rpm installs the core tools with minimal dependencies. To
install all git packages, including tools for integrating with other
SCMs, install the git-all meta-package.
%package core
Summary: Core git tools
%package all
Summary: Meta-package to pull in all git tools
Group: Development/Tools
Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat
Obsoletes: git-p4
%description core
Requires: git = %{version}-%{release}
Requires: git-svn = %{version}-%{release}
Requires: git-cvs = %{version}-%{release}
Requires: git-arch = %{version}-%{release}
Requires: git-email = %{version}-%{release}
Requires: gitk = %{version}-%{release}
Requires: git-gui = %{version}-%{release}
Obsoletes: git <= 1.5.4.2
%description all
Git is a fast, scalable, distributed revision control system with an
unusually rich command set that provides both high-level operations
and full access to internals.
These are the core tools with minimal dependencies.
This is a dummy package which brings in all subpackages.
%package svn
Summary: Git tools for importing Subversion repositories
Group: Development/Tools
Requires: git-core = %{version}-%{release}, subversion
Requires: git = %{version}-%{release}, subversion
%description svn
Git tools for importing Subversion repositories.
%package cvs
Summary: Git tools for importing CVS repositories
Group: Development/Tools
Requires: git-core = %{version}-%{release}, cvs, cvsps
Requires: git = %{version}-%{release}, cvs, cvsps
%description cvs
Git tools for importing CVS repositories.
%package arch
Summary: Git tools for importing Arch repositories
Group: Development/Tools
Requires: git-core = %{version}-%{release}, tla
Requires: git = %{version}-%{release}, tla
%description arch
Git tools for importing Arch repositories.
%package email
Summary: Git tools for sending email
Group: Development/Tools
Requires: git-core = %{version}-%{release}
Requires: git = %{version}-%{release}
%description email
Git tools for sending email.
%package gui
Summary: Git GUI tool
Group: Development/Tools
Requires: git-core = %{version}-%{release}, tk >= 8.4
Requires: git = %{version}-%{release}, tk >= 8.4
%description gui
Git GUI tool
%package -n gitk
Summary: Git revision tree visualiser ('gitk')
Group: Development/Tools
Requires: git-core = %{version}-%{release}, tk >= 8.4
Requires: git = %{version}-%{release}, tk >= 8.4
%description -n gitk
Git revision tree visualiser ('gitk')
%package -n perl-Git
Summary: Perl interface to Git
Group: Development/Libraries
Requires: git-core = %{version}-%{release}
Requires: git = %{version}-%{release}
Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
BuildRequires: perl(Error)
@@ -121,8 +127,12 @@ rm -rf $RPM_BUILD_ROOT%{_mandir}
%clean
rm -rf $RPM_BUILD_ROOT
%files
# These are no files in the root package
%files -f bin-man-doc-files
%defattr(-,root,root)
%{_datadir}/git-core/
%doc README COPYING Documentation/*.txt
%{!?_without_docs: %doc Documentation/*.html Documentation/howto}
%{!?_without_docs: %doc Documentation/technical}
%files svn
%defattr(-,root,root)
@@ -173,14 +183,13 @@ rm -rf $RPM_BUILD_ROOT
%files -n perl-Git -f perl-files
%defattr(-,root,root)
%files core -f bin-man-doc-files
%defattr(-,root,root)
%{_datadir}/git-core/
%doc README COPYING Documentation/*.txt
%{!?_without_docs: %doc Documentation/*.html Documentation/howto}
%{!?_without_docs: %doc Documentation/technical}
%files all
# No files for you!
%changelog
* Fri Feb 15 2008 Kristian Høgsberg <krh@redhat.com>
- Rename git-core to just git and rename meta package from git to git-all.
* Sun Feb 03 2008 James Bowes <jbowes@dangerouslyinc.com>
- Add a BuildRequires for gettext

View File

@@ -233,6 +233,10 @@ You can use the following files in repository:
Displayed in the project summary page. You can use multiple-valued
gitweb.url repository configuration variable for that, but the file
takes precendence.
* gitweb.owner
You can use the gitweb.owner repository configuration variable to set
repository's owner. It is displayed in the project list and summary
page. If it's not set, filesystem directory's owner is used.
* various gitweb.* config variables (in config)
Read description of %feature hash for detailed list, and some
descriptions.

View File

@@ -611,6 +611,8 @@ sub href(%) {
);
my %mapping = @mapping;
$params{'project'} = $project unless exists $params{'project'};
if ($params{-replay}) {
while (my ($name, $symbol) = each %mapping) {
if (!exists $params{$name}) {
@@ -620,8 +622,6 @@ sub href(%) {
}
}
$params{'project'} = $project unless exists $params{'project'};
my ($use_pathinfo) = gitweb_check_feature('pathinfo');
if ($use_pathinfo) {
# use PATH_INFO for project name
@@ -753,29 +753,40 @@ sub esc_path {
# Make control characters "printable", using character escape codes (CEC)
sub quot_cec {
my $cntrl = shift;
my %opts = @_;
my %es = ( # character escape codes, aka escape sequences
"\t" => '\t', # tab (HT)
"\n" => '\n', # line feed (LF)
"\r" => '\r', # carrige return (CR)
"\f" => '\f', # form feed (FF)
"\b" => '\b', # backspace (BS)
"\a" => '\a', # alarm (bell) (BEL)
"\e" => '\e', # escape (ESC)
"\013" => '\v', # vertical tab (VT)
"\000" => '\0', # nul character (NUL)
);
"\t" => '\t', # tab (HT)
"\n" => '\n', # line feed (LF)
"\r" => '\r', # carrige return (CR)
"\f" => '\f', # form feed (FF)
"\b" => '\b', # backspace (BS)
"\a" => '\a', # alarm (bell) (BEL)
"\e" => '\e', # escape (ESC)
"\013" => '\v', # vertical tab (VT)
"\000" => '\0', # nul character (NUL)
);
my $chr = ( (exists $es{$cntrl})
? $es{$cntrl}
: sprintf('\%03o', ord($cntrl)) );
return "<span class=\"cntrl\">$chr</span>";
if ($opts{-nohtml}) {
return $chr;
} else {
return "<span class=\"cntrl\">$chr</span>";
}
}
# Alternatively use unicode control pictures codepoints,
# Unicode "printable representation" (PR)
sub quot_upr {
my $cntrl = shift;
my %opts = @_;
my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl));
return "<span class=\"cntrl\">$chr</span>";
if ($opts{-nohtml}) {
return $chr;
} else {
return "<span class=\"cntrl\">$chr</span>";
}
}
# git may return quoted and escaped filenames
@@ -800,7 +811,7 @@ sub unquote {
return chr(oct($seq));
} elsif (exists $es{$seq}) {
# C escape sequence, aka character escape code
return $es{$seq}
return $es{$seq};
}
# quoted ordinary character
return $seq;
@@ -866,8 +877,8 @@ sub chop_and_escape_str {
if ($chopped eq $str) {
return esc_html($chopped);
} else {
return qq{<span title="} . esc_html($str) . qq{">} .
esc_html($chopped) . qq{</span>};
$str =~ s/([[:cntrl:]])/?/g;
return $cgi->span({-title=>$str}, esc_html($chopped));
}
}
@@ -1759,6 +1770,7 @@ sub git_get_project_owner {
my $owner;
return undef unless $project;
$git_dir = "$projectroot/$project";
if (!defined $gitweb_project_owner) {
git_get_project_list_from_file();
@@ -1767,8 +1779,11 @@ sub git_get_project_owner {
if (exists $gitweb_project_owner->{$project}) {
$owner = $gitweb_project_owner->{$project};
}
if (!defined $owner){
$owner = git_get_project_config('owner');
}
if (!defined $owner) {
$owner = get_file_owner("$projectroot/$project");
$owner = get_file_owner("$git_dir");
}
return $owner;
@@ -3769,18 +3784,24 @@ sub git_search_grep_body {
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
"<td><i>" . $author . "</i></td>\n" .
"<td>" .
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"},
chop_and_escape_str($co{'title'}, 50) . "<br/>");
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
-class => "list subject"},
chop_and_escape_str($co{'title'}, 50) . "<br/>");
my $comment = $co{'comment'};
foreach my $line (@$comment) {
if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
my $lead = esc_html($1) || "";
$lead = chop_str($lead, 30, 10);
my $match = esc_html($2) || "";
my $trail = esc_html($3) || "";
$trail = chop_str($trail, 30, 10);
my $text = "$lead<span class=\"match\">$match</span>$trail";
print chop_str($text, 80, 5) . "<br/>\n";
my ($lead, $match, $trail) = ($1, $2, $3);
$match = chop_str($match, 70, 5); # in case match is very long
my $contextlen = (80 - len($match))/2; # is left for the remainder
$contextlen = 30 if ($contextlen > 30); # but not too much
$lead = chop_str($lead, $contextlen, 10);
$trail = chop_str($trail, $contextlen, 10);
$lead = esc_html($lead);
$match = esc_html($match);
$trail = esc_html($trail);
print "$lead<span class=\"match\">$match</span>$trail<br />";
}
}
print "</td>\n" .

2
hash.c
View File

@@ -70,7 +70,7 @@ void *lookup_hash(unsigned int hash, struct hash_table *table)
{
if (!table->array)
return NULL;
return &lookup_hash_entry(hash, table)->ptr;
return lookup_hash_entry(hash, table)->ptr;
}
void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table)

8
help.c
View File

@@ -44,12 +44,8 @@ static void parse_help_format(const char *format)
static int git_help_config(const char *var, const char *value)
{
if (!strcmp(var, "help.format")) {
if (!value)
return config_error_nonbool(var);
help_default_format = xstrdup(value);
return 0;
}
if (!strcmp(var, "help.format"))
return git_config_string(&help_default_format, var, value);
return git_default_config(var, value);
}

View File

@@ -1634,12 +1634,19 @@ static struct object_list **process_tree(struct tree *tree,
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
while (tree_entry(&desc, &entry))
switch (object_type(entry.mode)) {
case OBJ_TREE:
p = process_tree(lookup_tree(entry.sha1), p, &me, name);
else
break;
case OBJ_BLOB:
p = process_blob(lookup_blob(entry.sha1), p, &me, name);
}
break;
default:
/* Subproject commit - not in this repository */
break;
}
free(tree->buffer);
tree->buffer = NULL;
return p;
@@ -2383,7 +2390,8 @@ int main(int argc, char **argv)
/* Generate a list of objects that need to be pushed */
pushing = 0;
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(revs.commits);
objects_to_send = get_delta(&revs, ref_lock);
finish_all_active_slots();
@@ -2398,15 +2406,17 @@ int main(int argc, char **argv)
fill_active_slots();
add_fill_function(NULL, fill_active_slot);
#endif
finish_all_active_slots();
do {
finish_all_active_slots();
#ifdef USE_CURL_MULTI
fill_active_slots();
#endif
} while (request_queue_head && !aborted);
/* Update the remote branch if all went well */
if (aborted || !update_remote(ref->new_sha1, ref_lock)) {
if (aborted || !update_remote(ref->new_sha1, ref_lock))
rc = 1;
goto unlock;
}
unlock:
if (!rc)
fprintf(stderr, " done\n");
unlock_remote(ref_lock);

View File

@@ -18,6 +18,8 @@ static void process_blob(struct rev_info *revs,
if (!revs->blob_objects)
return;
if (!obj)
die("bad blob object");
if (obj->flags & (UNINTERESTING | SEEN))
return;
obj->flags |= SEEN;
@@ -69,6 +71,8 @@ static void process_tree(struct rev_info *revs,
if (!revs->tree_objects)
return;
if (!obj)
die("bad tree object");
if (obj->flags & (UNINTERESTING | SEEN))
return;
if (parse_tree(tree) < 0)

View File

@@ -149,10 +149,12 @@ void show_log(struct rev_info *opt, const char *sep)
opt->loginfo = NULL;
if (!opt->verbose_header) {
if (opt->left_right) {
if (commit->object.flags & BOUNDARY)
putchar('-');
else if (commit->object.flags & SYMMETRIC_LEFT)
if (commit->object.flags & BOUNDARY)
putchar('-');
else if (commit->object.flags & UNINTERESTING)
putchar('^');
else if (opt->left_right) {
if (commit->object.flags & SYMMETRIC_LEFT)
putchar('<');
else
putchar('>');
@@ -250,6 +252,8 @@ void show_log(struct rev_info *opt, const char *sep)
fputs("commit ", stdout);
if (commit->object.flags & BOUNDARY)
putchar('-');
else if (commit->object.flags & UNINTERESTING)
putchar('^');
else if (opt->left_right) {
if (commit->object.flags & SYMMETRIC_LEFT)
putchar('<');
@@ -278,6 +282,9 @@ void show_log(struct rev_info *opt, const char *sep)
}
}
if (!commit->buffer)
return;
/*
* And then the pretty-printed message itself
*/

View File

@@ -91,7 +91,7 @@ int main(int argc, char **argv)
signal(SIGCHLD, SIG_DFL);
if (argc < 3)
usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
setup_git_directory();
read_cache();

View File

@@ -1673,6 +1673,8 @@ static struct commit *get_ref(const char *ref)
if (get_sha1(ref, sha1))
die("Could not resolve ref '%s'", ref);
object = deref_tag(parse_object(sha1), ref, strlen(ref));
if (!object)
return NULL;
if (object->type == OBJ_TREE)
return make_virtual_commit((struct tree*)object,
better_branch_name(ref));

View File

@@ -34,7 +34,10 @@ static struct child_process pager_process = {
static void wait_for_pager(void)
{
fflush(stdout);
close(1); /* signals EOF to pager */
fflush(stderr);
/* signal EOF to pager */
close(1);
close(2);
finish_command(&pager_process);
}
#endif
@@ -76,6 +79,7 @@ void setup_pager(void)
/* return in the child */
if (!pid) {
dup2(fd[1], 1);
dup2(fd[1], 2);
close(fd[0]);
close(fd[1]);
return;
@@ -98,6 +102,7 @@ void setup_pager(void)
/* original process continues, but writes to the pipe */
dup2(pager_process.in, 1);
dup2(pager_process.in, 2);
close(pager_process.in);
/* this makes sure that the parent terminates after the pager */

178
pretty.c
View File

@@ -282,59 +282,59 @@ static char *logmsg_reencode(const struct commit *commit,
return out;
}
static void format_person_part(struct strbuf *sb, char part,
static size_t format_person_part(struct strbuf *sb, char part,
const char *msg, int len)
{
/* currently all placeholders have same length */
const int placeholder_len = 2;
int start, end, tz = 0;
unsigned long date;
unsigned long date = 0;
char *ep;
/* parse name */
/* advance 'end' to point to email start delimiter */
for (end = 0; end < len && msg[end] != '<'; end++)
; /* do nothing */
/*
* If it does not even have a '<' and '>', that is
* quite a bogus commit author and we discard it;
* this is in line with add_user_info() that is used
* in the normal codepath. When end points at the '<'
* that we found, it should have matching '>' later,
* which means start (beginning of email address) must
* be strictly below len.
*/
start = end + 1;
if (start >= len - 1)
return;
while (end > 0 && isspace(msg[end - 1]))
end--;
if (part == 'n') { /* name */
strbuf_add(sb, msg, end);
return;
}
/* parse email */
for (end = start; end < len && msg[end] != '>'; end++)
/*
* When end points at the '<' that we found, it should have
* matching '>' later, which means 'end' must be strictly
* below len - 1.
*/
if (end >= len - 2)
goto skip;
if (part == 'n') { /* name */
while (end > 0 && isspace(msg[end - 1]))
end--;
strbuf_add(sb, msg, end);
return placeholder_len;
}
start = ++end; /* save email start position */
/* advance 'end' to point to email end delimiter */
for ( ; end < len && msg[end] != '>'; end++)
; /* do nothing */
if (end >= len)
return;
goto skip;
if (part == 'e') { /* email */
strbuf_add(sb, msg + start, end - start);
return;
return placeholder_len;
}
/* parse date */
/* advance 'start' to point to date start delimiter */
for (start = end + 1; start < len && isspace(msg[start]); start++)
; /* do nothing */
if (start >= len)
return;
goto skip;
date = strtoul(msg + start, &ep, 10);
if (msg + start == ep)
return;
goto skip;
if (part == 't') { /* date, UNIX timestamp */
strbuf_add(sb, msg + start, ep - (msg + start));
return;
return placeholder_len;
}
/* parse tz */
@@ -349,17 +349,28 @@ static void format_person_part(struct strbuf *sb, char part,
switch (part) {
case 'd': /* date */
strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
return;
return placeholder_len;
case 'D': /* date, RFC2822 style */
strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
return;
return placeholder_len;
case 'r': /* date, relative */
strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
return;
return placeholder_len;
case 'i': /* date, ISO 8601 */
strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
return;
return placeholder_len;
}
skip:
/*
* bogus commit, 'sb' cannot be updated, but we still need to
* compute a valid return value.
*/
if (part == 'n' || part == 'e' || part == 't' || part == 'd'
|| part == 'D' || part == 'r' || part == 'i')
return placeholder_len;
return 0; /* unknown placeholder */
}
struct chunk {
@@ -440,7 +451,7 @@ static void parse_commit_header(struct format_commit_context *context)
context->commit_header_parsed = 1;
}
static void format_commit_item(struct strbuf *sb, const char *placeholder,
static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
void *context)
{
struct format_commit_context *c = context;
@@ -451,23 +462,23 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
/* these are independent of the commit */
switch (placeholder[0]) {
case 'C':
switch (placeholder[3]) {
case 'd': /* red */
if (!prefixcmp(placeholder + 1, "red")) {
strbuf_addstr(sb, "\033[31m");
return;
case 'e': /* green */
return 4;
} else if (!prefixcmp(placeholder + 1, "green")) {
strbuf_addstr(sb, "\033[32m");
return;
case 'u': /* blue */
return 6;
} else if (!prefixcmp(placeholder + 1, "blue")) {
strbuf_addstr(sb, "\033[34m");
return;
case 's': /* reset color */
return 5;
} else if (!prefixcmp(placeholder + 1, "reset")) {
strbuf_addstr(sb, "\033[m");
return;
}
return 6;
} else
return 0;
case 'n': /* newline */
strbuf_addch(sb, '\n');
return;
return 1;
}
/* these depend on the commit */
@@ -477,34 +488,34 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
switch (placeholder[0]) {
case 'H': /* commit hash */
strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
return;
return 1;
case 'h': /* abbreviated commit hash */
if (add_again(sb, &c->abbrev_commit_hash))
return;
return 1;
strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
DEFAULT_ABBREV));
c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
return;
return 1;
case 'T': /* tree hash */
strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
return;
return 1;
case 't': /* abbreviated tree hash */
if (add_again(sb, &c->abbrev_tree_hash))
return;
return 1;
strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
DEFAULT_ABBREV));
c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
return;
return 1;
case 'P': /* parent hashes */
for (p = commit->parents; p; p = p->next) {
if (p != commit->parents)
strbuf_addch(sb, ' ');
strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
}
return;
return 1;
case 'p': /* abbreviated parent hashes */
if (add_again(sb, &c->abbrev_parent_hashes))
return;
return 1;
for (p = commit->parents; p; p = p->next) {
if (p != commit->parents)
strbuf_addch(sb, ' ');
@@ -513,14 +524,14 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
}
c->abbrev_parent_hashes.len = sb->len -
c->abbrev_parent_hashes.off;
return;
return 1;
case 'm': /* left/right/bottom */
strbuf_addch(sb, (commit->object.flags & BOUNDARY)
? '-'
: (commit->object.flags & SYMMETRIC_LEFT)
? '<'
: '>');
return;
return 1;
}
/* For the rest we have to parse the commit header. */
@@ -528,66 +539,33 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
parse_commit_header(c);
switch (placeholder[0]) {
case 's':
case 's': /* subject */
strbuf_add(sb, msg + c->subject.off, c->subject.len);
return;
case 'a':
format_person_part(sb, placeholder[1],
return 1;
case 'a': /* author ... */
return format_person_part(sb, placeholder[1],
msg + c->author.off, c->author.len);
return;
case 'c':
format_person_part(sb, placeholder[1],
case 'c': /* committer ... */
return format_person_part(sb, placeholder[1],
msg + c->committer.off, c->committer.len);
return;
case 'e':
case 'e': /* encoding */
strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
return;
case 'b':
return 1;
case 'b': /* body */
strbuf_addstr(sb, msg + c->body_off);
return;
return 1;
}
return 0; /* unknown placeholder */
}
void format_commit_message(const struct commit *commit,
const void *format, struct strbuf *sb)
{
const char *placeholders[] = {
"H", /* commit hash */
"h", /* abbreviated commit hash */
"T", /* tree hash */
"t", /* abbreviated tree hash */
"P", /* parent hashes */
"p", /* abbreviated parent hashes */
"an", /* author name */
"ae", /* author email */
"ad", /* author date */
"aD", /* author date, RFC2822 style */
"ar", /* author date, relative */
"at", /* author date, UNIX timestamp */
"ai", /* author date, ISO 8601 */
"cn", /* committer name */
"ce", /* committer email */
"cd", /* committer date */
"cD", /* committer date, RFC2822 style */
"cr", /* committer date, relative */
"ct", /* committer date, UNIX timestamp */
"ci", /* committer date, ISO 8601 */
"e", /* encoding */
"s", /* subject */
"b", /* body */
"Cred", /* red */
"Cgreen", /* green */
"Cblue", /* blue */
"Creset", /* reset color */
"n", /* newline */
"m", /* left/right/bottom */
NULL
};
struct format_commit_context context;
memset(&context, 0, sizeof(context));
context.commit = commit;
strbuf_expand(sb, format, placeholders, format_commit_item, &context);
strbuf_expand(sb, format, format_commit_item, &context);
}
static void pp_header(enum cmit_fmt fmt,

View File

@@ -15,6 +15,8 @@ static void process_blob(struct blob *blob,
{
struct object *obj = &blob->object;
if (!blob)
die("bad blob object");
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
@@ -39,6 +41,8 @@ static void process_tree(struct tree *tree,
struct name_entry entry;
struct name_path me;
if (!tree)
die("bad tree object");
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
@@ -79,7 +83,8 @@ static void process_tag(struct tag *tag, struct object_array *p, const char *nam
if (parse_tag(tag) < 0)
die("bad tag object %s", sha1_to_hex(obj->sha1));
add_object(tag->tagged, p, NULL, name);
if (tag->tagged)
add_object(tag->tagged, p, NULL, name);
}
static void walk_commit_list(struct rev_info *revs)
@@ -150,7 +155,8 @@ static int add_one_reflog(const char *path, const unsigned char *sha1, int flag,
static void add_one_tree(const unsigned char *sha1, struct rev_info *revs)
{
struct tree *tree = lookup_tree(sha1);
add_pending_object(revs, &tree->object, "");
if (tree)
add_pending_object(revs, &tree->object, "");
}
static void add_cache_tree(struct cache_tree *it, struct rev_info *revs)
@@ -215,6 +221,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
* Set up the revision walk - this will move all commits
* from the pending list to the commit walking list.
*/
prepare_revision_walk(revs);
if (prepare_revision_walk(revs))
die("revision walk setup failed");
walk_commit_list(revs);
}

View File

@@ -46,6 +46,8 @@ void add_object(struct object *obj,
static void mark_blob_uninteresting(struct blob *blob)
{
if (!blob)
return;
if (blob->object.flags & UNINTERESTING)
return;
blob->object.flags |= UNINTERESTING;
@@ -57,6 +59,8 @@ void mark_tree_uninteresting(struct tree *tree)
struct name_entry entry;
struct object *obj = &tree->object;
if (!tree)
return;
if (obj->flags & UNINTERESTING)
return;
obj->flags |= UNINTERESTING;
@@ -173,6 +177,8 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
struct tag *tag = (struct tag *) object;
if (revs->tag_objects && !(flags & UNINTERESTING))
add_pending_object(revs, object, tag->tag);
if (!tag->tagged)
die("bad tag");
object = parse_object(tag->tagged->sha1);
if (!object)
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
@@ -558,6 +564,12 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
free_patch_ids(&ids);
}
static void add_to_list(struct commit_list **p, struct commit *commit, struct commit_list *n)
{
p = &commit_list_insert(commit, p)->next;
*p = n;
}
static int limit_list(struct rev_info *revs)
{
struct commit_list *list = revs->commits;
@@ -579,9 +591,13 @@ static int limit_list(struct rev_info *revs)
return -1;
if (obj->flags & UNINTERESTING) {
mark_parents_uninteresting(commit);
if (everybody_uninteresting(list))
if (everybody_uninteresting(list)) {
if (revs->show_all)
add_to_list(p, commit, list);
break;
continue;
}
if (!revs->show_all)
continue;
}
if (revs->min_age != -1 && (commit->date > revs->min_age))
continue;
@@ -685,6 +701,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
it = get_reference(revs, arg, sha1, 0);
if (it->type != OBJ_TAG)
break;
if (!((struct tag*)it)->tagged)
return 0;
hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
}
if (it->type != OBJ_COMMIT)
@@ -1055,6 +1073,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
revs->dense = 0;
continue;
}
if (!strcmp(arg, "--show-all")) {
revs->show_all = 1;
continue;
}
if (!strcmp(arg, "--remove-empty")) {
revs->remove_empty_trees = 1;
continue;
@@ -1438,6 +1460,8 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
return commit_ignore;
if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
return commit_ignore;
if (revs->show_all)
return commit_show;
if (commit->object.flags & UNINTERESTING)
return commit_ignore;
if (revs->min_age != -1 && (commit->date > revs->min_age))

View File

@@ -33,6 +33,7 @@ struct rev_info {
prune:1,
no_merges:1,
no_walk:1,
show_all:1,
remove_empty_trees:1,
simplify_history:1,
lifo:1,

194
setup.c
View File

@@ -10,68 +10,131 @@ static inline int is_dir_sep(char c) { return c == '/' || c == '\\'; }
static inline int is_dir_sep(char c) { return c == '/'; }
#endif
static int sanitary_path_copy(char *dst, const char *src)
{
char *dst0 = dst;
#ifdef __MINGW32__
if (isalpha(*src) && src[1] == ':') {
*dst++ = *src++;
*dst++ = *src++;
dst0 = dst;
}
#endif
if (is_dir_sep(*src)) {
*dst++ = '/';
while (is_dir_sep(*src))
src++;
}
for (;;) {
char c = *src;
/*
* A path component that begins with . could be
* special:
* (1) "." and ends -- ignore and terminate.
* (2) "./" -- ignore them, eat slash and continue.
* (3) ".." and ends -- strip one and terminate.
* (4) "../" -- strip one, eat slash and continue.
*/
if (c == '.') {
switch (src[1]) {
case '\0':
/* (1) */
src++;
break;
case '/':
#ifdef __MINGW32__
case '\\':
#endif
/* (2) */
src += 2;
while (is_dir_sep(*src))
src++;
continue;
case '.':
switch (src[2]) {
case '\0':
/* (3) */
src += 2;
goto up_one;
case '/':
#ifdef __MINGW32__
case '\\':
#endif
/* (4) */
src += 3;
while (is_dir_sep(*src))
src++;
goto up_one;
}
}
}
/* copy up to the next '/', and eat all '/' */
while ((c = *src++) != '\0' && !is_dir_sep(c))
*dst++ = c;
if (is_dir_sep(c)) {
*dst++ = '/';
while (is_dir_sep(c))
c = *src++;
src--;
} else if (!c)
break;
continue;
up_one:
/*
* dst0..dst is prefix portion, and dst[-1] is '/';
* go up one level.
*/
dst -= 2; /* go past trailing '/' if any */
if (dst < dst0)
return -1;
while (1) {
if (dst <= dst0)
break;
c = *dst--;
if (c == '/') { /* MinGW: cannot be '\\' anymore */
dst += 2;
break;
}
}
}
*dst = '\0';
return 0;
}
const char *prefix_path(const char *prefix, int len, const char *path)
{
const char *orig = path;
int do_pfx;
for (;;) {
char c;
if (*path != '.')
break;
c = path[1];
/* "." */
if (!c) {
path++;
break;
}
/* "./" */
if (is_dir_sep(c)) {
path += 2;
continue;
}
if (c != '.')
break;
c = path[2];
if (!c)
path += 2;
else if (is_dir_sep(c))
path += 3;
else
break;
/* ".." and "../" */
/* Remove last component of the prefix */
do {
if (!len)
die("'%s' is outside repository", orig);
len--;
} while (len && !is_dir_sep(prefix[len-1]));
continue;
char *sanitized = xmalloc(len + strlen(path) + 1);
if (is_absolute_path(orig))
strcpy(sanitized, path);
else {
if (len)
memcpy(sanitized, prefix, len);
strcpy(sanitized + len, path);
}
do_pfx = len;
#ifdef __MINGW32__
/* we want to convert '\' in path to '/' (prefix already has '/') */
{
const char *p = path;
while (!do_pfx && *p) {
do_pfx = *p++ == '\\';
if (sanitary_path_copy(sanitized, sanitized))
goto error_out;
if (is_absolute_path(orig)) {
const char *work_tree = get_git_work_tree();
size_t len = strlen(work_tree);
size_t total = strlen(sanitized) + 1;
if (strncmp(sanitized, work_tree, len) ||
(sanitized[len] != '\0' && sanitized[len] != '/')) {
error_out:
error("'%s' is outside repository", orig);
free(sanitized);
return NULL;
}
if (sanitized[len] == '/')
len++;
memmove(sanitized, sanitized + len, total - len);
}
#endif
if (do_pfx) {
int speclen = strlen(path);
char *n = xmalloc(speclen + len + 1);
char *p;
memcpy(n, prefix, len);
memcpy(n + len, path, speclen+1);
#ifdef __MINGW32__
for (p = n + len; *p; p++)
if (*p == '\\')
*p = '/';
#endif
path = n;
}
return path;
return sanitized;
}
/*
@@ -150,7 +213,7 @@ void verify_non_filename(const char *prefix, const char *arg)
const char **get_pathspec(const char *prefix, const char **pathspec)
{
const char *entry = *pathspec;
const char **p;
const char **src, **dst;
int prefixlen;
if (!prefix && !entry)
@@ -164,12 +227,19 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
}
/* Otherwise we have to re-write the entries.. */
p = pathspec;
src = pathspec;
dst = pathspec;
prefixlen = prefix ? strlen(prefix) : 0;
do {
*p = prefix_path(prefix, prefixlen, entry);
} while ((entry = *++p) != NULL);
return (const char **) pathspec;
while (*src) {
const char *p = prefix_path(prefix, prefixlen, *src);
if (p)
*(dst++) = p;
src++;
}
*dst = NULL;
if (!*pathspec)
return NULL;
return pathspec;
}
/*

View File

@@ -1855,6 +1855,15 @@ static struct cached_object {
} *cached_objects;
static int cached_object_nr, cached_object_alloc;
static struct cached_object empty_tree = {
/* empty tree sha1: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 */
"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60"
"\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04",
OBJ_TREE,
"",
0
};
static struct cached_object *find_cached_object(const unsigned char *sha1)
{
int i;
@@ -1864,6 +1873,8 @@ static struct cached_object *find_cached_object(const unsigned char *sha1)
if (!hashcmp(co->sha1, sha1))
return co;
}
if (!hashcmp(sha1, empty_tree.sha1))
return &empty_tree;
return NULL;
}
@@ -1953,7 +1964,8 @@ void *read_object_with_reference(const unsigned char *sha1,
}
ref_length = strlen(ref_type);
if (memcmp(buffer, ref_type, ref_length) ||
if (ref_length + 40 > isize ||
memcmp(buffer, ref_type, ref_length) ||
get_sha1_hex((char *) buffer + ref_length, actual_sha1)) {
free(buffer);
return NULL;

View File

@@ -494,8 +494,11 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
return error("%.*s: expected %s type, but the object dereferences to %s type",
len, name, typename(expected_type),
typename(o->type));
if (!o)
return -1;
if (!o->parsed)
parse_object(o->sha1);
if (!parse_object(o->sha1))
return -1;
}
}
return 0;
@@ -578,8 +581,11 @@ static int handle_one_ref(const char *path,
struct object *object = parse_object(sha1);
if (!object)
return 0;
if (object->type == OBJ_TAG)
if (object->type == OBJ_TAG) {
object = deref_tag(object, path, strlen(path));
if (!object)
return 0;
}
if (object->type != OBJ_COMMIT)
return 0;
insert_by_date((struct commit *)object, list);
@@ -617,7 +623,8 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
unsigned long size;
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
parse_object(commit->object.sha1);
if (!parse_object(commit->object.sha1))
continue;
if (temp_commit_buffer)
free(temp_commit_buffer);
if (commit->buffer)

View File

@@ -56,7 +56,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
if (i < heads->nr) {
commit = (struct commit *)
deref_tag(heads->objects[i++].item, NULL, 0);
if (commit->object.type != OBJ_COMMIT) {
if (!commit || commit->object.type != OBJ_COMMIT) {
commit = NULL;
continue;
}
@@ -70,7 +70,8 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
cur_depth = *(int *)commit->util;
}
}
parse_commit(commit);
if (parse_commit(commit))
die("invalid commit");
commit->object.flags |= not_shallow_flag;
cur_depth++;
for (p = commit->parents, commit = NULL; p; p = p->next) {

View File

@@ -146,11 +146,12 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
strbuf_setlen(sb, sb->len + len);
}
void strbuf_expand(struct strbuf *sb, const char *format,
const char **placeholders, expand_fn_t fn, void *context)
void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
void *context)
{
for (;;) {
const char *percent, **p;
const char *percent;
size_t consumed;
percent = strchrnul(format, '%');
strbuf_add(sb, format, percent - format);
@@ -158,14 +159,10 @@ void strbuf_expand(struct strbuf *sb, const char *format,
break;
format = percent + 1;
for (p = placeholders; *p; p++) {
if (!prefixcmp(format, *p))
break;
}
if (*p) {
fn(sb, *p, context);
format += strlen(*p);
} else
consumed = fn(sb, format, context);
if (consumed)
format += consumed;
else
strbuf_addch(sb, '%');
}
}

View File

@@ -103,8 +103,8 @@ static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) {
}
extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context);
typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
__attribute__((format(printf,2,3)))
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);

View File

@@ -61,8 +61,8 @@ test_expect_success 'setup' '
git tag I
'
cat > fake-editor.sh <<\EOF
#!/bin/sh
echo "#!$SHELL" >fake-editor
cat >> fake-editor.sh <<\EOF
case "$1" in
*/COMMIT_EDITMSG)
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"

69
t/t3701-add-interactive.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/sh
test_description='add -i basic tests'
. ./test-lib.sh
test_expect_success 'setup (initial)' '
echo content >file &&
git add file &&
echo more >>file &&
echo lines >>file
'
test_expect_success 'status works (initial)' '
git add -i </dev/null >output &&
grep "+1/-0 *+2/-0 file" output
'
cat >expected <<EOF
new file mode 100644
index 0000000..d95f3ad
--- /dev/null
+++ b/file
@@ -0,0 +1 @@
+content
EOF
test_expect_success 'diff works (initial)' '
(echo d; echo 1) | git add -i >output &&
sed -ne "/new file/,/content/p" <output >diff &&
diff -u expected diff
'
test_expect_success 'revert works (initial)' '
git add file &&
(echo r; echo 1) | git add -i &&
git ls-files >output &&
! grep . output
'
test_expect_success 'setup (commit)' '
echo baseline >file &&
git add file &&
git commit -m commit &&
echo content >>file &&
git add file &&
echo more >>file &&
echo lines >>file
'
test_expect_success 'status works (commit)' '
git add -i </dev/null >output &&
grep "+1/-0 *+2/-0 file" output
'
cat >expected <<EOF
index 180b47c..b6f2c08 100644
--- a/file
+++ b/file
@@ -1 +1,2 @@
baseline
+content
EOF
test_expect_success 'diff works (commit)' '
(echo d; echo 1) | git add -i >output &&
sed -ne "/^index/,/content/p" <output >diff &&
diff -u expected diff
'
test_expect_success 'revert works (commit)' '
git add file &&
(echo r; echo 1) | git add -i &&
git add -i </dev/null >output &&
grep "unchanged *+3/-0 file" output
'
test_done

View File

@@ -74,4 +74,12 @@ test_expect_success 'Even without -l, local will make a hardlink' '
test 0 = $copied
'
test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
cd "$D" &&
echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
git clone a d &&
cd d &&
git fetch &&
test ! -e .git/refs/remotes/origin/HEAD'
test_done

View File

@@ -118,4 +118,42 @@ test_expect_success "Sergey Vlasov's test case" '
git mv ab a
'
test_expect_success 'absolute pathname' '(
rm -fr mine &&
mkdir mine &&
cd mine &&
test_create_repo one &&
cd one &&
mkdir sub &&
>sub/file &&
git add sub/file &&
git mv sub "$(pwd)/in" &&
! test -d sub &&
test -d in &&
git ls-files --error-unmatch in/file
)'
test_expect_success 'absolute pathname outside should fail' '(
rm -fr mine &&
mkdir mine &&
cd mine &&
out=$(pwd) &&
test_create_repo one &&
cd one &&
mkdir sub &&
>sub/file &&
git add sub/file &&
! git mv sub "$out/out" &&
test -d sub &&
! test -d ../in &&
git ls-files --error-unmatch sub/file
)'
test_done

164
t/t7010-setup.sh Executable file
View File

@@ -0,0 +1,164 @@
#!/bin/sh
test_description='setup taking and sanitizing funny paths'
. ./test-lib.sh
test_expect_success setup '
mkdir -p a/b/c a/e &&
D=$(pwd) &&
>a/b/c/d &&
>a/e/f
'
test_expect_success 'git add (absolute)' '
git add "$D/a/b/c/d" &&
git ls-files >current &&
echo a/b/c/d >expect &&
diff -u expect current
'
test_expect_success 'git add (funny relative)' '
rm -f .git/index &&
(
cd a/b &&
git add "../e/./f"
) &&
git ls-files >current &&
echo a/e/f >expect &&
diff -u expect current
'
test_expect_success 'git rm (absolute)' '
rm -f .git/index &&
git add a &&
git rm -f --cached "$D/a/b/c/d" &&
git ls-files >current &&
echo a/e/f >expect &&
diff -u expect current
'
test_expect_success 'git rm (funny relative)' '
rm -f .git/index &&
git add a &&
(
cd a/b &&
git rm -f --cached "../e/./f"
) &&
git ls-files >current &&
echo a/b/c/d >expect &&
diff -u expect current
'
test_expect_success 'git ls-files (absolute)' '
rm -f .git/index &&
git add a &&
git ls-files "$D/a/e/../b" >current &&
echo a/b/c/d >expect &&
diff -u expect current
'
test_expect_success 'git ls-files (relative #1)' '
rm -f .git/index &&
git add a &&
(
cd a/b &&
git ls-files "../b/c"
) >current &&
echo c/d >expect &&
diff -u expect current
'
test_expect_success 'git ls-files (relative #2)' '
rm -f .git/index &&
git add a &&
(
cd a/b &&
git ls-files --full-name "../e/f"
) >current &&
echo a/e/f >expect &&
diff -u expect current
'
test_expect_success 'git ls-files (relative #3)' '
rm -f .git/index &&
git add a &&
(
cd a/b &&
if git ls-files "../e/f"
then
echo Gaah, should have failed
exit 1
else
: happy
fi
)
'
test_expect_success 'commit using absolute path names' '
git commit -m "foo" &&
echo aa >>a/b/c/d &&
git commit -m "aa" "$(pwd)/a/b/c/d"
'
test_expect_success 'log using absolute path names' '
echo bb >>a/b/c/d &&
git commit -m "bb" $(pwd)/a/b/c/d &&
git log a/b/c/d >f1.txt &&
git log "$(pwd)/a/b/c/d" >f2.txt &&
diff -u f1.txt f2.txt
'
test_expect_success 'blame using absolute path names' '
git blame a/b/c/d >f1.txt &&
git blame "$(pwd)/a/b/c/d" >f2.txt &&
diff -u f1.txt f2.txt
'
test_expect_success 'setup deeper work tree' '
test_create_repo tester
'
test_expect_success 'add a directory outside the work tree' '(
cd tester &&
d1="$(cd .. ; pwd)" &&
git add "$d1"
)'
test_expect_success 'add a file outside the work tree, nasty case 1' '(
cd tester &&
f="$(pwd)x" &&
echo "$f" &&
touch "$f" &&
git add "$f"
)'
test_expect_success 'add a file outside the work tree, nasty case 2' '(
cd tester &&
f="$(pwd | sed "s/.$//")x" &&
echo "$f" &&
touch "$f" &&
git add "$f"
)'
test_done

46
t/t7104-reset.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/sh
test_description='reset --hard unmerged'
. ./test-lib.sh
test_expect_success setup '
mkdir before later &&
>before/1 &&
>before/2 &&
>hello &&
>later/3 &&
git add before hello later &&
git commit -m world &&
H=$(git rev-parse :hello) &&
git rm --cached hello &&
echo "100644 $H 2 hello" | git update-index --index-info &&
rm -f hello &&
mkdir -p hello &&
>hello/world &&
test "$(git ls-files -o)" = hello/world
'
test_expect_success 'reset --hard should restore unmerged ones' '
git reset --hard &&
git ls-files --error-unmatch before/1 before/2 hello later/3 &&
test -f hello
'
test_expect_success 'reset --hard did not corrupt index nor cached-tree' '
T=$(git write-tree) &&
rm -f .git/index &&
git add before hello later &&
U=$(git write-tree) &&
test "$T" = "$U"
'
test_done

View File

@@ -316,4 +316,14 @@ test_expect_success 'core.excludesfile' '
'
test_expect_success 'removal failure' '
mkdir foo &&
touch foo/bar &&
chmod 0 foo &&
! git clean -f -d
'
chmod 755 foo
test_done

View File

@@ -112,4 +112,25 @@ test_expect_success 'allow long lines with --no-validate' '
2>errors
'
test_expect_success 'Invalid In-Reply-To' '
git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--in-reply-to=" " \
--smtp-server="$(pwd)/fake.sendmail" \
$patches
2>errors
! grep "^In-Reply-To: < *>" msgtxt
'
test_expect_success 'Valid In-Reply-To when prompting' '
(echo "From Example <from@example.com>"
echo "To Example <to@example.com>"
echo ""
) | env GIT_SEND_EMAIL_NOTTY=1 git send-email \
--smtp-server="$(pwd)/fake.sendmail" \
$patches 2>errors &&
! grep "^In-Reply-To: < *>" msgtxt
'
test_done

View File

@@ -266,4 +266,39 @@ test_expect_success '-w option should work with relative GIT_DIR' '
)
'
test_expect_success 'check files before directories' '
echo Notes > release-notes &&
git add release-notes &&
git commit -m "Add release notes" release-notes &&
id=$(git rev-parse HEAD) &&
git cvsexportcommit -w "$CVSWORK" -c $id &&
echo new > DS &&
echo new > E/DS &&
echo modified > release-notes &&
git add DS E/DS release-notes &&
git commit -m "Add two files with the same basename" &&
id=$(git rev-parse HEAD) &&
git cvsexportcommit -w "$CVSWORK" -c $id &&
check_entries "$CVSWORK/E" "DS/1.1/|newfile5.txt/1.1/" &&
check_entries "$CVSWORK" "DS/1.1/|release-notes/1.2/" &&
diff -u "$CVSWORK/DS" DS &&
diff -u "$CVSWORK/E/DS" E/DS &&
diff -u "$CVSWORK/release-notes" release-notes
'
test_expect_success 'commit a file with leading spaces in the name' '
echo space > " space" &&
git add " space" &&
git commit -m "Add a file with a leading space" &&
id=$(git rev-parse HEAD) &&
git cvsexportcommit -w "$CVSWORK" -c $id &&
check_entries "$CVSWORK" " space/1.1/|DS/1.1/|release-notes/1.2/" &&
diff -u "$CVSWORK/ space" " space"
'
test_done

5
tag.c
View File

@@ -9,7 +9,10 @@ const char *tag_type = "tag";
struct object *deref_tag(struct object *o, const char *warn, int warnlen)
{
while (o && o->type == OBJ_TAG)
o = parse_object(((struct tag *)o)->tagged->sha1);
if (((struct tag *)o)->tagged)
o = parse_object(((struct tag *)o)->tagged->sha1);
else
o = NULL;
if (!o && warn) {
if (!warnlen)
warnlen = strlen(warn);

View File

@@ -34,10 +34,10 @@ boilerplates.made : $(bpsrc)
mkdir -p blt/$$dir && \
case "$$boilerplate" in \
*--) ;; \
*) if head -1 $$boilerplate | grep -q '^#!/'; then \
cp $$boilerplate blt/$${dst}$(NOEXECTEMPL); \
*) if test -n "$$(sed -ne '/^#!\//p' -e '1q' < "$$boilerplate")"; then \
cp "$$boilerplate" "blt/$${dst}$(NOEXECTEMPL)"; \
else \
cp $$boilerplate blt/$$dst; \
cp "$$boilerplate" "blt/$$dst"; \
fi ;; \
esac || exit; \
done && \

View File

@@ -129,7 +129,8 @@ static int do_rev_list(int fd, void *create_full_pack)
}
setup_revisions(0, NULL, &revs, NULL);
}
prepare_revision_walk(&revs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(revs.commits, &revs, show_edge);
traverse_commit_list(&revs, show_commit, show_object);
fflush(pack_pipe);
@@ -535,7 +536,8 @@ static void receive_needs(void)
/* make sure the real parents are parsed */
unregister_shallow(object->sha1);
object->parsed = 0;
parse_commit((struct commit *)object);
if (parse_commit((struct commit *)object))
die("invalid commit");
parents = ((struct commit *)object)->parents;
while (parents) {
add_object_array(&parents->item->object,
@@ -577,7 +579,8 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
}
if (o->type == OBJ_TAG) {
o = deref_tag(o, refname, 0);
packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
if (o)
packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
}
return 0;
}

View File

@@ -9,7 +9,7 @@
#include "diffcore.h"
int wt_status_relative_paths = 1;
int wt_status_use_color = 0;
int wt_status_use_color = -1;
static char wt_status_colors[][COLOR_MAXLEN] = {
"", /* WT_STATUS_HEADER: normal */
"\033[32m", /* WT_STATUS_UPDATED: green */
@@ -40,7 +40,7 @@ static int parse_status_slot(const char *var, int offset)
static const char* color(int slot)
{
return wt_status_use_color ? wt_status_colors[slot] : "";
return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
}
void wt_status_prepare(struct wt_status *s)
@@ -403,5 +403,5 @@ int git_status_config(const char *k, const char *v)
wt_status_relative_paths = git_config_bool(k, v);
return 0;
}
return git_default_config(k, v);
return git_color_default_config(k, v);
}