Merge master into aw/mirror-push

This commit is contained in:
Junio C Hamano 2007-11-09 21:13:46 -08:00
commit 0d9d89f61c
82 changed files with 2184 additions and 1167 deletions

View File

@ -0,0 +1,112 @@
Like other projects, we also have some guidelines to keep to the
code. For git in general, three rough rules are:
- Most importantly, we never say "It's in POSIX; we'll happily
ignore your needs should your system not conform to it."
We live in the real world.
- However, we often say "Let's stay away from that construct,
it's not even in POSIX".
- In spite of the above two rules, we sometimes say "Although
this is not in POSIX, it (is so convenient | makes the code
much more readable | has other good characteristics) and
practically all the platforms we care about support it, so
let's use it".
Again, we live in the real world, and it is sometimes a
judgement call, the decision based more on real world
constraints people face than what the paper standard says.
As for more concrete guidelines, just imitate the existing code
(this is a good guideline, no matter which project you are
contributing to). But if you must have a list of rules,
here they are.
For shell scripts specifically (not exhaustive):
- We prefer $( ... ) for command substitution; unlike ``, it
properly nests. It should have been the way Bourne spelled
it from day one, but unfortunately isn't.
- We use ${parameter-word} and its [-=?+] siblings, and their
colon'ed "unset or null" form.
- We use ${parameter#word} and its [#%] siblings, and their
doubled "longest matching" form.
- We use Arithmetic Expansion $(( ... )).
- No "Substring Expansion" ${parameter:offset:length}.
- No shell arrays.
- No strlen ${#parameter}.
- No regexp ${parameter/pattern/string}.
- We do not use Process Substitution <(list) or >(list).
- We prefer "test" over "[ ... ]".
- We do not write the noiseword "function" in front of shell
functions.
For C programs:
- We use tabs to indent, and interpret tabs as taking up to
8 spaces.
- We try to keep to at most 80 characters per line.
- When declaring pointers, the star sides with the variable
name, i.e. "char *string", not "char* string" or
"char * string". This makes it easier to understand code
like "char *string, c;".
- We avoid using braces unnecessarily. I.e.
if (bla) {
x = 1;
}
is frowned upon. A gray area is when the statement extends
over a few lines, and/or you have a lengthy comment atop of
it. Also, like in the Linux kernel, if there is a long list
of "else if" statements, it can make sense to add braces to
single line blocks.
- Try to make your code understandable. You may put comments
in, but comments invariably tend to stale out when the code
they were describing changes. Often splitting a function
into two makes the intention of the code much clearer.
- Double negation is often harder to understand than no negation
at all.
- Some clever tricks, like using the !! operator with arithmetic
constructs, can be extremely confusing to others. Avoid them,
unless there is a compelling reason to use them.
- Use the API. No, really. We have a strbuf (variable length
string), several arrays with the ALLOC_GROW() macro, a
path_list for sorted string lists, a hash map (mapping struct
objects) named "struct decorate", amongst other things.
- When you come up with an API, document it.
- The first #include in C files, except in platform specific
compat/ implementations, should be git-compat-util.h or another
header file that includes it, such as cache.h or builtin.h.
- If you are planning a new command, consider writing it in shell
or perl first, so that changes in semantics can be easily
changed and discussed. Many git commands started out like
that, and a few are still scripts.
- Avoid introducing a new dependency into git. This means you
usually should stay away from scripting languages not already
used in the git core command set (unless your command is clearly
separate from it, such as an importer to convert random-scm-X
repositories to git).

View File

@ -63,8 +63,8 @@ Fixes since v1.5.3.4
* Git segfaulted when reading an invalid .gitattributes file. Fixed. * Git segfaulted when reading an invalid .gitattributes file. Fixed.
* post-receive-email example hook fixed was fixed for * post-receive-email example hook was fixed for non-fast-forward
non-fast-forward updates. updates.
* Documentation updates for supported (but previously undocumented) * Documentation updates for supported (but previously undocumented)
options of "git-archive" and "git-reflog". options of "git-archive" and "git-reflog".
@ -90,5 +90,5 @@ Fixes since v1.5.3.4
* "git-send-pack $remote frotz" segfaulted when there is nothing * "git-send-pack $remote frotz" segfaulted when there is nothing
named 'frotz' on the local end. named 'frotz' on the local end.
* "git-rebase -interactive" did not handle its "--strategy" option * "git-rebase --interactive" did not handle its "--strategy" option
properly. properly.

View File

@ -0,0 +1,21 @@
GIT v1.5.3.6 Release Notes
==========================
Fixes since v1.5.3.5
--------------------
* git-cvsexportcommit handles root commits better;
* git-svn dcommit used to clobber when sending a series of
patches;
* git-grep sometimes refused to work when your index was
unmerged;
* Quite a lot of documentation clarifications.
--
exec >/var/tmp/1
O=v1.5.3.5-32-gcb6c162
echo O=`git describe refs/heads/maint`
git shortlog --no-merges $O..refs/heads/maint

View File

@ -6,7 +6,10 @@ Updates since v1.5.3
* Comes with much improved gitk. * Comes with much improved gitk.
* git-reset is now built-in. * "progress display" from many commands are a lot nicer to the
eye. Transfer commands show throughput data.
* git-reset is now built-in and its output can be squelched with -q.
* git-send-email can optionally talk over ssmtp and use SMTP-AUTH. * git-send-email can optionally talk over ssmtp and use SMTP-AUTH.
@ -46,6 +49,28 @@ Updates since v1.5.3
* Various Perforce importer updates. * Various Perforce importer updates.
* git-lost-found was deprecated in favor of git-fsck's --lost-found
option.
* git-svnimport was removed in favor of git-svn.
* git-bisect learned "skip" action to mark untestable commits.
* rename detection diff family, while detecting exact matches,
has been greatly optimized.
* Example update and post-receive hooks have been improved.
* In addition there are quite a few internal clean-ups. Notably
- many fork/exec have been replaced with run-command API,
brought from the msysgit effort.
- introduction and more use of the option parser API.
- enhancement and more use of the strbuf API.
Fixes since v1.5.3 Fixes since v1.5.3
------------------ ------------------
@ -54,6 +79,6 @@ this release, unless otherwise noted.
-- --
exec >/var/tmp/1 exec >/var/tmp/1
O=v1.5.3.4-450-g952a9e5 O=v1.5.3.5-618-g5d4138a
echo O=`git describe refs/heads/master` echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint

View File

@ -20,9 +20,6 @@ Checklist (and a short version for the impatient):
Patch: Patch:
- use "git format-patch -M" to create the patch - use "git format-patch -M" to create the patch
- send your patch to <git@vger.kernel.org>. If you use
git-send-email(1), please test it first by sending
email to yourself.
- do not PGP sign your patch - do not PGP sign your patch
- do not attach your patch, but read in the mail - do not attach your patch, but read in the mail
body, unless you cannot teach your mailer to body, unless you cannot teach your mailer to
@ -31,13 +28,15 @@ Checklist (and a short version for the impatient):
corrupt whitespaces. corrupt whitespaces.
- provide additional information (which is unsuitable for - provide additional information (which is unsuitable for
the commit message) between the "---" and the diffstat the commit message) between the "---" and the diffstat
- send the patch to the list (git@vger.kernel.org) and the
maintainer (gitster@pobox.com).
- if you change, add, or remove a command line option or - if you change, add, or remove a command line option or
make some other user interface change, the associated make some other user interface change, the associated
documentation should be updated as well. documentation should be updated as well.
- if your name is not writable in ASCII, make sure that - if your name is not writable in ASCII, make sure that
you send off a message in the correct encoding. you send off a message in the correct encoding.
- send the patch to the list (git@vger.kernel.org) and the
maintainer (gitster@pobox.com). If you use
git-send-email(1), please test it first by sending
email to yourself.
Long version: Long version:

View File

@ -345,8 +345,8 @@ branch.<name>.mergeoptions::
supported. supported.
clean.requireForce:: clean.requireForce::
A boolean to make git-clean do nothing unless given -f or -n. Defaults A boolean to make git-clean do nothing unless given -f
to false. or -n. Defaults to true.
color.branch:: color.branch::
A boolean to enable/disable color in the output of A boolean to enable/disable color in the output of
@ -661,6 +661,15 @@ pack.threads::
machines. The required amount of memory for the delta search window machines. The required amount of memory for the delta search window
is however multiplied by the number of threads. is however multiplied by the number of threads.
pack.indexVersion::
Specify the default pack index version. Valid values are 1 for
legacy pack index used by Git versions prior to 1.5.2, and 2 for
the new pack index with capabilities for packs larger than 4 GB
as well as proper protection against the repacking of corrupted
packs. Version 2 is selected and this config option ignored
whenever the corresponding pack is larger than 2 GB. Otherwise
the default is 1.
pull.octopus:: pull.octopus::
The default merge strategy to use when pulling multiple branches The default merge strategy to use when pulling multiple branches
at once. at once.

View File

@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit
SYNOPSIS SYNOPSIS
-------- --------
'git-cherry-pick' [--edit] [-n] [-x] <commit> 'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>
DESCRIPTION DESCRIPTION
----------- -----------
@ -44,6 +44,13 @@ OPTIONS
described above, and `-r` was to disable it. Now the described above, and `-r` was to disable it. Now the
default is not to do `-x` so this option is a no-op. default is not to do `-x` so this option is a no-op.
-m parent-number|--mainline parent-number::
Usually you cannot revert a merge because you do not know which
side of the merge should be considered the mainline. This
option specifies the parent number (starting from 1) of
the mainline and allows cherry-pick to replay the change
relative to the specified parent.
-n|--no-commit:: -n|--no-commit::
Usually the command automatically creates a commit with Usually the command automatically creates a commit with
a commit log message stating which commit was a commit log message stating which commit was

View File

@ -12,7 +12,7 @@ SYNOPSIS
'git-clone' [--template=<template_directory>] 'git-clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>] [-o <name>] [-u <upload-pack>] [--reference <repository>]
[--depth <depth>] <repository> [<directory>] [--depth <depth>] [--] <repository> [<directory>]
DESCRIPTION DESCRIPTION
----------- -----------

View File

@ -11,6 +11,10 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
*NOTE*: this command is deprecated. Use gitlink:git-fsck[1] with
the option '--lost-found' instead.
Finds dangling commits and tags from the object database, and Finds dangling commits and tags from the object database, and
creates refs to them in the .git/lost-found/ directory. Commits and creates refs to them in the .git/lost-found/ directory. Commits and
tags that dereference to commits are stored in .git/lost-found/commit, tags that dereference to commits are stored in .git/lost-found/commit,

View File

@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git-reset' [--mixed | --soft | --hard] [<commit>] 'git-reset' [--mixed | --soft | --hard] [-q] [<commit>]
'git-reset' [--mixed] <commit> [--] <paths>... 'git-reset' [--mixed] [-q] <commit> [--] <paths>...
DESCRIPTION DESCRIPTION
----------- -----------
@ -45,6 +45,9 @@ OPTIONS
switched to. Any changes to tracked files in the working tree switched to. Any changes to tracked files in the working tree
since <commit> are lost. since <commit> are lost.
-q::
Be quiet, only report errors.
<commit>:: <commit>::
Commit to make the current HEAD. Commit to make the current HEAD.

View File

@ -7,7 +7,7 @@ git-revert - Revert an existing commit
SYNOPSIS SYNOPSIS
-------- --------
'git-revert' [--edit | --no-edit] [-n] <commit> 'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>
DESCRIPTION DESCRIPTION
----------- -----------
@ -27,6 +27,13 @@ OPTIONS
message prior committing the revert. This is the default if message prior committing the revert. This is the default if
you run the command from a terminal. you run the command from a terminal.
-m parent-number|--mainline parent-number::
Usually you cannot revert a merge because you do not know which
side of the merge should be considered the mainline. This
option specifies the parent number (starting from 1) of
the mainline and allows revert to reverse the change
relative to the specified parent.
--no-edit:: --no-edit::
With this option, `git-revert` will not start the commit With this option, `git-revert` will not start the commit
message editor. message editor.

View File

@ -113,8 +113,7 @@ The --cc option must be repeated for each user you want on the cc list.
is not set, this will be prompted for. is not set, this will be prompted for.
--suppress-from, --no-suppress-from:: --suppress-from, --no-suppress-from::
If this is set, do not add the From: address to the cc: list, if it If this is set, do not add the From: address to the cc: list.
shows up in a From: line.
Default is the value of 'sendemail.suppressfrom' configuration value; Default is the value of 'sendemail.suppressfrom' configuration value;
if that is unspecified, default to --no-suppress-from. if that is unspecified, default to --no-suppress-from.

View File

@ -98,6 +98,8 @@ all::
# Define OLD_ICONV if your library has an old iconv(), where the second # Define OLD_ICONV if your library has an old iconv(), where the second
# (input buffer pointer) parameter is declared with type (const char **). # (input buffer pointer) parameter is declared with type (const char **).
# #
# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
#
# Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib" # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
# that tells runtime paths to dynamic libraries; # that tells runtime paths to dynamic libraries;
# "-Wl,-rpath=/path/lib" is used instead. # "-Wl,-rpath=/path/lib" is used instead.
@ -298,7 +300,7 @@ DIFF_OBJS = \
LIB_OBJS = \ LIB_OBJS = \
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o \
interpolate.o hash.o \ pretty.o interpolate.o hash.o \
lockfile.o \ lockfile.o \
patch-ids.o \ patch-ids.o \
object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \ object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
@ -662,6 +664,10 @@ ifdef OLD_ICONV
BASIC_CFLAGS += -DOLD_ICONV BASIC_CFLAGS += -DOLD_ICONV
endif endif
ifdef NO_DEFLATE_BOUND
BASIC_CFLAGS += -DNO_DEFLATE_BOUND
endif
ifdef PPC_SHA1 ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h" SHA1_HEADER = "ppc/sha1.h"
LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
@ -917,6 +923,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h
$(LIB_FILE): $(LIB_OBJS) $(LIB_FILE): $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)

View File

@ -2215,9 +2215,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
argv[unk++] = arg; argv[unk++] = arg;
} }
if (!incremental)
setup_pager();
if (!blame_move_score) if (!blame_move_score)
blame_move_score = BLAME_DEFAULT_MOVE_SCORE; blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
if (!blame_copy_score) if (!blame_copy_score)
@ -2345,6 +2342,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
* do not default to HEAD, but use the working tree * do not default to HEAD, but use the working tree
* or "--contents". * or "--contents".
*/ */
setup_work_tree();
sb.final = fake_working_tree_commit(path, contents_from); sb.final = fake_working_tree_commit(path, contents_from);
add_pending_object(&revs, &(sb.final->object), ":"); add_pending_object(&revs, &(sb.final->object), ":");
} }
@ -2411,6 +2409,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
read_mailmap(&mailmap, ".mailmap", NULL); read_mailmap(&mailmap, ".mailmap", NULL);
if (!incremental)
setup_pager();
assign_blame(&sb, &revs, opt); assign_blame(&sb, &revs, opt);
if (incremental) if (incremental)

View File

@ -148,7 +148,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
if (!force && if (!force &&
!in_merge_bases(rev, &head_rev, 1)) { !in_merge_bases(rev, &head_rev, 1)) {
error("The branch '%s' is not a strict subset of " error("The branch '%s' is not an ancestor of "
"your current HEAD.\n" "your current HEAD.\n"
"If you are sure you want to delete it, " "If you are sure you want to delete it, "
"run 'git branch -D %s'.", argv[i], argv[i]); "run 'git branch -D %s'.", argv[i], argv[i]);
@ -282,7 +282,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
commit = lookup_commit(item->sha1); commit = lookup_commit(item->sha1);
if (commit && !parse_commit(commit)) { if (commit && !parse_commit(commit)) {
pretty_print_commit(CMIT_FMT_ONELINE, commit, pretty_print_commit(CMIT_FMT_ONELINE, commit,
&subject, 0, NULL, NULL, 0); &subject, 0, NULL, NULL, 0, 0);
sub = subject.buf; sub = subject.buf;
} }
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),

View File

@ -200,15 +200,11 @@ static void refresh_index_quietly(void)
discard_cache(); discard_cache();
read_cache(); read_cache();
refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
if (active_cache_changed) {
if (write_cache(fd, active_cache, active_nr) || if (active_cache_changed &&
close(fd) || !write_cache(fd, active_cache, active_nr) && !close(fd))
commit_locked_index(lock_file)) commit_locked_index(lock_file);
; /*
* silently ignore it -- we haven't mucked
* with the real index.
*/
}
rollback_lock_file(lock_file); rollback_lock_file(lock_file);
} }

View File

@ -32,7 +32,7 @@ static const char fetch_pack_usage[] =
#define MAX_IN_VAIN 256 #define MAX_IN_VAIN 256
static struct commit_list *rev_list; static struct commit_list *rev_list;
static int non_common_revs, multi_ack, use_thin_pack, use_sideband; static int non_common_revs, multi_ack, use_sideband;
static void rev_list_push(struct commit *commit, int mark) static void rev_list_push(struct commit *commit, int mark)
{ {
@ -178,7 +178,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
(multi_ack ? " multi_ack" : ""), (multi_ack ? " multi_ack" : ""),
(use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 2 ? " side-band-64k" : ""),
(use_sideband == 1 ? " side-band" : ""), (use_sideband == 1 ? " side-band" : ""),
(use_thin_pack ? " thin-pack" : ""), (args.use_thin_pack ? " thin-pack" : ""),
(args.no_progress ? " no-progress" : ""), (args.no_progress ? " no-progress" : ""),
" ofs-delta"); " ofs-delta");
else else

View File

@ -131,12 +131,6 @@ static struct ref *get_ref_map(struct transport *transport,
return ref_map; return ref_map;
} }
static void show_new(enum object_type type, unsigned char *sha1_new)
{
fprintf(stderr, " %s: %s\n", typename(type),
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
}
static int s_update_ref(const char *action, static int s_update_ref(const char *action,
struct ref *ref, struct ref *ref,
int check_old) int check_old)
@ -157,34 +151,38 @@ static int s_update_ref(const char *action,
return 0; return 0;
} }
#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
static int update_local_ref(struct ref *ref, static int update_local_ref(struct ref *ref,
const char *note, const char *remote,
int verbose) int verbose,
char *display)
{ {
char oldh[41], newh[41];
struct commit *current = NULL, *updated; struct commit *current = NULL, *updated;
enum object_type type; enum object_type type;
struct branch *current_branch = branch_get(NULL); struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = ref->name + (
!prefixcmp(ref->name, "refs/heads/") ? 11 :
!prefixcmp(ref->name, "refs/tags/") ? 10 :
!prefixcmp(ref->name, "refs/remotes/") ? 13 :
0);
*display = 0;
type = sha1_object_info(ref->new_sha1, NULL); type = sha1_object_info(ref->new_sha1, NULL);
if (type < 0) if (type < 0)
die("object %s not found", sha1_to_hex(ref->new_sha1)); die("object %s not found", sha1_to_hex(ref->new_sha1));
if (!*ref->name) { if (!*ref->name) {
/* Not storing */ /* Not storing */
if (verbose) { if (verbose)
fprintf(stderr, "* fetched %s\n", note); sprintf(display, "* branch %s -> FETCH_HEAD", remote);
show_new(type, ref->new_sha1);
}
return 0; return 0;
} }
if (!hashcmp(ref->old_sha1, ref->new_sha1)) { if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
if (verbose) { if (verbose)
fprintf(stderr, "* %s: same as %s\n", sprintf(display, "= %-*s %s -> %s", SUMMARY_WIDTH,
ref->name, note); "[up to date]", remote, pretty_ref);
show_new(type, ref->new_sha1);
}
return 0; return 0;
} }
@ -196,63 +194,65 @@ static int update_local_ref(struct ref *ref,
* If this is the head, and it's not okay to update * If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty... * the head, and the old value of the head isn't empty...
*/ */
fprintf(stderr, sprintf(display, "! %-*s %s -> %s (can't fetch in current branch)",
" * %s: Cannot fetch into the current branch.\n", SUMMARY_WIDTH, "[rejected]", remote, pretty_ref);
ref->name);
return 1; return 1;
} }
if (!is_null_sha1(ref->old_sha1) && if (!is_null_sha1(ref->old_sha1) &&
!prefixcmp(ref->name, "refs/tags/")) { !prefixcmp(ref->name, "refs/tags/")) {
fprintf(stderr, "* %s: updating with %s\n", sprintf(display, "- %-*s %s -> %s",
ref->name, note); SUMMARY_WIDTH, "[tag update]", remote, pretty_ref);
show_new(type, ref->new_sha1);
return s_update_ref("updating tag", ref, 0); return s_update_ref("updating tag", ref, 0);
} }
current = lookup_commit_reference_gently(ref->old_sha1, 1); current = lookup_commit_reference_gently(ref->old_sha1, 1);
updated = lookup_commit_reference_gently(ref->new_sha1, 1); updated = lookup_commit_reference_gently(ref->new_sha1, 1);
if (!current || !updated) { if (!current || !updated) {
char *msg; const char *msg;
if (!strncmp(ref->name, "refs/tags/", 10)) const char *what;
if (!strncmp(ref->name, "refs/tags/", 10)) {
msg = "storing tag"; msg = "storing tag";
else what = "[new tag]";
}
else {
msg = "storing head"; msg = "storing head";
fprintf(stderr, "* %s: storing %s\n", what = "[new branch]";
ref->name, note); }
show_new(type, ref->new_sha1);
sprintf(display, "* %-*s %s -> %s",
SUMMARY_WIDTH, what, remote, pretty_ref);
return s_update_ref(msg, ref, 0); return s_update_ref(msg, ref, 0);
} }
strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
strcpy(newh, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
if (in_merge_bases(current, &updated, 1)) { if (in_merge_bases(current, &updated, 1)) {
fprintf(stderr, "* %s: fast forward to %s\n", char quickref[83];
ref->name, note); strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
fprintf(stderr, " old..new: %s..%s\n", oldh, newh); strcat(quickref, "..");
strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
sprintf(display, " %-*s %s -> %s (fast forward)",
SUMMARY_WIDTH, quickref, remote, pretty_ref);
return s_update_ref("fast forward", ref, 1); return s_update_ref("fast forward", ref, 1);
} } else if (force || ref->force) {
if (!force && !ref->force) { char quickref[84];
fprintf(stderr, strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
"* %s: not updating to non-fast forward %s\n", strcat(quickref, "...");
ref->name, note); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
fprintf(stderr, sprintf(display, "+ %-*s %s -> %s (forced update)",
" old...new: %s...%s\n", oldh, newh); SUMMARY_WIDTH, quickref, remote, pretty_ref);
return s_update_ref("forced-update", ref, 1);
} else {
sprintf(display, "! %-*s %s -> %s (non fast forward)",
SUMMARY_WIDTH, "[rejected]", remote, pretty_ref);
return 1; return 1;
} }
fprintf(stderr,
"* %s: forcing update to non-fast forward %s\n",
ref->name, note);
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
return s_update_ref("forced-update", ref, 1);
} }
static void store_updated_refs(const char *url, struct ref *ref_map) static void store_updated_refs(const char *url, struct ref *ref_map)
{ {
FILE *fp; FILE *fp;
struct commit *commit; struct commit *commit;
int url_len, i, note_len; int url_len, i, note_len, shown_url = 0;
char note[1024]; char note[1024];
const char *what, *kind; const char *what, *kind;
struct ref *rm; struct ref *rm;
@ -315,8 +315,17 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
rm->merge ? "" : "not-for-merge", rm->merge ? "" : "not-for-merge",
note); note);
if (ref) if (ref) {
update_local_ref(ref, note, verbose); update_local_ref(ref, what, verbose, note);
if (*note) {
if (!shown_url) {
fprintf(stderr, "From %.*s\n",
url_len, url);
shown_url = 1;
}
fprintf(stderr, " %s\n", note);
}
}
} }
fclose(fp); fclose(fp);
} }
@ -376,9 +385,6 @@ static struct ref *find_non_local_tags(struct transport *transport,
if (!path_list_has_path(&existing_refs, ref_name) && if (!path_list_has_path(&existing_refs, ref_name) &&
!path_list_has_path(&new_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) &&
lookup_object(ref->old_sha1)) { lookup_object(ref->old_sha1)) {
fprintf(stderr, "Auto-following %s\n",
ref_name);
path_list_insert(ref_name, &new_refs); path_list_insert(ref_name, &new_refs);
rm = alloc_ref(strlen(ref_name) + 1); rm = alloc_ref(strlen(ref_name) + 1);
@ -517,7 +523,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
depth = argv[i]; depth = argv[i];
continue; continue;
} }
if (!strcmp(arg, "--quiet")) { if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1; quiet = 1;
continue; continue;
} }

View File

@ -175,7 +175,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
char buf[80]; char buf[80];
struct option builtin_gc_options[] = { struct option builtin_gc_options[] = {
OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced loose objects"), OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
OPT_END() OPT_END()

View File

@ -343,7 +343,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
memcpy(name + 2, ce->name, len + 1); memcpy(name + 2, ce->name, len + 1);
} }
argv[argc++] = name; argv[argc++] = name;
if (argc < MAXARGS && !ce_stage(ce)) if (argc < MAXARGS)
continue; continue;
status = flush_grep(opt, argc, nr, argv, &kept); status = flush_grep(opt, argc, nr, argv, &kept);
if (0 < status) if (0 < status)

View File

@ -787,7 +787,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
struct strbuf buf; struct strbuf buf;
strbuf_init(&buf, 0); strbuf_init(&buf, 0);
pretty_print_commit(CMIT_FMT_ONELINE, commit, pretty_print_commit(CMIT_FMT_ONELINE, commit,
&buf, 0, NULL, NULL, 0); &buf, 0, NULL, NULL, 0, 0);
printf("%c %s %s\n", sign, printf("%c %s %s\n", sign,
sha1_to_hex(commit->object.sha1), buf.buf); sha1_to_hex(commit->object.sha1), buf.buf);
strbuf_release(&buf); strbuf_release(&buf);

View File

@ -525,11 +525,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
break; break;
} }
if (require_work_tree && !is_inside_work_tree()) { if (require_work_tree && !is_inside_work_tree())
const char *work_tree = get_git_work_tree(); setup_work_tree();
if (!work_tree || chdir(work_tree))
die("This operation must be run in a work tree");
}
pathspec = get_pathspec(prefix, argv + i); pathspec = get_pathspec(prefix, argv + i);

View File

@ -915,6 +915,7 @@ static void handle_info(void)
static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
const char *msg, const char *patch) const char *msg, const char *patch)
{ {
int peek;
keep_subject = ks; keep_subject = ks;
metainfo_charset = encoding; metainfo_charset = encoding;
fin = in; fin = in;
@ -935,6 +936,11 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));
s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));
do {
peek = fgetc(in);
} while (isspace(peek));
ungetc(peek, in);
/* process the email header */ /* process the email header */
while (read_one_header_line(line, sizeof(line), fin)) while (read_one_header_line(line, sizeof(line), fin))
check_header(line, sizeof(line), p_hdr_data, 1); check_header(line, sizeof(line), p_hdr_data, 1);

View File

@ -101,19 +101,28 @@ static int populate_maildir_list(struct path_list *list, const char *path)
{ {
DIR *dir; DIR *dir;
struct dirent *dent; struct dirent *dent;
char name[PATH_MAX];
char *subs[] = { "cur", "new", NULL };
char **sub;
if ((dir = opendir(path)) == NULL) { for (sub = subs; *sub; ++sub) {
error("cannot opendir %s (%s)", path, strerror(errno)); snprintf(name, sizeof(name), "%s/%s", path, *sub);
if ((dir = opendir(name)) == NULL) {
if (errno == ENOENT)
continue;
error("cannot opendir %s (%s)", name, strerror(errno));
return -1; return -1;
} }
while ((dent = readdir(dir)) != NULL) { while ((dent = readdir(dir)) != NULL) {
if (dent->d_name[0] == '.') if (dent->d_name[0] == '.')
continue; continue;
path_list_insert(dent->d_name, list); snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
path_list_insert(name, list);
} }
closedir(dir); closedir(dir);
}
return 0; return 0;
} }
@ -122,19 +131,17 @@ static int split_maildir(const char *maildir, const char *dir,
int nr_prec, int skip) int nr_prec, int skip)
{ {
char file[PATH_MAX]; char file[PATH_MAX];
char curdir[PATH_MAX];
char name[PATH_MAX]; char name[PATH_MAX];
int ret = -1; int ret = -1;
int i; int i;
struct path_list list = {NULL, 0, 0, 1}; struct path_list list = {NULL, 0, 0, 1};
snprintf(curdir, sizeof(curdir), "%s/cur", maildir); if (populate_maildir_list(&list, maildir) < 0)
if (populate_maildir_list(&list, curdir) < 0)
goto out; goto out;
for (i = 0; i < list.nr; i++) { for (i = 0; i < list.nr; i++) {
FILE *f; FILE *f;
snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path); snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].path);
f = fopen(file, "r"); f = fopen(file, "r");
if (!f) { if (!f) {
error("cannot open mail %s (%s)", file, strerror(errno)); error("cannot open mail %s (%s)", file, strerror(errno));
@ -152,10 +159,9 @@ static int split_maildir(const char *maildir, const char *dir,
fclose(f); fclose(f);
} }
path_list_clear(&list, 1);
ret = skip; ret = skip;
out: out:
path_list_clear(&list, 1);
return ret; return ret;
} }
@ -164,6 +170,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
{ {
char name[PATH_MAX]; char name[PATH_MAX];
int ret = -1; int ret = -1;
int peek;
FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
int file_done = 0; int file_done = 0;
@ -173,6 +180,11 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
goto out; goto out;
} }
do {
peek = fgetc(f);
} while (isspace(peek));
ungetc(peek, f);
if (fgets(buf, sizeof(buf), f) == NULL) { if (fgets(buf, sizeof(buf), f) == NULL) {
/* empty stdin is OK */ /* empty stdin is OK */
if (f != stdin) { if (f != stdin) {

View File

@ -57,7 +57,7 @@ struct object_entry {
* nice "minimum seek" order. * nice "minimum seek" order.
*/ */
static struct object_entry *objects; static struct object_entry *objects;
static struct object_entry **written_list; static struct pack_idx_entry **written_list;
static uint32_t nr_objects, nr_alloc, nr_result, nr_written; static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
static int non_empty; static int non_empty;
@ -577,7 +577,7 @@ static off_t write_one(struct sha1file *f,
e->idx.offset = 0; e->idx.offset = 0;
return 0; return 0;
} }
written_list[nr_written++] = e; written_list[nr_written++] = &e->idx;
/* make sure off_t is sufficiently large not to wrap */ /* make sure off_t is sufficiently large not to wrap */
if (offset > offset + size) if (offset > offset + size)
@ -599,7 +599,7 @@ static void write_pack_file(void)
if (do_progress) if (do_progress)
progress_state = start_progress("Writing objects", nr_result); progress_state = start_progress("Writing objects", nr_result);
written_list = xmalloc(nr_objects * sizeof(struct object_entry *)); written_list = xmalloc(nr_objects * sizeof(*written_list));
do { do {
unsigned char sha1[20]; unsigned char sha1[20];
@ -651,8 +651,7 @@ static void write_pack_file(void)
umask(mode); umask(mode);
mode = 0444 & ~mode; mode = 0444 & ~mode;
idx_tmp_name = write_idx_file(NULL, idx_tmp_name = write_idx_file(NULL, written_list,
(struct pack_idx_entry **) written_list,
nr_written, sha1); nr_written, sha1);
snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
base_name, sha1_to_hex(sha1)); base_name, sha1_to_hex(sha1));
@ -677,7 +676,7 @@ static void write_pack_file(void)
/* mark written objects as written to previous pack */ /* mark written objects as written to previous pack */
for (j = 0; j < nr_written; j++) { for (j = 0; j < nr_written; j++) {
written_list[j]->idx.offset = (off_t)-1; written_list[j]->offset = (off_t)-1;
} }
nr_remaining -= nr_written; nr_remaining -= nr_written;
} while (nr_remaining && i < nr_objects); } while (nr_remaining && i < nr_objects);
@ -1768,6 +1767,12 @@ static int git_pack_config(const char *k, const char *v)
#endif #endif
return 0; return 0;
} }
if (!strcmp(k, "pack.indexversion")) {
pack_idx_default_version = git_config_int(k, v);
if (pack_idx_default_version > 2)
die("bad pack.indexversion=%d", pack_idx_default_version);
return 0;
}
return git_default_config(k, v); return git_default_config(k, v);
} }

View File

@ -7,8 +7,12 @@
#include "builtin.h" #include "builtin.h"
#include "remote.h" #include "remote.h"
#include "transport.h" #include "transport.h"
#include "parse-options.h"
static const char push_usage[] = "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]"; static const char * const push_usage[] = {
"git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
NULL,
};
static int thin, verbose; static int thin, verbose;
static const char *receivepack; static const char *receivepack;
@ -85,63 +89,43 @@ static int do_push(const char *repo, int flags)
int cmd_push(int argc, const char **argv, const char *prefix) int cmd_push(int argc, const char **argv, const char *prefix)
{ {
int i;
int flags = 0; int flags = 0;
int all = 0;
int dry_run = 0;
int force = 0;
int tags = 0;
const char *repo = NULL; /* default repository */ const char *repo = NULL; /* default repository */
for (i = 1; i < argc; i++) { struct option options[] = {
const char *arg = argv[i]; OPT__VERBOSE(&verbose),
OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
OPT_BOOLEAN( 0 , "all", &all, "push all refs"),
OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"),
OPT_BOOLEAN('f', "force", &force, "force updates"),
OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
OPT_END()
};
if (arg[0] != '-') { argc = parse_options(argc, argv, options, push_usage, 0);
repo = arg;
i++; if (force)
break;
}
if (!strcmp(arg, "-v")) {
verbose=1;
continue;
}
if (!prefixcmp(arg, "--repo=")) {
repo = arg+7;
continue;
}
if (!strcmp(arg, "--all")) {
flags |= TRANSPORT_PUSH_ALL;
continue;
}
if (!strcmp(arg, "--dry-run")) {
flags |= TRANSPORT_PUSH_DRY_RUN;
continue;
}
if (!strcmp(arg, "--tags")) {
add_refspec("refs/tags/*");
continue;
}
if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
flags |= TRANSPORT_PUSH_FORCE; flags |= TRANSPORT_PUSH_FORCE;
continue; if (dry_run)
flags |= TRANSPORT_PUSH_DRY_RUN;
if (tags)
add_refspec("refs/tags/*");
if (all)
flags |= TRANSPORT_PUSH_ALL;
if (argc > 0) {
repo = argv[0];
set_refspecs(argv + 1, argc - 1);
} }
if (!strcmp(arg, "--thin")) {
thin = 1;
continue;
}
if (!strcmp(arg, "--no-thin")) {
thin = 0;
continue;
}
if (!prefixcmp(arg, "--receive-pack=")) {
receivepack = arg + 15;
continue;
}
if (!prefixcmp(arg, "--exec=")) {
receivepack = arg + 7;
continue;
}
usage(push_usage);
}
set_refspecs(argv + i, argc - i);
if ((flags & TRANSPORT_PUSH_ALL) && refspec) if ((flags & TRANSPORT_PUSH_ALL) && refspec)
usage(push_usage); usage_with_options(push_usage, options);
return do_push(repo, flags); return do_push(repo, flags);
} }

View File

@ -18,7 +18,7 @@
#include "tree.h" #include "tree.h"
static const char builtin_reset_usage[] = static const char builtin_reset_usage[] =
"git-reset [--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]"; "git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
static char *args_to_str(const char **argv) static char *args_to_str(const char **argv)
{ {
@ -113,10 +113,17 @@ static int update_index_refresh(void)
return run_command_v_opt(argv_update_index, RUN_GIT_CMD); return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
} }
struct update_cb_data {
int index_fd;
struct lock_file *lock;
int exit_code;
};
static void update_index_from_diff(struct diff_queue_struct *q, static void update_index_from_diff(struct diff_queue_struct *q,
struct diff_options *opt, void *data) struct diff_options *opt, void *data)
{ {
int i; int i;
struct update_cb_data *cb = data;
/* do_diff_cache() mangled the index */ /* do_diff_cache() mangled the index */
discard_cache(); discard_cache();
@ -133,29 +140,34 @@ static void update_index_from_diff(struct diff_queue_struct *q,
} else } else
remove_file_from_cache(one->path); remove_file_from_cache(one->path);
} }
cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) ||
close(cb->index_fd) ||
commit_locked_index(cb->lock);
} }
static int read_from_tree(const char *prefix, const char **argv, static int read_from_tree(const char *prefix, const char **argv,
unsigned char *tree_sha1) unsigned char *tree_sha1)
{ {
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
struct diff_options opt; struct diff_options opt;
struct update_cb_data cb;
memset(&opt, 0, sizeof(opt)); memset(&opt, 0, sizeof(opt));
diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
opt.output_format = DIFF_FORMAT_CALLBACK; opt.output_format = DIFF_FORMAT_CALLBACK;
opt.format_callback = update_index_from_diff; opt.format_callback = update_index_from_diff;
opt.format_callback_data = &cb;
index_fd = hold_locked_index(lock, 1); cb.lock = xcalloc(1, sizeof(struct lock_file));
cb.index_fd = hold_locked_index(cb.lock, 1);
cb.exit_code = 0;
read_cache(); read_cache();
if (do_diff_cache(tree_sha1, &opt)) if (do_diff_cache(tree_sha1, &opt))
return 1; return 1;
diffcore_std(&opt); diffcore_std(&opt);
diff_flush(&opt); diff_flush(&opt);
return write_cache(index_fd, active_cache, active_nr) ||
close(index_fd) || return cb.exit_code;
commit_locked_index(lock);
} }
static void prepend_reflog_action(const char *action, char *buf, size_t size) static void prepend_reflog_action(const char *action, char *buf, size_t size)
@ -173,7 +185,7 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
int cmd_reset(int argc, const char **argv, const char *prefix) int cmd_reset(int argc, const char **argv, const char *prefix)
{ {
int i = 1, reset_type = NONE, update_ref_status = 0; int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0;
const char *rev = "HEAD"; const char *rev = "HEAD";
unsigned char sha1[20], *orig = NULL, sha1_orig[20], unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20]; *old_orig = NULL, sha1_old_orig[20];
@ -185,7 +197,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
reflog_action = args_to_str(argv); reflog_action = args_to_str(argv);
setenv("GIT_REFLOG_ACTION", reflog_action, 0); setenv("GIT_REFLOG_ACTION", reflog_action, 0);
if (i < argc) { while (i < argc) {
if (!strcmp(argv[i], "--mixed")) { if (!strcmp(argv[i], "--mixed")) {
reset_type = MIXED; reset_type = MIXED;
i++; i++;
@ -198,6 +210,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
reset_type = HARD; reset_type = HARD;
i++; i++;
} }
else if (!strcmp(argv[i], "-q")) {
quiet = 1;
i++;
}
else
break;
} }
if (i < argc && argv[i][0] != '-') if (i < argc && argv[i][0] != '-')
@ -258,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
switch (reset_type) { switch (reset_type) {
case HARD: case HARD:
if (!update_ref_status) if (!update_ref_status && !quiet)
print_new_head_line(commit); print_new_head_line(commit);
break; break;
case SOFT: /* Nothing else to do. */ case SOFT: /* Nothing else to do. */

View File

@ -86,7 +86,8 @@ static void show_commit(struct commit *commit)
struct strbuf buf; struct strbuf buf;
strbuf_init(&buf, 0); strbuf_init(&buf, 0);
pretty_print_commit(revs.commit_format, commit, pretty_print_commit(revs.commit_format, commit,
&buf, revs.abbrev, NULL, NULL, revs.date_mode); &buf, revs.abbrev, NULL, NULL,
revs.date_mode, 0);
if (buf.len) if (buf.len)
printf("%s%c", buf.buf, hdr_termination); printf("%s%c", buf.buf, hdr_termination);
strbuf_release(&buf); strbuf_release(&buf);

View File

@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = {
NULL NULL
}; };
static int edit, no_replay, no_commit, needed_deref; static int edit, no_replay, no_commit, needed_deref, mainline;
static enum { REVERT, CHERRY_PICK } action; static enum { REVERT, CHERRY_PICK } action;
static struct commit *commit; static struct commit *commit;
@ -50,12 +50,14 @@ static void parse_args(int argc, const char **argv)
OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"), OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"), OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
OPT_END(), OPT_END(),
}; };
if (parse_options(argc, argv, options, usage_str, 0) != 1) if (parse_options(argc, argv, options, usage_str, 0) != 1)
usage_with_options(usage_str, options); usage_with_options(usage_str, options);
arg = argv[0]; arg = argv[0];
if (get_sha1(arg, sha1)) if (get_sha1(arg, sha1))
die ("Cannot find '%s'", arg); die ("Cannot find '%s'", arg);
commit = (struct commit *)parse_object(sha1); commit = (struct commit *)parse_object(sha1);
@ -226,7 +228,7 @@ static int merge_recursive(const char *base_sha1,
static int revert_or_cherry_pick(int argc, const char **argv) static int revert_or_cherry_pick(int argc, const char **argv)
{ {
unsigned char head[20]; unsigned char head[20];
struct commit *base, *next; struct commit *base, *next, *parent;
int i; int i;
char *oneline, *reencoded_message = NULL; char *oneline, *reencoded_message = NULL;
const char *message, *encoding; const char *message, *encoding;
@ -261,8 +263,29 @@ static int revert_or_cherry_pick(int argc, const char **argv)
if (!commit->parents) if (!commit->parents)
die ("Cannot %s a root commit", me); die ("Cannot %s a root commit", me);
if (commit->parents->next) if (commit->parents->next) {
die ("Cannot %s a multi-parent commit.", me); /* Reverting or cherry-picking a merge commit */
int cnt;
struct commit_list *p;
if (!mainline)
die("Commit %s is a merge but no -m option was given.",
sha1_to_hex(commit->object.sha1));
for (cnt = 1, p = commit->parents;
cnt != mainline && p;
cnt++)
p = p->next;
if (cnt != mainline || !p)
die("Commit %s does not have parent %d",
sha1_to_hex(commit->object.sha1), mainline);
parent = p->item;
} else if (0 < mainline)
die("Mainline was specified but commit %s is not a merge.",
sha1_to_hex(commit->object.sha1));
else
parent = commit->parents->item;
if (!(message = commit->buffer)) if (!(message = commit->buffer))
die ("Cannot get commit message for %s", die ("Cannot get commit message for %s",
sha1_to_hex(commit->object.sha1)); sha1_to_hex(commit->object.sha1));
@ -291,14 +314,14 @@ static int revert_or_cherry_pick(int argc, const char **argv)
char *oneline_body = strchr(oneline, ' '); char *oneline_body = strchr(oneline, ' ');
base = commit; base = commit;
next = commit->parents->item; next = parent;
add_to_msg("Revert \""); add_to_msg("Revert \"");
add_to_msg(oneline_body + 1); add_to_msg(oneline_body + 1);
add_to_msg("\"\n\nThis reverts commit "); add_to_msg("\"\n\nThis reverts commit ");
add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(sha1_to_hex(commit->object.sha1));
add_to_msg(".\n"); add_to_msg(".\n");
} else { } else {
base = commit->parents->item; base = parent;
next = commit; next = commit;
set_author_ident_env(message); set_author_ident_env(message);
add_message_to_msg(message); add_message_to_msg(message);

View File

@ -155,6 +155,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!argc) if (!argc)
usage_with_options(builtin_rm_usage, builtin_rm_options); usage_with_options(builtin_rm_usage, builtin_rm_options);
if (!index_only)
setup_work_tree();
pathspec = get_pathspec(prefix, argv); pathspec = get_pathspec(prefix, argv);
seen = NULL; seen = NULL;
for (i = 0; pathspec[i] ; i++) for (i = 0; pathspec[i] ; i++)

View File

@ -266,7 +266,7 @@ static void show_one_commit(struct commit *commit, int no_name)
strbuf_init(&pretty, 0); strbuf_init(&pretty, 0);
if (commit->object.parsed) { if (commit->object.parsed) {
pretty_print_commit(CMIT_FMT_ONELINE, commit, pretty_print_commit(CMIT_FMT_ONELINE, commit,
&pretty, 0, NULL, NULL, 0); &pretty, 0, NULL, NULL, 0, 0);
pretty_str = pretty.buf; pretty_str = pretty.buf;
} }
if (!prefixcmp(pretty_str, "[PATCH] ")) if (!prefixcmp(pretty_str, "[PATCH] "))

View File

@ -81,17 +81,16 @@ static int show_reference(const char *refname, const unsigned char *sha1,
} }
printf("%-15s ", refname); printf("%-15s ", refname);
sp = buf = read_sha1_file(sha1, &type, &size); buf = read_sha1_file(sha1, &type, &size);
if (!buf) if (!buf || !size)
return 0; return 0;
if (!size) {
/* skip header */
sp = strstr(buf, "\n\n");
if (!sp) {
free(buf); free(buf);
return 0; return 0;
} }
/* skip header */
while (sp + 1 < buf + size &&
!(sp[0] == '\n' && sp[1] == '\n'))
sp++;
/* only take up to "lines" lines, and strip the signature */ /* only take up to "lines" lines, and strip the signature */
for (i = 0, sp += 2; for (i = 0, sp += 2;
i < filter->lines && sp < buf + size && i < filter->lines && sp < buf + size &&

View File

@ -23,7 +23,8 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name,
} }
/* returns an fd */ /* returns an fd */
int read_bundle_header(const char *path, struct bundle_header *header) { int read_bundle_header(const char *path, struct bundle_header *header)
{
char buffer[1024]; char buffer[1024];
int fd; int fd;
long fpos; long fpos;

View File

@ -7,7 +7,7 @@
#include SHA1_HEADER #include SHA1_HEADER
#include <zlib.h> #include <zlib.h>
#if ZLIB_VERNUM < 0x1200 #if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
#define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11) #define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
#endif #endif
@ -222,6 +222,7 @@ extern const char *get_git_work_tree(void);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
extern const char **get_pathspec(const char *prefix, const char **pathspec); extern const char **get_pathspec(const char *prefix, const char **pathspec);
extern void setup_work_tree(void);
extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void); extern const char *setup_git_directory(void);
extern const char *prefix_path(const char *prefix, int len, const char *path); extern const char *prefix_path(const char *prefix, int len, const char *path);

719
commit.c
View File

@ -3,7 +3,6 @@
#include "commit.h" #include "commit.h"
#include "pkt-line.h" #include "pkt-line.h"
#include "utf8.h" #include "utf8.h"
#include "interpolate.h"
#include "diff.h" #include "diff.h"
#include "revision.h" #include "revision.h"
@ -27,46 +26,6 @@ struct sort_node
const char *commit_type = "commit"; const char *commit_type = "commit";
static struct cmt_fmt_map {
const char *n;
size_t cmp_len;
enum cmit_fmt v;
} cmt_fmts[] = {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
{ "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
{ "format:", 7, CMIT_FMT_USERFORMAT},
};
static char *user_format;
enum cmit_fmt get_commit_format(const char *arg)
{
int i;
if (!arg || !*arg)
return CMIT_FMT_DEFAULT;
if (*arg == '=')
arg++;
if (!prefixcmp(arg, "format:")) {
if (user_format)
free(user_format);
user_format = xstrdup(arg + 7);
return CMIT_FMT_USERFORMAT;
}
for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
!strncmp(arg, cmt_fmts[i].n, strlen(arg)))
return cmt_fmts[i].v;
}
die("invalid --pretty format: %s", arg);
}
static struct commit *check_commit(struct object *obj, static struct commit *check_commit(struct object *obj,
const unsigned char *sha1, const unsigned char *sha1,
int quiet) int quiet)
@ -460,684 +419,6 @@ void clear_commit_marks(struct commit *commit, unsigned int mark)
} }
} }
/*
* Generic support for pretty-printing the header
*/
static int get_one_line(const char *msg)
{
int ret = 0;
for (;;) {
char c = *msg++;
if (!c)
break;
ret++;
if (c == '\n')
break;
}
return ret;
}
/* High bit set, or ISO-2022-INT */
static int non_ascii(int ch)
{
ch = (ch & 0xff);
return ((ch & 0x80) || (ch == 0x1b));
}
static int is_rfc2047_special(char ch)
{
return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
}
static void add_rfc2047(struct strbuf *sb, const char *line, int len,
const char *encoding)
{
int i, last;
for (i = 0; i < len; i++) {
int ch = line[i];
if (non_ascii(ch))
goto needquote;
if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
goto needquote;
}
strbuf_add(sb, line, len);
return;
needquote:
strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
strbuf_addf(sb, "=?%s?q?", encoding);
for (i = last = 0; i < len; i++) {
unsigned ch = line[i] & 0xFF;
/*
* We encode ' ' using '=20' even though rfc2047
* allows using '_' for readability. Unfortunately,
* many programs do not understand this and just
* leave the underscore in place.
*/
if (is_rfc2047_special(ch) || ch == ' ') {
strbuf_add(sb, line + last, i - last);
strbuf_addf(sb, "=%02X", ch);
last = i + 1;
}
}
strbuf_add(sb, line + last, len - last);
strbuf_addstr(sb, "?=");
}
static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
const char *line, enum date_mode dmode,
const char *encoding)
{
char *date;
int namelen;
unsigned long time;
int tz;
const char *filler = " ";
if (fmt == CMIT_FMT_ONELINE)
return;
date = strchr(line, '>');
if (!date)
return;
namelen = ++date - line;
time = strtoul(date, &date, 10);
tz = strtol(date, NULL, 10);
if (fmt == CMIT_FMT_EMAIL) {
char *name_tail = strchr(line, '<');
int display_name_length;
if (!name_tail)
return;
while (line < name_tail && isspace(name_tail[-1]))
name_tail--;
display_name_length = name_tail - line;
filler = "";
strbuf_addstr(sb, "From: ");
add_rfc2047(sb, line, display_name_length, encoding);
strbuf_add(sb, name_tail, namelen - display_name_length);
strbuf_addch(sb, '\n');
} else {
strbuf_addf(sb, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
}
switch (fmt) {
case CMIT_FMT_MEDIUM:
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode));
break;
case CMIT_FMT_EMAIL:
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
break;
case CMIT_FMT_FULLER:
strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
break;
default:
/* notin' */
break;
}
}
static int is_empty_line(const char *line, int *len_p)
{
int len = *len_p;
while (len && isspace(line[len-1]))
len--;
*len_p = len;
return !len;
}
static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
const struct commit *commit, int abbrev)
{
struct commit_list *parent = commit->parents;
if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
!parent || !parent->next)
return;
strbuf_addstr(sb, "Merge:");
while (parent) {
struct commit *p = parent->item;
const char *hex = NULL;
const char *dots;
if (abbrev)
hex = find_unique_abbrev(p->object.sha1, abbrev);
if (!hex)
hex = sha1_to_hex(p->object.sha1);
dots = (abbrev && strlen(hex) != 40) ? "..." : "";
parent = parent->next;
strbuf_addf(sb, " %s%s", hex, dots);
}
strbuf_addch(sb, '\n');
}
static char *get_header(const struct commit *commit, const char *key)
{
int key_len = strlen(key);
const char *line = commit->buffer;
for (;;) {
const char *eol = strchr(line, '\n'), *next;
if (line == eol)
return NULL;
if (!eol) {
eol = line + strlen(line);
next = NULL;
} else
next = eol + 1;
if (eol - line > key_len &&
!strncmp(line, key, key_len) &&
line[key_len] == ' ') {
return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
}
line = next;
}
}
static char *replace_encoding_header(char *buf, const char *encoding)
{
struct strbuf tmp;
size_t start, len;
char *cp = buf;
/* guess if there is an encoding header before a \n\n */
while (strncmp(cp, "encoding ", strlen("encoding "))) {
cp = strchr(cp, '\n');
if (!cp || *++cp == '\n')
return buf;
}
start = cp - buf;
cp = strchr(cp, '\n');
if (!cp)
return buf; /* should not happen but be defensive */
len = cp + 1 - (buf + start);
strbuf_init(&tmp, 0);
strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
if (is_encoding_utf8(encoding)) {
/* we have re-coded to UTF-8; drop the header */
strbuf_remove(&tmp, start, len);
} else {
/* just replaces XXXX in 'encoding XXXX\n' */
strbuf_splice(&tmp, start + strlen("encoding "),
len - strlen("encoding \n"),
encoding, strlen(encoding));
}
return strbuf_detach(&tmp, NULL);
}
static char *logmsg_reencode(const struct commit *commit,
const char *output_encoding)
{
static const char *utf8 = "utf-8";
const char *use_encoding;
char *encoding;
char *out;
if (!*output_encoding)
return NULL;
encoding = get_header(commit, "encoding");
use_encoding = encoding ? encoding : utf8;
if (!strcmp(use_encoding, output_encoding))
if (encoding) /* we'll strip encoding header later */
out = xstrdup(commit->buffer);
else
return NULL; /* nothing to do */
else
out = reencode_string(commit->buffer,
output_encoding, use_encoding);
if (out)
out = replace_encoding_header(out, output_encoding);
free(encoding);
return out;
}
static void fill_person(struct interp *table, const char *msg, int len)
{
int start, end, tz = 0;
unsigned long date;
char *ep;
/* parse name */
for (end = 0; end < len && msg[end] != '<'; end++)
; /* do nothing */
start = end + 1;
while (end > 0 && isspace(msg[end - 1]))
end--;
table[0].value = xmemdupz(msg, end);
if (start >= len)
return;
/* parse email */
for (end = start + 1; end < len && msg[end] != '>'; end++)
; /* do nothing */
if (end >= len)
return;
table[1].value = xmemdupz(msg + start, end - start);
/* parse date */
for (start = end + 1; start < len && isspace(msg[start]); start++)
; /* do nothing */
if (start >= len)
return;
date = strtoul(msg + start, &ep, 10);
if (msg + start == ep)
return;
table[5].value = xmemdupz(msg + start, ep - (msg + start));
/* parse tz */
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
; /* do nothing */
if (start + 1 < len) {
tz = strtoul(msg + start + 1, NULL, 10);
if (msg[start] == '-')
tz = -tz;
}
interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
}
void format_commit_message(const struct commit *commit,
const void *format, struct strbuf *sb)
{
struct interp table[] = {
{ "%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 */
};
enum interp_index {
IHASH = 0, IHASH_ABBREV,
ITREE, ITREE_ABBREV,
IPARENTS, IPARENTS_ABBREV,
IAUTHOR_NAME, IAUTHOR_EMAIL,
IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
ICOMMITTER_NAME, ICOMMITTER_EMAIL,
ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
ICOMMITTER_ISO8601,
IENCODING,
ISUBJECT,
IBODY,
IRED, IGREEN, IBLUE, IRESET_COLOR,
INEWLINE,
ILEFT_RIGHT,
};
struct commit_list *p;
char parents[1024];
unsigned long len;
int i;
enum { HEADER, SUBJECT, BODY } state;
const char *msg = commit->buffer;
if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
die("invalid interp table!");
/* these are independent of the commit */
interp_set_entry(table, IRED, "\033[31m");
interp_set_entry(table, IGREEN, "\033[32m");
interp_set_entry(table, IBLUE, "\033[34m");
interp_set_entry(table, IRESET_COLOR, "\033[m");
interp_set_entry(table, INEWLINE, "\n");
/* these depend on the commit */
if (!commit->object.parsed)
parse_object(commit->object.sha1);
interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
interp_set_entry(table, IHASH_ABBREV,
find_unique_abbrev(commit->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
interp_set_entry(table, ITREE_ABBREV,
find_unique_abbrev(commit->tree->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, ILEFT_RIGHT,
(commit->object.flags & BOUNDARY)
? "-"
: (commit->object.flags & SYMMETRIC_LEFT)
? "<"
: ">");
parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
sha1_to_hex(p->item->object.sha1));
interp_set_entry(table, IPARENTS, parents + 1);
parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
find_unique_abbrev(p->item->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
int eol;
for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
; /* do nothing */
if (state == SUBJECT) {
table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
i = eol;
}
if (i == eol) {
state++;
/* strip empty lines */
while (msg[eol + 1] == '\n')
eol++;
} else if (!prefixcmp(msg + i, "author "))
fill_person(table + IAUTHOR_NAME,
msg + i + 7, eol - i - 7);
else if (!prefixcmp(msg + i, "committer "))
fill_person(table + ICOMMITTER_NAME,
msg + i + 10, eol - i - 10);
else if (!prefixcmp(msg + i, "encoding "))
table[IENCODING].value =
xmemdupz(msg + i + 9, eol - i - 9);
i = eol;
}
if (msg[i])
table[IBODY].value = xstrdup(msg + i);
len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
format, table, ARRAY_SIZE(table));
if (len > strbuf_avail(sb)) {
strbuf_grow(sb, len);
interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
format, table, ARRAY_SIZE(table));
}
strbuf_setlen(sb, sb->len + len);
interp_clear_table(table, ARRAY_SIZE(table));
}
static void pp_header(enum cmit_fmt fmt,
int abbrev,
enum date_mode dmode,
const char *encoding,
const struct commit *commit,
const char **msg_p,
struct strbuf *sb)
{
int parents_shown = 0;
for (;;) {
const char *line = *msg_p;
int linelen = get_one_line(*msg_p);
if (!linelen)
return;
*msg_p += linelen;
if (linelen == 1)
/* End of header */
return;
if (fmt == CMIT_FMT_RAW) {
strbuf_add(sb, line, linelen);
continue;
}
if (!memcmp(line, "parent ", 7)) {
if (linelen != 48)
die("bad parent line in commit");
continue;
}
if (!parents_shown) {
struct commit_list *parent;
int num;
for (parent = commit->parents, num = 0;
parent;
parent = parent->next, num++)
;
/* with enough slop */
strbuf_grow(sb, num * 50 + 20);
add_merge_info(fmt, sb, commit, abbrev);
parents_shown = 1;
}
/*
* MEDIUM == DEFAULT shows only author with dates.
* FULL shows both authors but not dates.
* FULLER shows both authors and dates.
*/
if (!memcmp(line, "author ", 7)) {
strbuf_grow(sb, linelen + 80);
add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
}
if (!memcmp(line, "committer ", 10) &&
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
strbuf_grow(sb, linelen + 80);
add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
}
}
}
static void pp_title_line(enum cmit_fmt fmt,
const char **msg_p,
struct strbuf *sb,
const char *subject,
const char *after_subject,
const char *encoding,
int plain_non_ascii)
{
struct strbuf title;
strbuf_init(&title, 80);
for (;;) {
const char *line = *msg_p;
int linelen = get_one_line(line);
*msg_p += linelen;
if (!linelen || is_empty_line(line, &linelen))
break;
strbuf_grow(&title, linelen + 2);
if (title.len) {
if (fmt == CMIT_FMT_EMAIL) {
strbuf_addch(&title, '\n');
}
strbuf_addch(&title, ' ');
}
strbuf_add(&title, line, linelen);
}
strbuf_grow(sb, title.len + 1024);
if (subject) {
strbuf_addstr(sb, subject);
add_rfc2047(sb, title.buf, title.len, encoding);
} else {
strbuf_addbuf(sb, &title);
}
strbuf_addch(sb, '\n');
if (plain_non_ascii) {
const char *header_fmt =
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=%s\n"
"Content-Transfer-Encoding: 8bit\n";
strbuf_addf(sb, header_fmt, encoding);
}
if (after_subject) {
strbuf_addstr(sb, after_subject);
}
if (fmt == CMIT_FMT_EMAIL) {
strbuf_addch(sb, '\n');
}
strbuf_release(&title);
}
static void pp_remainder(enum cmit_fmt fmt,
const char **msg_p,
struct strbuf *sb,
int indent)
{
int first = 1;
for (;;) {
const char *line = *msg_p;
int linelen = get_one_line(line);
*msg_p += linelen;
if (!linelen)
break;
if (is_empty_line(line, &linelen)) {
if (first)
continue;
if (fmt == CMIT_FMT_SHORT)
break;
}
first = 0;
strbuf_grow(sb, linelen + indent + 20);
if (indent) {
memset(sb->buf + sb->len, ' ', indent);
strbuf_setlen(sb, sb->len + indent);
}
strbuf_add(sb, line, linelen);
strbuf_addch(sb, '\n');
}
}
void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
struct strbuf *sb, int abbrev,
const char *subject, const char *after_subject,
enum date_mode dmode)
{
unsigned long beginning_of_body;
int indent = 4;
const char *msg = commit->buffer;
int plain_non_ascii = 0;
char *reencoded;
const char *encoding;
if (fmt == CMIT_FMT_USERFORMAT) {
format_commit_message(commit, user_format, sb);
return;
}
encoding = (git_log_output_encoding
? git_log_output_encoding
: git_commit_encoding);
if (!encoding)
encoding = "utf-8";
reencoded = logmsg_reencode(commit, encoding);
if (reencoded) {
msg = reencoded;
}
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
indent = 0;
/* After-subject is used to pass in Content-Type: multipart
* MIME header; in that case we do not have to do the
* plaintext content type even if the commit message has
* non 7-bit ASCII character. Otherwise, check if we need
* to say this is not a 7-bit ASCII.
*/
if (fmt == CMIT_FMT_EMAIL && !after_subject) {
int i, ch, in_body;
for (in_body = i = 0; (ch = msg[i]); i++) {
if (!in_body) {
/* author could be non 7-bit ASCII but
* the log may be so; skip over the
* header part first.
*/
if (ch == '\n' && msg[i+1] == '\n')
in_body = 1;
}
else if (non_ascii(ch)) {
plain_non_ascii = 1;
break;
}
}
}
pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
if (fmt != CMIT_FMT_ONELINE && !subject) {
strbuf_addch(sb, '\n');
}
/* Skip excess blank lines at the beginning of body, if any... */
for (;;) {
int linelen = get_one_line(msg);
int ll = linelen;
if (!linelen)
break;
if (!is_empty_line(msg, &ll))
break;
msg += linelen;
}
/* These formats treat the title line specially. */
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
pp_title_line(fmt, &msg, sb, subject,
after_subject, encoding, plain_non_ascii);
beginning_of_body = sb->len;
if (fmt != CMIT_FMT_ONELINE)
pp_remainder(fmt, &msg, sb, indent);
strbuf_rtrim(sb);
/* Make sure there is an EOLN for the non-oneline case */
if (fmt != CMIT_FMT_ONELINE)
strbuf_addch(sb, '\n');
/*
* The caller may append additional body text in e-mail
* format. Make sure we did not strip the blank line
* between the header and the body.
*/
if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
strbuf_addch(sb, '\n');
free(reencoded);
}
struct commit *pop_commit(struct commit_list **stack) struct commit *pop_commit(struct commit_list **stack)
{ {
struct commit_list *top = *stack; struct commit_list *top = *stack;

View File

@ -61,13 +61,15 @@ enum cmit_fmt {
CMIT_FMT_UNSPECIFIED, CMIT_FMT_UNSPECIFIED,
}; };
extern int non_ascii(int);
extern enum cmit_fmt get_commit_format(const char *arg); extern enum cmit_fmt get_commit_format(const char *arg);
extern void format_commit_message(const struct commit *commit, extern void format_commit_message(const struct commit *commit,
const void *format, struct strbuf *sb); const void *format, struct strbuf *sb);
extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
struct strbuf *, struct strbuf *,
int abbrev, const char *subject, int abbrev, const char *subject,
const char *after_subject, enum date_mode); const char *after_subject, enum date_mode,
int non_ascii_present);
/** Removes the first commit from a list sorted by date, and adds all /** Removes the first commit from a list sorted by date, and adds all
* of its parents. * of its parents.

View File

@ -18,7 +18,6 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <stdio.h> #include <stdio.h>

View File

@ -18,7 +18,6 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <stdio.h> #include <stdio.h>

View File

@ -38,3 +38,4 @@ NO_STRCASESTR=@NO_STRCASESTR@
NO_STRLCPY=@NO_STRLCPY@ NO_STRLCPY=@NO_STRLCPY@
NO_SETENV=@NO_SETENV@ NO_SETENV=@NO_SETENV@
NO_ICONV=@NO_ICONV@ NO_ICONV=@NO_ICONV@
NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@

View File

@ -73,7 +73,7 @@ fi \
AC_ARG_WITH([lib], AC_ARG_WITH([lib],
[AS_HELP_STRING([--with-lib=ARG], [AS_HELP_STRING([--with-lib=ARG],
[ARG specifies alternative name for lib directory])], [ARG specifies alternative name for lib directory])],
[if test "$withval" = "no" -o "$withval" = "yes"; then \ [if test "$withval" = "no" || test "$withval" = "yes"; then \
AC_MSG_WARN([You should provide name for --with-lib=ARG]); \ AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
else \ else \
GIT_CONF_APPEND_LINE(lib=$withval); \ GIT_CONF_APPEND_LINE(lib=$withval); \
@ -182,6 +182,26 @@ AC_SUBST(NEEDS_LIBICONV)
AC_SUBST(NO_ICONV) AC_SUBST(NO_ICONV)
test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv" test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv"
# #
# Define NO_DEFLATE_BOUND if deflateBound is missing from zlib.
AC_DEFUN([ZLIBTEST_SRC], [
#include <zlib.h>
int main(void)
{
deflateBound(0, 0);
return 0;
}
])
AC_MSG_CHECKING([for deflateBound in -lz])
old_LIBS="$LIBS"
LIBS="$LIBS -lz"
AC_LINK_IFELSE(ZLIBTEST_SRC,
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
NO_DEFLATE_BOUND=yes])
LIBS="$old_LIBS"
AC_SUBST(NO_DEFLATE_BOUND)
#
# Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
# Patrick Mauritz). # Patrick Mauritz).
AC_CHECK_LIB([c], [socket], AC_CHECK_LIB([c], [socket],
@ -245,9 +265,9 @@ AC_RUN_IFELSE(
[AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
[[char buf[64]; [[char buf[64];
if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5) if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5)
exit(1); return 1;
else if (strcmp(buf, "12345")) else if (strcmp(buf, "12345"))
exit(2);]])], return 2;]])],
[ac_cv_c_c99_format=yes], [ac_cv_c_c99_format=yes],
[ac_cv_c_c99_format=no]) [ac_cv_c_c99_format=no])
]) ])

View File

@ -71,6 +71,79 @@ def isP4Exec(kind):
a plus sign, it is also executable""" a plus sign, it is also executable"""
return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) return (re.search(r"(^[cku]?x)|\+.*x", kind) != None)
def setP4ExecBit(file, mode):
# Reopens an already open file and changes the execute bit to match
# the execute bit setting in the passed in mode.
p4Type = "+x"
if not isModeExec(mode):
p4Type = getP4OpenedType(file)
p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
if p4Type[-1] == "+":
p4Type = p4Type[0:-1]
system("p4 reopen -t %s %s" % (p4Type, file))
def getP4OpenedType(file):
# Returns the perforce file type for the given file.
result = read_pipe("p4 opened %s" % file)
match = re.match(".*\((.+)\)$", result)
if match:
return match.group(1)
else:
die("Could not determine file type for %s" % file)
def diffTreePattern():
# This is a simple generator for the diff tree regex pattern. This could be
# a class variable if this and parseDiffTreeEntry were a part of a class.
pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
while True:
yield pattern
def parseDiffTreeEntry(entry):
"""Parses a single diff tree entry into its component elements.
See git-diff-tree(1) manpage for details about the format of the diff
output. This method returns a dictionary with the following elements:
src_mode - The mode of the source file
dst_mode - The mode of the destination file
src_sha1 - The sha1 for the source file
dst_sha1 - The sha1 fr the destination file
status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)
status_score - The score for the status (applicable for 'C' and 'R'
statuses). This is None if there is no score.
src - The path for the source file.
dst - The path for the destination file. This is only present for
copy or renames. If it is not present, this is None.
If the pattern is not matched, None is returned."""
match = diffTreePattern().next().match(entry)
if match:
return {
'src_mode': match.group(1),
'dst_mode': match.group(2),
'src_sha1': match.group(3),
'dst_sha1': match.group(4),
'status': match.group(5),
'status_score': match.group(6),
'src': match.group(7),
'dst': match.group(10)
}
return None
def isModeExec(mode):
# Returns True if the given git mode represents an executable file,
# otherwise False.
return mode[-3:] == "755"
def isModeExecChanged(src_mode, dst_mode):
return isModeExec(src_mode) != isModeExec(dst_mode)
def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
cmd = "p4 -G %s" % cmd cmd = "p4 -G %s" % cmd
if verbose: if verbose:
@ -494,18 +567,23 @@ class P4Submit(Command):
else: else:
print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
diffOpts = ("", "-M")[self.detectRename] diffOpts = ("", "-M")[self.detectRename]
diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
filesToAdd = set() filesToAdd = set()
filesToDelete = set() filesToDelete = set()
editedFiles = set() editedFiles = set()
filesToChangeExecBit = {}
for line in diff: for line in diff:
modifier = line[0] diff = parseDiffTreeEntry(line)
path = line[1:].strip() modifier = diff['status']
path = diff['src']
if modifier == "M": if modifier == "M":
system("p4 edit \"%s\"" % path) system("p4 edit \"%s\"" % path)
if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
filesToChangeExecBit[path] = diff['dst_mode']
editedFiles.add(path) editedFiles.add(path)
elif modifier == "A": elif modifier == "A":
filesToAdd.add(path) filesToAdd.add(path)
filesToChangeExecBit[path] = diff['dst_mode']
if path in filesToDelete: if path in filesToDelete:
filesToDelete.remove(path) filesToDelete.remove(path)
elif modifier == "D": elif modifier == "D":
@ -513,9 +591,11 @@ class P4Submit(Command):
if path in filesToAdd: if path in filesToAdd:
filesToAdd.remove(path) filesToAdd.remove(path)
elif modifier == "R": elif modifier == "R":
src, dest = line.strip().split("\t")[1:3] src, dest = diff['src'], diff['dst']
system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest))
system("p4 edit \"%s\"" % (dest)) system("p4 edit \"%s\"" % (dest))
if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
filesToChangeExecBit[dest] = diff['dst_mode']
os.unlink(dest) os.unlink(dest)
editedFiles.add(dest) editedFiles.add(dest)
filesToDelete.add(src) filesToDelete.add(src)
@ -568,6 +648,11 @@ class P4Submit(Command):
system("p4 revert \"%s\"" % f) system("p4 revert \"%s\"" % f)
system("p4 delete \"%s\"" % f) system("p4 delete \"%s\"" % f)
# Set/clear executable bits
for f in filesToChangeExecBit.keys():
mode = filesToChangeExecBit[f]
setP4ExecBit(f, mode)
logMessage = "" logMessage = ""
if not self.directSubmit: if not self.directSubmit:
logMessage = extractLogMessageFromGitCommit(id) logMessage = extractLogMessageFromGitCommit(id)

View File

@ -2,24 +2,26 @@
# #
# Copyright (c) 2007 Andy Parkins # Copyright (c) 2007 Andy Parkins
# #
# An example hook script to mail out commit update information. This hook sends emails # An example hook script to mail out commit update information. This hook
# listing new revisions to the repository introduced by the change being reported. The # sends emails listing new revisions to the repository introduced by the
# rule is that (for branch updates) each commit will appear on one email and one email # change being reported. The rule is that (for branch updates) each commit
# only. # will appear on one email and one email only.
# #
# This hook is stored in the contrib/hooks directory. Your distribution will have put # This hook is stored in the contrib/hooks directory. Your distribution
# this somewhere standard. You should make this script executable then link to it in # will have put this somewhere standard. You should make this script
# the repository you would like to use it in. For example, on debian the hook is stored # executable then link to it in the repository you would like to use it in.
# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: # For example, on debian the hook is stored in
# /usr/share/doc/git-core/contrib/hooks/post-receive-email:
# #
# chmod a+x post-receive-email # chmod a+x post-receive-email
# cd /path/to/your/repository.git # cd /path/to/your/repository.git
# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
# #
# This hook script assumes it is enabled on the central repository of a project, with # This hook script assumes it is enabled on the central repository of a
# all users pushing only to it and not between each other. It will still work if you # project, with all users pushing only to it and not between each other. It
# don't operate in that style, but it would become possible for the email to be from # will still work if you don't operate in that style, but it would become
# someone other than the person doing the push. # possible for the email to be from someone other than the person doing the
# push.
# #
# Config # Config
# ------ # ------
@ -28,15 +30,17 @@
# emails for every ref update. # emails for every ref update.
# hooks.announcelist # hooks.announcelist
# This is the list that all pushes of annotated tags will go to. Leave it # This is the list that all pushes of annotated tags will go to. Leave it
# blank to default to the mailinglist field. The announce emails lists the # blank to default to the mailinglist field. The announce emails lists
# short log summary of the changes since the last annotated tag. # the short log summary of the changes since the last annotated tag.
# hook.envelopesender # hooks.envelopesender
# If set then the -f option is passed to sendmail to allow the envelope sender # If set then the -f option is passed to sendmail to allow the envelope
# address to be set # sender address to be set
# hooks.emailprefix
# All emails have their subjects prefixed with this prefix, or "[SCM]"
# if emailprefix is unset, to aid filtering
# #
# Notes # Notes
# ----- # -----
# All emails have their subjects prefixed with "[SCM]" to aid filtering.
# All emails include the headers "X-Git-Refname", "X-Git-Oldrev", # All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
# give information for debugging. # give information for debugging.
@ -49,8 +53,8 @@
# this is and calls the appropriate body-generation routine after outputting # this is and calls the appropriate body-generation routine after outputting
# the common header # the common header
# #
# Note this function doesn't actually generate any email output, that is taken # Note this function doesn't actually generate any email output, that is
# care of by the functions it calls: # taken care of by the functions it calls:
# - generate_email_header # - generate_email_header
# - generate_create_XXXX_email # - generate_create_XXXX_email
# - generate_update_XXXX_email # - generate_update_XXXX_email
@ -152,10 +156,6 @@ generate_email()
fi fi
# Email parameters # Email parameters
# The committer will be obtained from the latest existing rev; so
# for a deletion it will be the oldrev, for the others, then newrev
committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" |
sed -ne 's/\(.*\) </"\1" </p')
# The email subject will contain the best description of the ref # The email subject will contain the best description of the ref
# that we can build from the parameters # that we can build from the parameters
describe=$(git describe $rev 2>/dev/null) describe=$(git describe $rev 2>/dev/null)
@ -186,7 +186,7 @@ generate_email_header()
# Generate header # Generate header
cat <<-EOF cat <<-EOF
To: $recipients To: $recipients
Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
X-Git-Refname: $refname X-Git-Refname: $refname
X-Git-Reftype: $refname_type X-Git-Reftype: $refname_type
X-Git-Oldrev: $oldrev X-Git-Oldrev: $oldrev
@ -225,8 +225,9 @@ generate_create_branch_email()
echo $LOGBEGIN echo $LOGBEGIN
# This shows all log entries that are not already covered by # This shows all log entries that are not already covered by
# another ref - i.e. commits that are now accessible from this # another ref - i.e. commits that are now accessible from this
# ref that were previously not accessible (see generate_update_branch_email # ref that were previously not accessible
# for the explanation of this command) # (see generate_update_branch_email for the explanation of this
# command)
git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $newrev git rev-list --pretty --stdin $newrev
echo $LOGEND echo $LOGEND
@ -254,9 +255,10 @@ generate_update_branch_email()
# #
# git-rev-list N ^O ^X ^N # git-rev-list N ^O ^X ^N
# #
# So, we need to build up the list more carefully. git-rev-parse will # So, we need to build up the list more carefully. git-rev-parse
# generate a list of revs that may be fed into git-rev-list. We can get # will generate a list of revs that may be fed into git-rev-list.
# it to make the "--not --all" part and then filter out the "^N" with: # We can get it to make the "--not --all" part and then filter out
# the "^N" with:
# #
# git-rev-parse --not --all | grep -v N # git-rev-parse --not --all | grep -v N
# #
@ -266,16 +268,17 @@ generate_update_branch_email()
# git-rev-list N ^O ^X # git-rev-list N ^O ^X
# #
# This leaves a problem when someone else updates the repository # This leaves a problem when someone else updates the repository
# while this script is running. Their new value of the ref we're working # while this script is running. Their new value of the ref we're
# on would be included in the "--not --all" output; and as our $newrev # working on would be included in the "--not --all" output; and as
# would be an ancestor of that commit, it would exclude all of our # our $newrev would be an ancestor of that commit, it would exclude
# commits. What we really want is to exclude the current value of # all of our commits. What we really want is to exclude the current
# $refname from the --not list, rather than N itself. So: # value of $refname from the --not list, rather than N itself. So:
# #
# git-rev-parse --not --all | grep -v $(git-rev-parse $refname) # git-rev-parse --not --all | grep -v $(git-rev-parse $refname)
# #
# Get's us to something pretty safe (apart from the small time between # Get's us to something pretty safe (apart from the small time
# refname being read, and git-rev-parse running - for that, I give up) # between refname being read, and git-rev-parse running - for that,
# I give up)
# #
# #
# Next problem, consider this: # Next problem, consider this:
@ -283,18 +286,18 @@ generate_update_branch_email()
# \ # \
# * --- X --- * --- N ($newrev) # * --- X --- * --- N ($newrev)
# #
# That is to say, there is no guarantee that oldrev is a strict subset of # That is to say, there is no guarantee that oldrev is a strict
# newrev (it would have required a --force, but that's allowed). So, we # subset of newrev (it would have required a --force, but that's
# can't simply say rev-list $oldrev..$newrev. Instead we find the common # allowed). So, we can't simply say rev-list $oldrev..$newrev.
# base of the two revs and list from there. # Instead we find the common base of the two revs and list from
# there.
# #
# As above, we need to take into account the presence of X; if another # As above, we need to take into account the presence of X; if
# branch is already in the repository and points at some of the revisions # another branch is already in the repository and points at some of
# that we are about to output - we don't want them. The solution is as # the revisions that we are about to output - we don't want them.
# before: git-rev-parse output filtered. # The solution is as before: git-rev-parse output filtered.
# #
# Finally, tags: # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N
# 1 --- 2 --- O --- T --- 3 --- 4 --- N
# #
# Tags pushed into the repository generate nice shortlog emails that # Tags pushed into the repository generate nice shortlog emails that
# summarise the commits between them and the previous tag. However, # summarise the commits between them and the previous tag. However,
@ -302,13 +305,14 @@ generate_update_branch_email()
# for a branch update. Therefore we still want to output revisions # for a branch update. Therefore we still want to output revisions
# that have been output on a tag email. # that have been output on a tag email.
# #
# Luckily, git-rev-parse includes just the tool. Instead of using "--all" # Luckily, git-rev-parse includes just the tool. Instead of using
# we use "--branches"; this has the added benefit that "remotes/" will # "--all" we use "--branches"; this has the added benefit that
# be ignored as well. # "remotes/" will be ignored as well.
# List all of the revisions that were removed by this update, in a fast forward # List all of the revisions that were removed by this update, in a
# update, this list will be empty, because rev-list O ^N is empty. For a non # fast forward update, this list will be empty, because rev-list O
# fast forward, O ^N is the list of removed revisions # ^N is empty. For a non fast forward, O ^N is the list of removed
# revisions
fast_forward="" fast_forward=""
rev="" rev=""
for rev in $(git rev-list $newrev..$oldrev) for rev in $(git rev-list $newrev..$oldrev)
@ -321,10 +325,10 @@ generate_update_branch_email()
fi fi
# List all the revisions from baserev to newrev in a kind of # List all the revisions from baserev to newrev in a kind of
# "table-of-contents"; note this list can include revisions that have # "table-of-contents"; note this list can include revisions that
# already had notification emails and is present to show the full detail # have already had notification emails and is present to show the
# of the change from rolling back the old revision to the base revision and # full detail of the change from rolling back the old revision to
# then forward to the new revision # the base revision and then forward to the new revision
for rev in $(git rev-list $oldrev..$newrev) for rev in $(git rev-list $oldrev..$newrev)
do do
revtype=$(git cat-file -t "$rev") revtype=$(git cat-file -t "$rev")
@ -334,19 +338,20 @@ generate_update_branch_email()
if [ "$fast_forward" ]; then if [ "$fast_forward" ]; then
echo " from $oldrev ($oldrev_type)" echo " from $oldrev ($oldrev_type)"
else else
# 1. Existing revisions were removed. In this case newrev is a # 1. Existing revisions were removed. In this case newrev
# subset of oldrev - this is the reverse of a fast-forward, # is a subset of oldrev - this is the reverse of a
# a rewind # fast-forward, a rewind
# 2. New revisions were added on top of an old revision, this is # 2. New revisions were added on top of an old revision,
# a rewind and addition. # this is a rewind and addition.
# (1) certainly happened, (2) possibly. When (2) hasn't happened, # (1) certainly happened, (2) possibly. When (2) hasn't
# we set a flag to indicate that no log printout is required. # happened, we set a flag to indicate that no log printout
# is required.
echo "" echo ""
# Find the common ancestor of the old and new revisions and compare # Find the common ancestor of the old and new revisions and
# it with newrev # compare it with newrev
baserev=$(git merge-base $oldrev $newrev) baserev=$(git merge-base $oldrev $newrev)
rewind_only="" rewind_only=""
if [ "$baserev" = "$newrev" ]; then if [ "$baserev" = "$newrev" ]; then
@ -387,21 +392,22 @@ generate_update_branch_email()
git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $oldrev..$newrev git rev-list --pretty --stdin $oldrev..$newrev
# XXX: Need a way of detecting whether git rev-list actually outputted # XXX: Need a way of detecting whether git rev-list actually
# anything, so that we can issue a "no new revisions added by this # outputted anything, so that we can issue a "no new
# update" message # revisions added by this update" message
echo $LOGEND echo $LOGEND
else else
echo "No new revisions were added by this update." echo "No new revisions were added by this update."
fi fi
# The diffstat is shown from the old revision to the new revision. This # The diffstat is shown from the old revision to the new revision.
# is to show the truth of what happened in this change. There's no point # This is to show the truth of what happened in this change.
# showing the stat from the base to the new revision because the base # There's no point showing the stat from the base to the new
# is effectively a random revision at this point - the user will be # revision because the base is effectively a random revision at this
# interested in what this revision changed - including the undoing of # point - the user will be interested in what this revision changed
# previous revisions in the case of non-fast forward updates. # - including the undoing of previous revisions in the case of
# non-fast forward updates.
echo "" echo ""
echo "Summary of changes:" echo "Summary of changes:"
git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
@ -448,7 +454,8 @@ generate_update_atag_email()
# #
generate_atag_email() generate_atag_email()
{ {
# Use git-for-each-ref to pull out the individual fields from the tag # Use git-for-each-ref to pull out the individual fields from the
# tag
eval $(git for-each-ref --shell --format=' eval $(git for-each-ref --shell --format='
tagobject=%(*objectname) tagobject=%(*objectname)
tagtype=%(*objecttype) tagtype=%(*objecttype)
@ -459,8 +466,10 @@ generate_atag_email()
echo " tagging $tagobject ($tagtype)" echo " tagging $tagobject ($tagtype)"
case "$tagtype" in case "$tagtype" in
commit) commit)
# If the tagged object is a commit, then we assume this is a # If the tagged object is a commit, then we assume this is a
# release, and so we calculate which tag this tag is replacing # release, and so we calculate which tag this tag is
# replacing
prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
if [ -n "$prevtag" ]; then if [ -n "$prevtag" ]; then
@ -477,25 +486,27 @@ generate_atag_email()
echo "" echo ""
echo $LOGBEGIN echo $LOGBEGIN
# Show the content of the tag message; this might contain a change log # Show the content of the tag message; this might contain a change
# or release notes so is worth displaying. # log or release notes so is worth displaying.
git cat-file tag $newrev | sed -e '1,/^$/d' git cat-file tag $newrev | sed -e '1,/^$/d'
echo "" echo ""
case "$tagtype" in case "$tagtype" in
commit) commit)
# Only commit tags make sense to have rev-list operations performed # Only commit tags make sense to have rev-list operations
# on them # performed on them
if [ -n "$prevtag" ]; then if [ -n "$prevtag" ]; then
# Show changes since the previous release # Show changes since the previous release
git rev-list --pretty=short "$prevtag..$newrev" | git shortlog git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
else else
# No previous tag, show all the changes since time began # No previous tag, show all the changes since time
# began
git rev-list --pretty=short $newrev | git shortlog git rev-list --pretty=short $newrev | git shortlog
fi fi
;; ;;
*) *)
# XXX: Is there anything useful we can do for non-commit objects? # XXX: Is there anything useful we can do for non-commit
# objects?
;; ;;
esac esac
@ -544,13 +555,14 @@ generate_update_general_email()
# #
generate_general_email() generate_general_email()
{ {
# Unannotated tags are more about marking a point than releasing a version; # Unannotated tags are more about marking a point than releasing a
# therefore we don't do the shortlog summary that we do for annotated tags # version; therefore we don't do the shortlog summary that we do for
# above - we simply show that the point has been marked, and print the log # annotated tags above - we simply show that the point has been
# message for the marked point for reference purposes # marked, and print the log message for the marked point for
# reference purposes
# #
# Note this section also catches any other reference type (although there # Note this section also catches any other reference type (although
# aren't any) and deals with them in the same way. # there aren't any) and deals with them in the same way.
echo "" echo ""
if [ "$newrev_type" = "commit" ]; then if [ "$newrev_type" = "commit" ]; then
@ -558,10 +570,10 @@ generate_general_email()
git show --no-color --root -s $newrev git show --no-color --root -s $newrev
echo $LOGEND echo $LOGEND
else else
# What can we do here? The tag marks an object that is not a commit, # What can we do here? The tag marks an object that is not
# so there is no log for us to display. It's probably not wise to # a commit, so there is no log for us to display. It's
# output git-cat-file as it could be a binary blob. We'll just say how # probably not wise to output git-cat-file as it could be a
# big it is # binary blob. We'll just say how big it is
echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
fi fi
} }
@ -590,7 +602,6 @@ send_mail()
# ---------------------------- main() # ---------------------------- main()
# --- Constants # --- Constants
EMAILPREFIX="[SCM] "
LOGBEGIN="- Log -----------------------------------------------------------------" LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------" LOGEND="-----------------------------------------------------------------------"
@ -604,8 +615,8 @@ if [ -z "$GIT_DIR" ]; then
fi fi
projectdesc=$(sed -ne '1p' "$GIT_DIR/description") projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
# Check if the description is unchanged from it's default, and shorten it to a # Check if the description is unchanged from it's default, and shorten it to
# more manageable length if it is # a more manageable length if it is
if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
then then
projectdesc="UNNAMED PROJECT" projectdesc="UNNAMED PROJECT"
@ -614,13 +625,15 @@ fi
recipients=$(git repo-config hooks.mailinglist) recipients=$(git repo-config hooks.mailinglist)
announcerecipients=$(git repo-config hooks.announcelist) announcerecipients=$(git repo-config hooks.announcelist)
envelopesender=$(git-repo-config hooks.envelopesender) envelopesender=$(git-repo-config hooks.envelopesender)
emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ')
# --- Main loop # --- Main loop
# Allow dual mode: run from the command line just like the update hook, or if # Allow dual mode: run from the command line just like the update hook, or
# no arguments are given then run as a hook script # if no arguments are given then run as a hook script
if [ -n "$1" -a -n "$2" -a -n "$3" ]; then if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
# Output to the terminal in command line mode - if someone wanted to # Output to the terminal in command line mode - if someone wanted to
# resend an email; they could redirect the output to sendmail themselves # resend an email; they could redirect the output to sendmail
# themselves
PAGER= generate_email $2 $3 $1 PAGER= generate_email $2 $3 $1
else else
while read oldrev newrev refname while read oldrev newrev refname

View File

@ -406,7 +406,8 @@ static struct daemon_service daemon_service[] = {
{ "receive-pack", "receivepack", receive_pack, 0, 1 }, { "receive-pack", "receivepack", receive_pack, 0, 1 },
}; };
static void enable_service(const char *name, int ena) { static void enable_service(const char *name, int ena)
{
int i; int i;
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
if (!strcmp(daemon_service[i].name, name)) { if (!strcmp(daemon_service[i].name, name)) {
@ -417,7 +418,8 @@ static void enable_service(const char *name, int ena) {
die("No such service %s", name); die("No such service %s", name);
} }
static void make_service_overridable(const char *name, int ena) { static void make_service_overridable(const char *name, int ena)
{
int i; int i;
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
if (!strcmp(daemon_service[i].name, name)) { if (!strcmp(daemon_service[i].name, name)) {
@ -540,7 +542,7 @@ static int execute(struct sockaddr *addr)
if (addr->sa_family == AF_INET) { if (addr->sa_family == AF_INET) {
struct sockaddr_in *sin_addr = (void *) addr; struct sockaddr_in *sin_addr = (void *) addr;
inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf)); inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
port = sin_addr->sin_port; port = ntohs(sin_addr->sin_port);
#ifndef NO_IPV6 #ifndef NO_IPV6
} else if (addr && addr->sa_family == AF_INET6) { } else if (addr && addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6_addr = (void *) addr; struct sockaddr_in6 *sin6_addr = (void *) addr;
@ -550,7 +552,7 @@ static int execute(struct sockaddr *addr)
inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1); inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
strcat(buf, "]"); strcat(buf, "]");
port = sin6_addr->sin6_port; port = ntohs(sin6_addr->sin6_port);
#endif #endif
} }
loginfo("Connection from %s:%d", addrbuf, port); loginfo("Connection from %s:%d", addrbuf, port);

3
dir.c
View File

@ -298,7 +298,8 @@ int excluded(struct dir_struct *dir, const char *pathname)
return 0; return 0;
} }
static struct dir_entry *dir_entry_new(const char *pathname, int len) { static struct dir_entry *dir_entry_new(const char *pathname, int len)
{
struct dir_entry *ent; struct dir_entry *ent;
ent = xmalloc(sizeof(*ent) + len + 1); ent = xmalloc(sizeof(*ent) + len + 1);

View File

@ -275,7 +275,8 @@ exit_if_skipped_commits () {
if expr "$_tried" : ".*[|].*" > /dev/null ; then if expr "$_tried" : ".*[|].*" > /dev/null ; then
echo "There are only 'skip'ped commit left to test." echo "There are only 'skip'ped commit left to test."
echo "The first bad commit could be any of:" echo "The first bad commit could be any of:"
echo "$_tried" | sed -e 's/[|]/\n/g' echo "$_tried" | sed -e 's/[|]/\
/g'
echo "We cannot bisect more!" echo "We cannot bisect more!"
exit 2 exit 2
fi fi

View File

@ -20,12 +20,16 @@ require_work_tree
ignored= ignored=
ignoredonly= ignoredonly=
cleandir= cleandir=
disabled="`git config --bool clean.requireForce`"
rmf="rm -f --" rmf="rm -f --"
rmrf="rm -rf --" rmrf="rm -rf --"
rm_refuse="echo Not removing" rm_refuse="echo Not removing"
echo1="echo" echo1="echo"
# requireForce used to default to false but now it defaults to true.
# IOW, lack of explicit "clean.requireForce = false" is taken as
# "clean.requireForce = true".
disabled=$(git config --bool clean.requireForce || echo true)
while test $# != 0 while test $# != 0
do do
case "$1" in case "$1" in

View File

@ -14,7 +14,7 @@ die() {
} }
usage() { usage() {
die "Usage: $0 [--template=<template_directory>] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [--depth <n>] [-n] <repo> [<dir>]" die "Usage: $0 [--template=<template_directory>] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [--depth <n>] [-n] [--] <repo> [<dir>]"
} }
get_repo_base() { get_repo_base() {
@ -160,6 +160,9 @@ while
*,--depth) *,--depth)
shift shift
depth="--depth=$1";; depth="--depth=$1";;
*,--)
shift
break ;;
*,-*) usage ;; *,-*) usage ;;
*) break ;; *) break ;;
esac esac

View File

@ -381,4 +381,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result)
return 0; return 0;
} }
static inline int strtol_i(char const *s, int base, int *result)
{
long ul;
char *p;
errno = 0;
ul = strtol(s, &p, base);
if (errno || *p || p == s || (int) ul != ul)
return -1;
*result = ul;
return 0;
}
#endif #endif

View File

@ -818,6 +818,7 @@ while (<CVS>) {
$state = 4; $state = 4;
} elsif ($state == 4 and s/^Branch:\s+//) { } elsif ($state == 4 and s/^Branch:\s+//) {
s/\s+$//; s/\s+$//;
tr/_/\./ if ( $opt_u );
s/[\/]/$opt_s/g; s/[\/]/$opt_s/g;
$branch = $_; $branch = $_;
$state = 5; $state = 5;

View File

@ -15,7 +15,7 @@ browser="`git config --get instaweb.browser`"
port=`git config --get instaweb.port` port=`git config --get instaweb.port`
module_path="`git config --get instaweb.modulepath`" module_path="`git config --get instaweb.modulepath`"
conf=$GIT_DIR/gitweb/httpd.conf conf="$GIT_DIR/gitweb/httpd.conf"
# Defaults: # Defaults:
@ -32,7 +32,7 @@ start_httpd () {
httpd_only="`echo $httpd | cut -f1 -d' '`" httpd_only="`echo $httpd | cut -f1 -d' '`"
if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
then then
$httpd $fqgitdir/gitweb/httpd.conf $httpd "$fqgitdir/gitweb/httpd.conf"
else else
# many httpds are installed in /usr/sbin or /usr/local/sbin # many httpds are installed in /usr/sbin or /usr/local/sbin
# these days and those are not in most users $PATHs # these days and those are not in most users $PATHs
@ -185,14 +185,14 @@ server.pid-file = "$fqgitdir/pid"
cgi.assign = ( ".cgi" => "" ) cgi.assign = ( ".cgi" => "" )
mimetype.assign = ( ".css" => "text/css" ) mimetype.assign = ( ".css" => "text/css" )
EOF EOF
test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf" test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
} }
apache2_conf () { apache2_conf () {
test -z "$module_path" && module_path=/usr/lib/apache2/modules test -z "$module_path" && module_path=/usr/lib/apache2/modules
mkdir -p "$GIT_DIR/gitweb/logs" mkdir -p "$GIT_DIR/gitweb/logs"
bind= bind=
test "$local" = true && bind='127.0.0.1:' test x"$local" = xtrue && bind='127.0.0.1:'
echo 'text/css css' > $fqgitdir/mime.types echo 'text/css css' > $fqgitdir/mime.types
cat > "$conf" <<EOF cat > "$conf" <<EOF
ServerName "git-instaweb" ServerName "git-instaweb"
@ -245,7 +245,7 @@ EOF
} }
script=' script='
s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'`dirname $fqgitdir`'";# s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'$(dirname "$fqgitdir")'";#
s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";# s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";#
s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;# s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;#
s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#' s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#'
@ -265,8 +265,8 @@ gitweb_css () {
EOFGITWEB EOFGITWEB
} }
gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
gitweb_css $GIT_DIR/gitweb/gitweb.css gitweb_css "$GIT_DIR/gitweb/gitweb.css"
case "$httpd" in case "$httpd" in
*lighttpd*) *lighttpd*)
@ -285,6 +285,5 @@ webrick)
esac esac
start_httpd start_httpd
test -z "$browser" && browser=echo
url=http://127.0.0.1:$port url=http://127.0.0.1:$port
$browser $url || echo $url "$browser" $url || echo $url

View File

@ -4,6 +4,8 @@ USAGE=''
SUBDIRECTORY_OK='Yes' SUBDIRECTORY_OK='Yes'
. git-sh-setup . git-sh-setup
echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2
if [ "$#" != "0" ] if [ "$#" != "0" ]
then then
usage usage

View File

@ -391,7 +391,7 @@ do
-s|--strategy) -s|--strategy)
case "$#,$1" in case "$#,$1" in
*,*=*) *,*=*)
STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;; STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
1,*) 1,*)
usage ;; usage ;;
*) *)

View File

@ -24,13 +24,13 @@ headrev=`git rev-parse --verify "$head"^0` || exit
merge_base=`git merge-base $baserev $headrev` || merge_base=`git merge-base $baserev $headrev` ||
die "fatal: No commits in common between $base and $head" die "fatal: No commits in common between $base and $head"
url="`get_remote_url "$url"`" url=$(get_remote_url "$url")
branch=`git peek-remote "$url" \ branch=$(git peek-remote "$url" \
| sed -n -e "/^$headrev refs.heads./{ | sed -n -e "/^$headrev refs.heads./{
s/^.* refs.heads.// s/^.* refs.heads.//
p p
q q
}"` }")
if [ -z "$branch" ]; then if [ -z "$branch" ]; then
echo "warn: No branch of $url is at:" >&2 echo "warn: No branch of $url is at:" >&2
git log --max-count=1 --pretty='format:warn: %h: %s' $headrev >&2 git log --max-count=1 --pretty='format:warn: %h: %s' $headrev >&2

View File

@ -88,8 +88,7 @@ Options:
--smtp-ssl If set, connects to the SMTP server using SSL. --smtp-ssl If set, connects to the SMTP server using SSL.
--suppress-from Suppress sending emails to yourself if your address --suppress-from Suppress sending emails to yourself. Defaults to off.
appears in a From: line. Defaults to off.
--thread Specify that the "In-Reply-To:" header should be set on all --thread Specify that the "In-Reply-To:" header should be set on all
emails. Defaults to on. emails. Defaults to on.
@ -353,7 +352,7 @@ sub expand_aliases {
if (!defined $initial_subject && $compose) { if (!defined $initial_subject && $compose) {
do { do {
$_ = $term->readline("What subject should the emails start with? ", $_ = $term->readline("What subject should the initial email start with? ",
$initial_subject); $initial_subject);
} while (!defined $_); } while (!defined $_);
$initial_subject = $_; $initial_subject = $_;
@ -730,6 +729,7 @@ foreach my $t (@files) {
if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) { if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) {
my $c = $2; my $c = $2;
chomp $c; chomp $c;
next if ($c eq $sender and $suppress_from);
push @cc, $c; push @cc, $c;
printf("(sob) Adding cc: %s from line '%s'\n", printf("(sob) Adding cc: %s from line '%s'\n",
$c, $_) unless $quiet; $c, $_) unless $quiet;
@ -745,6 +745,7 @@ foreach my $t (@files) {
my $c = $_; my $c = $_;
$c =~ s/^\s*//g; $c =~ s/^\s*//g;
$c =~ s/\n$//g; $c =~ s/\n$//g;
next if ($c eq $sender and $suppress_from);
push @cc, $c; push @cc, $c;
printf("(cc-cmd) Adding cc: %s from: '%s'\n", printf("(cc-cmd) Adding cc: %s from: '%s'\n",
$c, $cc_cmd) unless $quiet; $c, $cc_cmd) unless $quiet;

View File

@ -73,7 +73,7 @@ resolve_relative_url ()
module_name() module_name()
{ {
# Do we have "submodule.<something>.path = $1" defined in .gitmodules file? # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g') re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
name=$( GIT_CONFIG=.gitmodules \ name=$( GIT_CONFIG=.gitmodules \
git config --get-regexp '^submodule\..*\.path$' | git config --get-regexp '^submodule\..*\.path$' |
sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' ) sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )

View File

@ -252,7 +252,7 @@ Usage: $0 <command> [options] [arguments]\n
next if $cmd && $cmd ne $_; next if $cmd && $cmd ne $_;
next if /^multi-/; # don't show deprecated commands next if /^multi-/; # don't show deprecated commands
print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n";
foreach (keys %{$cmd{$_}->[2]}) { foreach (sort keys %{$cmd{$_}->[2]}) {
# mixed-case options are for .git/config only # mixed-case options are for .git/config only
next if /[A-Z]/ && /^[a-z]+$/i; next if /[A-Z]/ && /^[a-z]+$/i;
# prints out arguments as they should be passed: # prints out arguments as they should be passed:
@ -406,7 +406,8 @@ sub cmd_dcommit {
"If these changes depend on each other, re-running ", "If these changes depend on each other, re-running ",
"without --no-rebase will be required." "without --no-rebase will be required."
} }
foreach my $d (@$linear_refs) { while (1) {
my $d = shift @$linear_refs or last;
unless (defined $last_rev) { unless (defined $last_rev) {
(undef, $last_rev, undef) = cmt_metadata("$d~1"); (undef, $last_rev, undef) = cmt_metadata("$d~1");
unless (defined $last_rev) { unless (defined $last_rev) {
@ -439,14 +440,14 @@ sub cmd_dcommit {
# we always want to rebase against the current HEAD, # we always want to rebase against the current HEAD,
# not any head that was passed to us # not any head that was passed to us
my @diff = command('diff-tree', 'HEAD', my @diff = command('diff-tree', $d,
$gs->refname, '--'); $gs->refname, '--');
my @finish; my @finish;
if (@diff) { if (@diff) {
@finish = rebase_cmd(); @finish = rebase_cmd();
print STDERR "W: HEAD and ", $gs->refname, print STDERR "W: $d and ", $gs->refname,
" differ, using @finish:\n", " differ, using @finish:\n",
"@diff"; join("\n", @diff), "\n";
} else { } else {
print "No changes between current HEAD and ", print "No changes between current HEAD and ",
$gs->refname, $gs->refname,
@ -455,6 +456,45 @@ sub cmd_dcommit {
@finish = qw/reset --mixed/; @finish = qw/reset --mixed/;
} }
command_noisy(@finish, $gs->refname); command_noisy(@finish, $gs->refname);
if (@diff) {
@refs = ();
my ($url_, $rev_, $uuid_, $gs_) =
working_head_info($head, \@refs);
my ($linear_refs_, $parents_) =
linearize_history($gs_, \@refs);
if (scalar(@$linear_refs) !=
scalar(@$linear_refs_)) {
fatal "# of revisions changed ",
"\nbefore:\n",
join("\n", @$linear_refs),
"\n\nafter:\n",
join("\n", @$linear_refs_), "\n",
'If you are attempting to commit ',
"merges, try running:\n\t",
'git rebase --interactive',
'--preserve-merges ',
$gs->refname,
"\nBefore dcommitting";
}
if ($url_ ne $url) {
fatal "URL mismatch after rebase: ",
"$url_ != $url";
}
if ($uuid_ ne $uuid) {
fatal "uuid mismatch after rebase: ",
"$uuid_ != $uuid";
}
# remap parents
my (%p, @l, $i);
for ($i = 0; $i < scalar @$linear_refs; $i++) {
my $new = $linear_refs_->[$i] or next;
$p{$new} =
$parents->{$linear_refs->[$i]};
push @l, $new;
}
$parents = \%p;
$linear_refs = \@l;
}
$last_rev = $cmt_rev; $last_rev = $cmt_rev;
} }
} }

13
git.c
View File

@ -249,14 +249,9 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
prefix = setup_git_directory(); prefix = setup_git_directory();
if (p->option & USE_PAGER) if (p->option & USE_PAGER)
setup_pager(); setup_pager();
if (p->option & NEED_WORK_TREE) { if (p->option & NEED_WORK_TREE)
const char *work_tree = get_git_work_tree(); setup_work_tree();
const char *git_dir = get_git_dir();
if (!is_absolute_path(git_dir))
set_git_dir(make_absolute_path(git_dir));
if (!work_tree || chdir(work_tree))
die("%s must be run in a work tree", p->cmd);
}
trace_argv_printf(argv, argc, "trace: built-in: git"); trace_argv_printf(argv, argc, "trace: built-in: git");
status = p->fn(argc, argv, prefix); status = p->fn(argc, argv, prefix);
@ -347,7 +342,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "rev-list", cmd_rev_list, RUN_SETUP }, { "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP }, { "rev-parse", cmd_rev_parse, RUN_SETUP },
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE }, { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
{ "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE }, { "rm", cmd_rm, RUN_SETUP },
{ "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE }, { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
{ "send-pack", cmd_send_pack, RUN_SETUP }, { "send-pack", cmd_send_pack, RUN_SETUP },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },

View File

@ -611,6 +611,15 @@ sub href(%) {
); );
my %mapping = @mapping; my %mapping = @mapping;
if ($params{-replay}) {
while (my ($name, $symbol) = each %mapping) {
if (!exists $params{$name}) {
# to allow for multivalued params we use arrayref form
$params{$name} = [ $cgi->param($symbol) ];
}
}
}
$params{'project'} = $project unless exists $params{'project'}; $params{'project'} = $project unless exists $params{'project'};
my ($use_pathinfo) = gitweb_check_feature('pathinfo'); my ($use_pathinfo) = gitweb_check_feature('pathinfo');
@ -1423,20 +1432,121 @@ sub git_get_type {
return $type; return $type;
} }
# repository configuration
our $config_file = '';
our %config;
# store multiple values for single key as anonymous array reference
# single values stored directly in the hash, not as [ <value> ]
sub hash_set_multi {
my ($hash, $key, $value) = @_;
if (!exists $hash->{$key}) {
$hash->{$key} = $value;
} elsif (!ref $hash->{$key}) {
$hash->{$key} = [ $hash->{$key}, $value ];
} else {
push @{$hash->{$key}}, $value;
}
}
# return hash of git project configuration
# optionally limited to some section, e.g. 'gitweb'
sub git_parse_project_config {
my $section_regexp = shift;
my %config;
local $/ = "\0";
open my $fh, "-|", git_cmd(), "config", '-z', '-l',
or return;
while (my $keyval = <$fh>) {
chomp $keyval;
my ($key, $value) = split(/\n/, $keyval, 2);
hash_set_multi(\%config, $key, $value)
if (!defined $section_regexp || $key =~ /^(?:$section_regexp)\./o);
}
close $fh;
return %config;
}
# convert config value to boolean, 'true' or 'false'
# no value, number > 0, 'true' and 'yes' values are true
# rest of values are treated as false (never as error)
sub config_to_bool {
my $val = shift;
# strip leading and trailing whitespace
$val =~ s/^\s+//;
$val =~ s/\s+$//;
return (!defined $val || # section.key
($val =~ /^\d+$/ && $val) || # section.key = 1
($val =~ /^(?:true|yes)$/i)); # section.key = true
}
# convert config value to simple decimal number
# an optional value suffix of 'k', 'm', or 'g' will cause the value
# to be multiplied by 1024, 1048576, or 1073741824
sub config_to_int {
my $val = shift;
# strip leading and trailing whitespace
$val =~ s/^\s+//;
$val =~ s/\s+$//;
if (my ($num, $unit) = ($val =~ /^([0-9]*)([kmg])$/i)) {
$unit = lc($unit);
# unknown unit is treated as 1
return $num * ($unit eq 'g' ? 1073741824 :
$unit eq 'm' ? 1048576 :
$unit eq 'k' ? 1024 : 1);
}
return $val;
}
# convert config value to array reference, if needed
sub config_to_multi {
my $val = shift;
return ref($val) ? $val : [ $val ];
}
sub git_get_project_config { sub git_get_project_config {
my ($key, $type) = @_; my ($key, $type) = @_;
# key sanity check
return unless ($key); return unless ($key);
$key =~ s/^gitweb\.//; $key =~ s/^gitweb\.//;
return if ($key =~ m/\W/); return if ($key =~ m/\W/);
my @x = (git_cmd(), 'config'); # type sanity check
if (defined $type) { push @x, $type; } if (defined $type) {
push @x, "--get"; $type =~ s/^--//;
push @x, "gitweb.$key"; $type = undef
my $val = qx(@x); unless ($type eq 'bool' || $type eq 'int');
chomp $val; }
return ($val);
# get config
if (!defined $config_file ||
$config_file ne "$git_dir/config") {
%config = git_parse_project_config('gitweb');
$config_file = "$git_dir/config";
}
# ensure given type
if (!defined $type) {
return $config{"gitweb.$key"};
} elsif ($type eq 'bool') {
# backward compatibility: 'git config --bool' returns true/false
return config_to_bool($config{"gitweb.$key"}) ? 'true' : 'false';
} elsif ($type eq 'int') {
return config_to_int($config{"gitweb.$key"});
}
return $config{"gitweb.$key"};
} }
# get hash of given path at given ref # get hash of given path at given ref
@ -1496,7 +1606,9 @@ sub git_get_path_by_hash {
sub git_get_project_description { sub git_get_project_description {
my $path = shift; my $path = shift;
open my $fd, "$projectroot/$path/description" or return undef; $git_dir = "$projectroot/$path";
open my $fd, "$projectroot/$path/description"
or return git_get_project_config('description');
my $descr = <$fd>; my $descr = <$fd>;
close $fd; close $fd;
if (defined $descr) { if (defined $descr) {
@ -1508,7 +1620,11 @@ sub git_get_project_description {
sub git_get_project_url_list { sub git_get_project_url_list {
my $path = shift; my $path = shift;
open my $fd, "$projectroot/$path/cloneurl" or return; $git_dir = "$projectroot/$path";
open my $fd, "$projectroot/$path/cloneurl"
or return wantarray ?
@{ config_to_multi(git_get_project_config('url')) } :
config_to_multi(git_get_project_config('url'));
my @git_project_url_list = map { chomp; $_ } <$fd>; my @git_project_url_list = map { chomp; $_ } <$fd>;
close $fd; close $fd;
@ -1990,12 +2106,12 @@ sub parse_difftree_raw_line {
$res{'to_mode'} = $2; $res{'to_mode'} = $2;
$res{'from_id'} = $3; $res{'from_id'} = $3;
$res{'to_id'} = $4; $res{'to_id'} = $4;
$res{'status'} = $5; $res{'status'} = $res{'status_str'} = $5;
$res{'similarity'} = $6; $res{'similarity'} = $6;
if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied
($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7);
} else { } else {
$res{'file'} = unquote($7); $res{'from_file'} = $res{'to_file'} = $res{'file'} = unquote($7);
} }
} }
# '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh' # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh'
@ -2006,6 +2122,7 @@ sub parse_difftree_raw_line {
$res{'to_mode'} = pop @{$res{'from_mode'}}; $res{'to_mode'} = pop @{$res{'from_mode'}};
$res{'from_id'} = [ split(' ', $3) ]; $res{'from_id'} = [ split(' ', $3) ];
$res{'to_id'} = pop @{$res{'from_id'}}; $res{'to_id'} = pop @{$res{'from_id'}};
$res{'status_str'} = $4;
$res{'status'} = [ split('', $4) ]; $res{'status'} = [ split('', $4) ];
$res{'to_file'} = unquote($5); $res{'to_file'} = unquote($5);
} }
@ -2062,7 +2179,10 @@ sub parse_from_to_diffinfo {
fill_from_file_info($diffinfo, @parents) fill_from_file_info($diffinfo, @parents)
unless exists $diffinfo->{'from_file'}; unless exists $diffinfo->{'from_file'};
for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
$from->{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'}; $from->{'file'}[$i] =
defined $diffinfo->{'from_file'}[$i] ?
$diffinfo->{'from_file'}[$i] :
$diffinfo->{'to_file'};
if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file
$from->{'href'}[$i] = href(action=>"blob", $from->{'href'}[$i] = href(action=>"blob",
hash_base=>$parents[$i], hash_base=>$parents[$i],
@ -2074,7 +2194,7 @@ sub parse_from_to_diffinfo {
} }
} else { } else {
# ordinary (not combined) diff # ordinary (not combined) diff
$from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; $from->{'file'} = $diffinfo->{'from_file'};
if ($diffinfo->{'status'} ne "A") { # not new (added) file if ($diffinfo->{'status'} ne "A") { # not new (added) file
$from->{'href'} = href(action=>"blob", hash_base=>$hash_parent, $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent,
hash=>$diffinfo->{'from_id'}, hash=>$diffinfo->{'from_id'},
@ -2084,7 +2204,7 @@ sub parse_from_to_diffinfo {
} }
} }
$to->{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'}; $to->{'file'} = $diffinfo->{'to_file'};
if (!is_deleted($diffinfo)) { # file exists in result if (!is_deleted($diffinfo)) { # file exists in result
$to->{'href'} = href(action=>"blob", hash_base=>$hash, $to->{'href'} = href(action=>"blob", hash_base=>$hash,
hash=>$diffinfo->{'to_id'}, hash=>$diffinfo->{'to_id'},
@ -2505,7 +2625,7 @@ sub format_paging_nav {
if ($page > 0) { if ($page > 0) {
$paging_nav .= " &sdot; " . $paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page-1), $cgi->a({-href => href(-replay=>1, page=>$page-1),
-accesskey => "p", -title => "Alt-p"}, "prev"); -accesskey => "p", -title => "Alt-p"}, "prev");
} else { } else {
$paging_nav .= " &sdot; prev"; $paging_nav .= " &sdot; prev";
@ -2513,7 +2633,7 @@ sub format_paging_nav {
if ($nrevs >= (100 * ($page+1)-1)) { if ($nrevs >= (100 * ($page+1)-1)) {
$paging_nav .= " &sdot; " . $paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page+1), $cgi->a({-href => href(-replay=>1, page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next"); -accesskey => "n", -title => "Alt-n"}, "next");
} else { } else {
$paging_nav .= " &sdot; next"; $paging_nav .= " &sdot; next";
@ -2818,7 +2938,7 @@ sub fill_from_file_info {
sub is_deleted { sub is_deleted {
my $diffinfo = shift; my $diffinfo = shift;
return $diffinfo->{'to_id'} eq ('0' x 40); return $diffinfo->{'status_str'} =~ /D/;
} }
# does patch correspond to [previous] difftree raw line # does patch correspond to [previous] difftree raw line
@ -2829,7 +2949,7 @@ sub is_patch_split {
my ($diffinfo, $patchinfo) = @_; my ($diffinfo, $patchinfo) = @_;
return defined $diffinfo && defined $patchinfo return defined $diffinfo && defined $patchinfo
&& ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'}; && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'};
} }
@ -3898,10 +4018,10 @@ sub git_blame2 {
or die_error(undef, "Open git-blame failed"); or die_error(undef, "Open git-blame failed");
git_header_html(); git_header_html();
my $formats_nav = my $formats_nav =
$cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, $cgi->a({-href => href(action=>"blob", -replay=>1)},
"blob") . "blob") .
" | " . " | " .
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, $cgi->a({-href => href(action=>"history", -replay=>1)},
"history") . "history") .
" | " . " | " .
$cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, $cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
@ -4178,18 +4298,15 @@ sub git_blob {
if (defined $file_name) { if (defined $file_name) {
if ($have_blame) { if ($have_blame) {
$formats_nav .= $formats_nav .=
$cgi->a({-href => href(action=>"blame", hash_base=>$hash_base, $cgi->a({-href => href(action=>"blame", -replay=>1)},
hash=>$hash, file_name=>$file_name)},
"blame") . "blame") .
" | "; " | ";
} }
$formats_nav .= $formats_nav .=
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base, $cgi->a({-href => href(action=>"history", -replay=>1)},
hash=>$hash, file_name=>$file_name)},
"history") . "history") .
" | " . " | " .
$cgi->a({-href => href(action=>"blob_plain", $cgi->a({-href => href(action=>"blob_plain", -replay=>1)},
hash=>$hash, file_name=>$file_name)},
"raw") . "raw") .
" | " . " | " .
$cgi->a({-href => href(action=>"blob", $cgi->a({-href => href(action=>"blob",
@ -4197,7 +4314,8 @@ sub git_blob {
"HEAD"); "HEAD");
} else { } else {
$formats_nav .= $formats_nav .=
$cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "raw"); $cgi->a({-href => href(action=>"blob_plain", -replay=>1)},
"raw");
} }
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
@ -4260,8 +4378,7 @@ sub git_tree {
my @views_nav = (); my @views_nav = ();
if (defined $file_name) { if (defined $file_name) {
push @views_nav, push @views_nav,
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base, $cgi->a({-href => href(action=>"history", -replay=>1)},
hash=>$hash, file_name=>$file_name)},
"history"), "history"),
$cgi->a({-href => href(action=>"tree", $cgi->a({-href => href(action=>"tree",
hash_base=>"HEAD", file_name=>$file_name)}, hash_base=>"HEAD", file_name=>$file_name)},
@ -4435,7 +4552,7 @@ sub git_log {
} }
if ($#commitlist >= 100) { if ($#commitlist >= 100) {
print "<div class=\"page_nav\">\n"; print "<div class=\"page_nav\">\n";
print $cgi->a({-href => href(action=>"log", hash=>$hash, page=>$page+1), print $cgi->a({-href => href(-replay=>1, page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next"); -accesskey => "n", -title => "Alt-n"}, "next");
print "</div>\n"; print "</div>\n";
} }
@ -4667,8 +4784,8 @@ sub git_blobdiff {
} }
%diffinfo = parse_difftree_raw_line($difftree[0]); %diffinfo = parse_difftree_raw_line($difftree[0]);
$file_parent ||= $diffinfo{'from_file'} || $file_name || $diffinfo{'file'}; $file_parent ||= $diffinfo{'from_file'} || $file_name;
$file_name ||= $diffinfo{'to_file'} || $diffinfo{'file'}; $file_name ||= $diffinfo{'to_file'};
$hash_parent ||= $diffinfo{'from_id'}; $hash_parent ||= $diffinfo{'from_id'};
$hash ||= $diffinfo{'to_id'}; $hash ||= $diffinfo{'to_id'};
@ -4729,10 +4846,7 @@ sub git_blobdiff {
# header # header
if ($format eq 'html') { if ($format eq 'html') {
my $formats_nav = my $formats_nav =
$cgi->a({-href => href(action=>"blobdiff_plain", $cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)},
hash=>$hash, hash_parent=>$hash_parent,
hash_base=>$hash_base, hash_parent_base=>$hash_parent_base,
file_name=>$file_name, file_parent=>$file_parent)},
"raw"); "raw");
git_header_html(undef, $expires); git_header_html(undef, $expires);
if (defined $hash_base && (my %co = parse_commit($hash_base))) { if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@ -4806,8 +4920,7 @@ sub git_commitdiff {
my $formats_nav; my $formats_nav;
if ($format eq 'html') { if ($format eq 'html') {
$formats_nav = $formats_nav =
$cgi->a({-href => href(action=>"commitdiff_plain", $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
hash=>$hash, hash_parent=>$hash_parent)},
"raw"); "raw");
if (defined $hash_parent && if (defined $hash_parent &&
@ -5002,27 +5115,20 @@ sub git_history {
file_name=>$file_name)}, file_name=>$file_name)},
"first"); "first");
$paging_nav .= " &sdot; " . $paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, $cgi->a({-href => href(-replay=>1, page=>$page-1),
file_name=>$file_name, page=>$page-1),
-accesskey => "p", -title => "Alt-p"}, "prev"); -accesskey => "p", -title => "Alt-p"}, "prev");
} else { } else {
$paging_nav .= "first"; $paging_nav .= "first";
$paging_nav .= " &sdot; prev"; $paging_nav .= " &sdot; prev";
} }
if ($#commitlist >= 100) {
$paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
file_name=>$file_name, page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next");
} else {
$paging_nav .= " &sdot; next";
}
my $next_link = ''; my $next_link = '';
if ($#commitlist >= 100) { if ($#commitlist >= 100) {
$next_link = $next_link =
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, $cgi->a({-href => href(-replay=>1, page=>$page+1),
file_name=>$file_name, page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next"); -accesskey => "n", -title => "Alt-n"}, "next");
$paging_nav .= " &sdot; $next_link";
} else {
$paging_nav .= " &sdot; next";
} }
git_header_html(); git_header_html();
@ -5092,30 +5198,23 @@ sub git_search {
searchtext=>$searchtext, searchtype=>$searchtype)}, searchtext=>$searchtext, searchtype=>$searchtype)},
"first"); "first");
$paging_nav .= " &sdot; " . $paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>"search", hash=>$hash, $cgi->a({-href => href(-replay=>1, page=>$page-1),
searchtext=>$searchtext, searchtype=>$searchtype,
page=>$page-1),
-accesskey => "p", -title => "Alt-p"}, "prev"); -accesskey => "p", -title => "Alt-p"}, "prev");
} else { } else {
$paging_nav .= "first"; $paging_nav .= "first";
$paging_nav .= " &sdot; prev"; $paging_nav .= " &sdot; prev";
} }
if ($#commitlist >= 100) {
$paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>"search", hash=>$hash,
searchtext=>$searchtext, searchtype=>$searchtype,
page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next");
} else {
$paging_nav .= " &sdot; next";
}
my $next_link = ''; my $next_link = '';
if ($#commitlist >= 100) { if ($#commitlist >= 100) {
$next_link = $next_link =
$cgi->a({-href => href(action=>"search", hash=>$hash, $cgi->a({-href => href(-replay=>1, page=>$page+1),
searchtext=>$searchtext, searchtype=>$searchtype,
page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next"); -accesskey => "n", -title => "Alt-n"}, "next");
$paging_nav .= " &sdot; $next_link";
} else {
$paging_nav .= " &sdot; next";
}
if ($#commitlist >= 100) {
} }
git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav); git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
@ -5314,7 +5413,7 @@ sub git_shortlog {
my $next_link = ''; my $next_link = '';
if ($#commitlist >= 100) { if ($#commitlist >= 100) {
$next_link = $next_link =
$cgi->a({-href => href(action=>"shortlog", hash=>$hash, page=>$page+1), $cgi->a({-href => href(-replay=>1, page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next"); -accesskey => "n", -title => "Alt-n"}, "next");
} }

3
help.c
View File

@ -79,7 +79,8 @@ static void uniq(struct cmdnames *cmds)
cmds->cnt = j; cmds->cnt = j;
} }
static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
{
int ci, cj, ei; int ci, cj, ei;
int cmp; int cmp;

View File

@ -2241,7 +2241,11 @@ static int delete_remote_branch(char *pattern, int force)
/* Remote branch must be an ancestor of remote HEAD */ /* Remote branch must be an ancestor of remote HEAD */
if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) { if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) {
return error("The branch '%s' is not a strict subset of your current HEAD.\nIf you are sure you want to delete it, run:\n\t'git http-push -D %s %s'", remote_ref->name, remote->url, pattern); return error("The branch '%s' is not an ancestor "
"of your current HEAD.\n"
"If you are sure you want to delete it,"
" run:\n\t'git http-push -D %s %s'",
remote_ref->name, remote->url, pattern);
} }
} }
@ -2417,16 +2421,17 @@ int main(int argc, char **argv)
if (!has_sha1_file(ref->old_sha1) || if (!has_sha1_file(ref->old_sha1) ||
!ref_newer(ref->peer_ref->new_sha1, !ref_newer(ref->peer_ref->new_sha1,
ref->old_sha1)) { ref->old_sha1)) {
/* We do not have the remote ref, or /*
* We do not have the remote ref, or
* we know that the remote ref is not * we know that the remote ref is not
* an ancestor of what we are trying to * an ancestor of what we are trying to
* push. Either way this can be losing * push. Either way this can be losing
* commits at the remote end and likely * commits at the remote end and likely
* we were not up to date to begin with. * we were not up to date to begin with.
*/ */
error("remote '%s' is not a strict " error("remote '%s' is not an ancestor of\n"
"subset of local ref '%s'. " "local '%s'.\n"
"maybe you are not up-to-date and " "Maybe you are not up-to-date and "
"need to pull first?", "need to pull first?",
ref->name, ref->name,
ref->peer_ref->name); ref->peer_ref->name);

View File

@ -683,6 +683,17 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
} }
} }
static int git_index_pack_config(const char *k, const char *v)
{
if (!strcmp(k, "pack.indexversion")) {
pack_idx_default_version = git_config_int(k, v);
if (pack_idx_default_version > 2)
die("bad pack.indexversion=%d", pack_idx_default_version);
return 0;
}
return git_default_config(k, v);
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i, fix_thin_pack = 0; int i, fix_thin_pack = 0;
@ -693,6 +704,8 @@ int main(int argc, char **argv)
struct pack_idx_entry **idx_objects; struct pack_idx_entry **idx_objects;
unsigned char sha1[20]; unsigned char sha1[20];
git_config(git_index_pack_config);
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
char *arg = argv[i]; char *arg = argv[i];

View File

@ -125,6 +125,18 @@ static unsigned int digits_in_number(unsigned int number)
return result; return result;
} }
static int has_non_ascii(const char *s)
{
int ch;
if (!s)
return 0;
while ((ch = *s++) != '\0') {
if (non_ascii(ch))
return 1;
}
return 0;
}
void show_log(struct rev_info *opt, const char *sep) void show_log(struct rev_info *opt, const char *sep)
{ {
struct strbuf msgbuf; struct strbuf msgbuf;
@ -273,7 +285,8 @@ void show_log(struct rev_info *opt, const char *sep)
*/ */
strbuf_init(&msgbuf, 0); strbuf_init(&msgbuf, 0);
pretty_print_commit(opt->commit_format, commit, &msgbuf, pretty_print_commit(opt->commit_format, commit, &msgbuf,
abbrev, subject, extra_headers, opt->date_mode); abbrev, subject, extra_headers, opt->date_mode,
has_non_ascii(opt->add_signoff));
if (opt->add_signoff) if (opt->add_signoff)
append_signoff(&msgbuf, opt->add_signoff); append_signoff(&msgbuf, opt->add_signoff);

View File

@ -22,6 +22,41 @@ enum parse_opt_option_flags {
struct option; struct option;
typedef int parse_opt_cb(const struct option *, const char *arg, int unset); typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
/*
* `type`::
* holds the type of the option, you must have an OPTION_END last in your
* array.
*
* `short_name`::
* the character to use as a short option name, '\0' if none.
*
* `long_name`::
* the long option name, without the leading dashes, NULL if none.
*
* `value`::
* stores pointers to the values to be filled.
*
* `argh`::
* token to explain the kind of argument this option wants. Keep it
* homogenous across the repository.
*
* `help`::
* the short help associated to what the option does.
* Must never be NULL (except for OPTION_END).
* OPTION_GROUP uses this pointer to store the group header.
*
* `flags`::
* mask of parse_opt_option_flags.
* PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs)
* PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
*
* `callback`::
* pointer to the callback to use for OPTION_CALLBACK.
*
* `defval`::
* default value to fill (*->value) with for PARSE_OPT_OPTARG.
* CALLBACKS can use it like they want.
*/
struct option { struct option {
enum parse_opt_type type; enum parse_opt_type type;
int short_name; int short_name;
@ -32,8 +67,6 @@ struct option {
int flags; int flags;
parse_opt_cb *callback; parse_opt_cb *callback;
/* holds default value for PARSE_OPT_OPTARG,
though callbacks can use it like they want */
intptr_t defval; intptr_t defval;
}; };

View File

@ -812,7 +812,7 @@ sub _cmd_exec {
$self->wc_subdir() and chdir($self->wc_subdir()); $self->wc_subdir() and chdir($self->wc_subdir());
} }
_execv_git_cmd(@args); _execv_git_cmd(@args);
die "exec failed: $!"; die qq[exec "@args" failed: $!];
} }
# Execute the given Git command ($_[0]) with arguments ($_[1..]) # Execute the given Git command ($_[0]) with arguments ($_[1..])

723
pretty.c Normal file
View File

@ -0,0 +1,723 @@
#include "cache.h"
#include "commit.h"
#include "interpolate.h"
#include "utf8.h"
#include "diff.h"
#include "revision.h"
static struct cmt_fmt_map {
const char *n;
size_t cmp_len;
enum cmit_fmt v;
} cmt_fmts[] = {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
{ "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
{ "format:", 7, CMIT_FMT_USERFORMAT},
};
static char *user_format;
enum cmit_fmt get_commit_format(const char *arg)
{
int i;
if (!arg || !*arg)
return CMIT_FMT_DEFAULT;
if (*arg == '=')
arg++;
if (!prefixcmp(arg, "format:")) {
if (user_format)
free(user_format);
user_format = xstrdup(arg + 7);
return CMIT_FMT_USERFORMAT;
}
for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
!strncmp(arg, cmt_fmts[i].n, strlen(arg)))
return cmt_fmts[i].v;
}
die("invalid --pretty format: %s", arg);
}
/*
* Generic support for pretty-printing the header
*/
static int get_one_line(const char *msg)
{
int ret = 0;
for (;;) {
char c = *msg++;
if (!c)
break;
ret++;
if (c == '\n')
break;
}
return ret;
}
/* High bit set, or ISO-2022-INT */
int non_ascii(int ch)
{
ch = (ch & 0xff);
return ((ch & 0x80) || (ch == 0x1b));
}
static int is_rfc2047_special(char ch)
{
return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
}
static void add_rfc2047(struct strbuf *sb, const char *line, int len,
const char *encoding)
{
int i, last;
for (i = 0; i < len; i++) {
int ch = line[i];
if (non_ascii(ch))
goto needquote;
if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
goto needquote;
}
strbuf_add(sb, line, len);
return;
needquote:
strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
strbuf_addf(sb, "=?%s?q?", encoding);
for (i = last = 0; i < len; i++) {
unsigned ch = line[i] & 0xFF;
/*
* We encode ' ' using '=20' even though rfc2047
* allows using '_' for readability. Unfortunately,
* many programs do not understand this and just
* leave the underscore in place.
*/
if (is_rfc2047_special(ch) || ch == ' ') {
strbuf_add(sb, line + last, i - last);
strbuf_addf(sb, "=%02X", ch);
last = i + 1;
}
}
strbuf_add(sb, line + last, len - last);
strbuf_addstr(sb, "?=");
}
static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
const char *line, enum date_mode dmode,
const char *encoding)
{
char *date;
int namelen;
unsigned long time;
int tz;
const char *filler = " ";
if (fmt == CMIT_FMT_ONELINE)
return;
date = strchr(line, '>');
if (!date)
return;
namelen = ++date - line;
time = strtoul(date, &date, 10);
tz = strtol(date, NULL, 10);
if (fmt == CMIT_FMT_EMAIL) {
char *name_tail = strchr(line, '<');
int display_name_length;
if (!name_tail)
return;
while (line < name_tail && isspace(name_tail[-1]))
name_tail--;
display_name_length = name_tail - line;
filler = "";
strbuf_addstr(sb, "From: ");
add_rfc2047(sb, line, display_name_length, encoding);
strbuf_add(sb, name_tail, namelen - display_name_length);
strbuf_addch(sb, '\n');
} else {
strbuf_addf(sb, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
}
switch (fmt) {
case CMIT_FMT_MEDIUM:
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode));
break;
case CMIT_FMT_EMAIL:
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
break;
case CMIT_FMT_FULLER:
strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
break;
default:
/* notin' */
break;
}
}
static int is_empty_line(const char *line, int *len_p)
{
int len = *len_p;
while (len && isspace(line[len-1]))
len--;
*len_p = len;
return !len;
}
static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
const struct commit *commit, int abbrev)
{
struct commit_list *parent = commit->parents;
if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
!parent || !parent->next)
return;
strbuf_addstr(sb, "Merge:");
while (parent) {
struct commit *p = parent->item;
const char *hex = NULL;
const char *dots;
if (abbrev)
hex = find_unique_abbrev(p->object.sha1, abbrev);
if (!hex)
hex = sha1_to_hex(p->object.sha1);
dots = (abbrev && strlen(hex) != 40) ? "..." : "";
parent = parent->next;
strbuf_addf(sb, " %s%s", hex, dots);
}
strbuf_addch(sb, '\n');
}
static char *get_header(const struct commit *commit, const char *key)
{
int key_len = strlen(key);
const char *line = commit->buffer;
for (;;) {
const char *eol = strchr(line, '\n'), *next;
if (line == eol)
return NULL;
if (!eol) {
eol = line + strlen(line);
next = NULL;
} else
next = eol + 1;
if (eol - line > key_len &&
!strncmp(line, key, key_len) &&
line[key_len] == ' ') {
return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
}
line = next;
}
}
static char *replace_encoding_header(char *buf, const char *encoding)
{
struct strbuf tmp;
size_t start, len;
char *cp = buf;
/* guess if there is an encoding header before a \n\n */
while (strncmp(cp, "encoding ", strlen("encoding "))) {
cp = strchr(cp, '\n');
if (!cp || *++cp == '\n')
return buf;
}
start = cp - buf;
cp = strchr(cp, '\n');
if (!cp)
return buf; /* should not happen but be defensive */
len = cp + 1 - (buf + start);
strbuf_init(&tmp, 0);
strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
if (is_encoding_utf8(encoding)) {
/* we have re-coded to UTF-8; drop the header */
strbuf_remove(&tmp, start, len);
} else {
/* just replaces XXXX in 'encoding XXXX\n' */
strbuf_splice(&tmp, start + strlen("encoding "),
len - strlen("encoding \n"),
encoding, strlen(encoding));
}
return strbuf_detach(&tmp, NULL);
}
static char *logmsg_reencode(const struct commit *commit,
const char *output_encoding)
{
static const char *utf8 = "utf-8";
const char *use_encoding;
char *encoding;
char *out;
if (!*output_encoding)
return NULL;
encoding = get_header(commit, "encoding");
use_encoding = encoding ? encoding : utf8;
if (!strcmp(use_encoding, output_encoding))
if (encoding) /* we'll strip encoding header later */
out = xstrdup(commit->buffer);
else
return NULL; /* nothing to do */
else
out = reencode_string(commit->buffer,
output_encoding, use_encoding);
if (out)
out = replace_encoding_header(out, output_encoding);
free(encoding);
return out;
}
static void fill_person(struct interp *table, const char *msg, int len)
{
int start, end, tz = 0;
unsigned long date;
char *ep;
/* parse name */
for (end = 0; end < len && msg[end] != '<'; end++)
; /* do nothing */
start = end + 1;
while (end > 0 && isspace(msg[end - 1]))
end--;
table[0].value = xmemdupz(msg, end);
if (start >= len)
return;
/* parse email */
for (end = start + 1; end < len && msg[end] != '>'; end++)
; /* do nothing */
if (end >= len)
return;
table[1].value = xmemdupz(msg + start, end - start);
/* parse date */
for (start = end + 1; start < len && isspace(msg[start]); start++)
; /* do nothing */
if (start >= len)
return;
date = strtoul(msg + start, &ep, 10);
if (msg + start == ep)
return;
table[5].value = xmemdupz(msg + start, ep - (msg + start));
/* parse tz */
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
; /* do nothing */
if (start + 1 < len) {
tz = strtoul(msg + start + 1, NULL, 10);
if (msg[start] == '-')
tz = -tz;
}
interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
}
void format_commit_message(const struct commit *commit,
const void *format, struct strbuf *sb)
{
struct interp table[] = {
{ "%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 */
};
enum interp_index {
IHASH = 0, IHASH_ABBREV,
ITREE, ITREE_ABBREV,
IPARENTS, IPARENTS_ABBREV,
IAUTHOR_NAME, IAUTHOR_EMAIL,
IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
ICOMMITTER_NAME, ICOMMITTER_EMAIL,
ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
ICOMMITTER_ISO8601,
IENCODING,
ISUBJECT,
IBODY,
IRED, IGREEN, IBLUE, IRESET_COLOR,
INEWLINE,
ILEFT_RIGHT,
};
struct commit_list *p;
char parents[1024];
unsigned long len;
int i;
enum { HEADER, SUBJECT, BODY } state;
const char *msg = commit->buffer;
if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
die("invalid interp table!");
/* these are independent of the commit */
interp_set_entry(table, IRED, "\033[31m");
interp_set_entry(table, IGREEN, "\033[32m");
interp_set_entry(table, IBLUE, "\033[34m");
interp_set_entry(table, IRESET_COLOR, "\033[m");
interp_set_entry(table, INEWLINE, "\n");
/* these depend on the commit */
if (!commit->object.parsed)
parse_object(commit->object.sha1);
interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
interp_set_entry(table, IHASH_ABBREV,
find_unique_abbrev(commit->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
interp_set_entry(table, ITREE_ABBREV,
find_unique_abbrev(commit->tree->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, ILEFT_RIGHT,
(commit->object.flags & BOUNDARY)
? "-"
: (commit->object.flags & SYMMETRIC_LEFT)
? "<"
: ">");
parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
sha1_to_hex(p->item->object.sha1));
interp_set_entry(table, IPARENTS, parents + 1);
parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
find_unique_abbrev(p->item->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
int eol;
for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
; /* do nothing */
if (state == SUBJECT) {
table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
i = eol;
}
if (i == eol) {
state++;
/* strip empty lines */
while (msg[eol + 1] == '\n')
eol++;
} else if (!prefixcmp(msg + i, "author "))
fill_person(table + IAUTHOR_NAME,
msg + i + 7, eol - i - 7);
else if (!prefixcmp(msg + i, "committer "))
fill_person(table + ICOMMITTER_NAME,
msg + i + 10, eol - i - 10);
else if (!prefixcmp(msg + i, "encoding "))
table[IENCODING].value =
xmemdupz(msg + i + 9, eol - i - 9);
i = eol;
}
if (msg[i])
table[IBODY].value = xstrdup(msg + i);
len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
format, table, ARRAY_SIZE(table));
if (len > strbuf_avail(sb)) {
strbuf_grow(sb, len);
interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
format, table, ARRAY_SIZE(table));
}
strbuf_setlen(sb, sb->len + len);
interp_clear_table(table, ARRAY_SIZE(table));
}
static void pp_header(enum cmit_fmt fmt,
int abbrev,
enum date_mode dmode,
const char *encoding,
const struct commit *commit,
const char **msg_p,
struct strbuf *sb)
{
int parents_shown = 0;
for (;;) {
const char *line = *msg_p;
int linelen = get_one_line(*msg_p);
if (!linelen)
return;
*msg_p += linelen;
if (linelen == 1)
/* End of header */
return;
if (fmt == CMIT_FMT_RAW) {
strbuf_add(sb, line, linelen);
continue;
}
if (!memcmp(line, "parent ", 7)) {
if (linelen != 48)
die("bad parent line in commit");
continue;
}
if (!parents_shown) {
struct commit_list *parent;
int num;
for (parent = commit->parents, num = 0;
parent;
parent = parent->next, num++)
;
/* with enough slop */
strbuf_grow(sb, num * 50 + 20);
add_merge_info(fmt, sb, commit, abbrev);
parents_shown = 1;
}
/*
* MEDIUM == DEFAULT shows only author with dates.
* FULL shows both authors but not dates.
* FULLER shows both authors and dates.
*/
if (!memcmp(line, "author ", 7)) {
strbuf_grow(sb, linelen + 80);
add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
}
if (!memcmp(line, "committer ", 10) &&
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
strbuf_grow(sb, linelen + 80);
add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
}
}
}
static void pp_title_line(enum cmit_fmt fmt,
const char **msg_p,
struct strbuf *sb,
const char *subject,
const char *after_subject,
const char *encoding,
int plain_non_ascii)
{
struct strbuf title;
strbuf_init(&title, 80);
for (;;) {
const char *line = *msg_p;
int linelen = get_one_line(line);
*msg_p += linelen;
if (!linelen || is_empty_line(line, &linelen))
break;
strbuf_grow(&title, linelen + 2);
if (title.len) {
if (fmt == CMIT_FMT_EMAIL) {
strbuf_addch(&title, '\n');
}
strbuf_addch(&title, ' ');
}
strbuf_add(&title, line, linelen);
}
strbuf_grow(sb, title.len + 1024);
if (subject) {
strbuf_addstr(sb, subject);
add_rfc2047(sb, title.buf, title.len, encoding);
} else {
strbuf_addbuf(sb, &title);
}
strbuf_addch(sb, '\n');
if (plain_non_ascii) {
const char *header_fmt =
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=%s\n"
"Content-Transfer-Encoding: 8bit\n";
strbuf_addf(sb, header_fmt, encoding);
}
if (after_subject) {
strbuf_addstr(sb, after_subject);
}
if (fmt == CMIT_FMT_EMAIL) {
strbuf_addch(sb, '\n');
}
strbuf_release(&title);
}
static void pp_remainder(enum cmit_fmt fmt,
const char **msg_p,
struct strbuf *sb,
int indent)
{
int first = 1;
for (;;) {
const char *line = *msg_p;
int linelen = get_one_line(line);
*msg_p += linelen;
if (!linelen)
break;
if (is_empty_line(line, &linelen)) {
if (first)
continue;
if (fmt == CMIT_FMT_SHORT)
break;
}
first = 0;
strbuf_grow(sb, linelen + indent + 20);
if (indent) {
memset(sb->buf + sb->len, ' ', indent);
strbuf_setlen(sb, sb->len + indent);
}
strbuf_add(sb, line, linelen);
strbuf_addch(sb, '\n');
}
}
void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
struct strbuf *sb, int abbrev,
const char *subject, const char *after_subject,
enum date_mode dmode, int plain_non_ascii)
{
unsigned long beginning_of_body;
int indent = 4;
const char *msg = commit->buffer;
char *reencoded;
const char *encoding;
if (fmt == CMIT_FMT_USERFORMAT) {
format_commit_message(commit, user_format, sb);
return;
}
encoding = (git_log_output_encoding
? git_log_output_encoding
: git_commit_encoding);
if (!encoding)
encoding = "utf-8";
reencoded = logmsg_reencode(commit, encoding);
if (reencoded) {
msg = reencoded;
}
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
indent = 0;
/* After-subject is used to pass in Content-Type: multipart
* MIME header; in that case we do not have to do the
* plaintext content type even if the commit message has
* non 7-bit ASCII character. Otherwise, check if we need
* to say this is not a 7-bit ASCII.
*/
if (fmt == CMIT_FMT_EMAIL && !after_subject) {
int i, ch, in_body;
for (in_body = i = 0; (ch = msg[i]); i++) {
if (!in_body) {
/* author could be non 7-bit ASCII but
* the log may be so; skip over the
* header part first.
*/
if (ch == '\n' && msg[i+1] == '\n')
in_body = 1;
}
else if (non_ascii(ch)) {
plain_non_ascii = 1;
break;
}
}
}
pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
if (fmt != CMIT_FMT_ONELINE && !subject) {
strbuf_addch(sb, '\n');
}
/* Skip excess blank lines at the beginning of body, if any... */
for (;;) {
int linelen = get_one_line(msg);
int ll = linelen;
if (!linelen)
break;
if (!is_empty_line(msg, &ll))
break;
msg += linelen;
}
/* These formats treat the title line specially. */
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
pp_title_line(fmt, &msg, sb, subject,
after_subject, encoding, plain_non_ascii);
beginning_of_body = sb->len;
if (fmt != CMIT_FMT_ONELINE)
pp_remainder(fmt, &msg, sb, indent);
strbuf_rtrim(sb);
/* Make sure there is an EOLN for the non-oneline case */
if (fmt != CMIT_FMT_ONELINE)
strbuf_addch(sb, '\n');
/*
* The caller may append additional body text in e-mail
* format. Make sure we did not strip the blank line
* between the header and the body.
*/
if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
strbuf_addch(sb, '\n');
free(reencoded);
}

View File

@ -26,7 +26,7 @@ void sq_quote_buf(struct strbuf *dst, const char *src)
strbuf_addch(dst, '\''); strbuf_addch(dst, '\'');
while (*src) { while (*src) {
size_t len = strcspn(src, "'\\"); size_t len = strcspn(src, "'!");
strbuf_add(dst, src, len); strbuf_add(dst, src, len);
src += len; src += len;
while (need_bs_quote(*src)) { while (need_bs_quote(*src)) {
@ -131,7 +131,8 @@ static signed char const sq_lookup[256] = {
/* 0x80 */ /* set to 0 */ /* 0x80 */ /* set to 0 */
}; };
static inline int sq_must_quote(char c) { static inline int sq_must_quote(char c)
{
return sq_lookup[(unsigned char)c] + quote_path_fully > 0; return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
} }

10
setup.c
View File

@ -206,6 +206,16 @@ static const char *set_work_tree(const char *dir)
return NULL; return NULL;
} }
void setup_work_tree(void)
{
const char *work_tree = get_git_work_tree();
const char *git_dir = get_git_dir();
if (!is_absolute_path(git_dir))
set_git_dir(make_absolute_path(git_dir));
if (!work_tree || chdir(work_tree))
die("This operation must be run in a work tree");
}
/* /*
* We cannot decide in this function whether we are in the work tree or * We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called. * not, since the config can only be read _after_ this function was called.

View File

@ -23,12 +23,12 @@
* that way: * that way:
* *
* strbuf_grow(sb, SOME_SIZE); * strbuf_grow(sb, SOME_SIZE);
* // ... here the memory areay starting at sb->buf, and of length * ... Here, the memory array starting at sb->buf, and of length
* // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at * ... strbuf_avail(sb) is all yours, and you are sure that
* // least SOME_SIZE * ... strbuf_avail(sb) is at least SOME_SIZE.
* strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE);
* *
* Of course, SOME_OTHER_SIZE must be smaller or equal to sb_avail(sb). * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb).
* *
* Doing so is safe, though if it has to be done in many places, adding the * Doing so is safe, though if it has to be done in many places, adding the
* missing API to the strbuf module is the way to go. * missing API to the strbuf module is the way to go.

View File

@ -205,7 +205,7 @@ test_expect_success \
echo $h_TEST >.git/MERGE_HEAD && echo $h_TEST >.git/MERGE_HEAD &&
GIT_AUTHOR_DATE="2005-05-26 23:45" \ GIT_AUTHOR_DATE="2005-05-26 23:45" \
GIT_COMMITTER_DATE="2005-05-26 23:45" git-commit -F M && GIT_COMMITTER_DATE="2005-05-26 23:45" git-commit -F M &&
h_MERGED=$(git rev-parse --verify HEAD) h_MERGED=$(git rev-parse --verify HEAD) &&
rm -f M' rm -f M'
cat >expect <<EOF cat >expect <<EOF

123
t/t3502-cherry-pick-merge.sh Executable file
View File

@ -0,0 +1,123 @@
#!/bin/sh
test_description='cherry picking and reverting a merge
b---c
/ /
initial---a
'
. ./test-lib.sh
test_expect_success setup '
>A &&
>B &&
git add A B &&
git commit -m "Initial" &&
git tag initial &&
git branch side &&
echo new line >A &&
git commit -m "add line to A" A &&
git tag a &&
git checkout side &&
echo new line >B &&
git commit -m "add line to B" B &&
git tag b &&
git checkout master &&
git merge side &&
git tag c
'
test_expect_success 'cherry-pick a non-merge with -m should fail' '
git reset --hard &&
git checkout a^0 &&
! git cherry-pick -m 1 b &&
git diff --exit-code a --
'
test_expect_success 'cherry pick a merge without -m should fail' '
git reset --hard &&
git checkout a^0 &&
! git cherry-pick c &&
git diff --exit-code a --
'
test_expect_success 'cherry pick a merge (1)' '
git reset --hard &&
git checkout a^0 &&
git cherry-pick -m 1 c &&
git diff --exit-code c
'
test_expect_success 'cherry pick a merge (2)' '
git reset --hard &&
git checkout b^0 &&
git cherry-pick -m 2 c &&
git diff --exit-code c
'
test_expect_success 'cherry pick a merge relative to nonexistent parent should fail' '
git reset --hard &&
git checkout b^0 &&
! git cherry-pick -m 3 c
'
test_expect_success 'revert a non-merge with -m should fail' '
git reset --hard &&
git checkout c^0 &&
! git revert -m 1 b &&
git diff --exit-code c
'
test_expect_success 'revert a merge without -m should fail' '
git reset --hard &&
git checkout c^0 &&
! git revert c &&
git diff --exit-code c
'
test_expect_success 'revert a merge (1)' '
git reset --hard &&
git checkout c^0 &&
git revert -m 1 c &&
git diff --exit-code a --
'
test_expect_success 'revert a merge (2)' '
git reset --hard &&
git checkout c^0 &&
git revert -m 2 c &&
git diff --exit-code b --
'
test_expect_success 'revert a merge relative to nonexistent parent should fail' '
git reset --hard &&
git checkout c^0 &&
! git revert -m 3 c &&
git diff --exit-code c
'
test_done

View File

@ -0,0 +1,42 @@
#!/bin/sh
test_description='format-patch -s should force MIME encoding as needed'
. ./test-lib.sh
test_expect_success setup '
>F &&
git add F &&
git commit -m initial &&
echo new line >F &&
test_tick &&
git commit -m "This adds some lines to F" F
'
test_expect_success 'format normally' '
git format-patch --stdout -1 >output &&
! grep Content-Type output
'
test_expect_success 'format with signoff without funny signer name' '
git format-patch -s --stdout -1 >output &&
! grep Content-Type output
'
test_expect_success 'format with non ASCII signer name' '
GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(B" \
git format-patch -s --stdout -1 >output &&
grep Content-Type output
'
test_done

View File

@ -1,3 +1,6 @@
From nobody Mon Sep 17 00:00:00 2001 From nobody Mon Sep 17 00:00:00 2001
From: A U Thor <a.u.thor@example.com> From: A U Thor <a.u.thor@example.com>
Date: Fri, 9 Jun 2006 00:44:16 -0700 Date: Fri, 9 Jun 2006 00:44:16 -0700

View File

@ -208,4 +208,11 @@ test_expect_success 'fetch with a non-applying branch.<name>.merge' '
git fetch blub git fetch blub
' '
# the strange name is: a\!'b
test_expect_success 'quoting of a strangely named repo' '
! git fetch "a\\!'\''b" > result 2>&1 &&
cat result &&
grep "fatal: '\''a\\\\!'\''b'\''" result
'
test_done test_done

View File

@ -402,4 +402,11 @@ test_expect_success 'test resetting the index at give paths' '
' '
test_expect_success 'resetting an unmodified path is a no-op' '
git reset --hard &&
git reset -- file1 &&
git diff-files --exit-code &&
git diff-index --cached --exit-code HEAD
'
test_done test_done

View File

@ -77,7 +77,7 @@ test_expect_success "checkout with dirty tree without -m" '
test_expect_success "checkout -m with dirty tree" ' test_expect_success "checkout -m with dirty tree" '
git checkout -f master && git checkout -f master &&
git clean && git clean -f &&
fill 0 1 2 3 4 5 6 7 8 >one && fill 0 1 2 3 4 5 6 7 8 >one &&
git checkout -m side && git checkout -m side &&
@ -99,7 +99,7 @@ test_expect_success "checkout -m with dirty tree" '
test_expect_success "checkout -m with dirty tree, renamed" ' test_expect_success "checkout -m with dirty tree, renamed" '
git checkout -f master && git clean && git checkout -f master && git clean -f &&
fill 1 2 3 4 5 7 8 >one && fill 1 2 3 4 5 7 8 >one &&
if git checkout renamer if git checkout renamer
@ -121,7 +121,7 @@ test_expect_success "checkout -m with dirty tree, renamed" '
test_expect_success 'checkout -m with merge conflict' ' test_expect_success 'checkout -m with merge conflict' '
git checkout -f master && git clean && git checkout -f master && git clean -f &&
fill 1 T 3 4 5 6 S 8 >one && fill 1 T 3 4 5 6 S 8 >one &&
if git checkout renamer if git checkout renamer
@ -144,7 +144,7 @@ test_expect_success 'checkout -m with merge conflict' '
test_expect_success 'checkout to detach HEAD' ' test_expect_success 'checkout to detach HEAD' '
git checkout -f renamer && git clean && git checkout -f renamer && git clean -f &&
git checkout renamer^ && git checkout renamer^ &&
H=$(git rev-parse --verify HEAD) && H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) && M=$(git show-ref -s --verify refs/heads/master) &&
@ -160,7 +160,7 @@ test_expect_success 'checkout to detach HEAD' '
test_expect_success 'checkout to detach HEAD with branchname^' ' test_expect_success 'checkout to detach HEAD with branchname^' '
git checkout -f master && git clean && git checkout -f master && git clean -f &&
git checkout renamer^ && git checkout renamer^ &&
H=$(git rev-parse --verify HEAD) && H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) && M=$(git show-ref -s --verify refs/heads/master) &&
@ -176,7 +176,7 @@ test_expect_success 'checkout to detach HEAD with branchname^' '
test_expect_success 'checkout to detach HEAD with HEAD^0' ' test_expect_success 'checkout to detach HEAD with HEAD^0' '
git checkout -f master && git clean && git checkout -f master && git clean -f &&
git checkout HEAD^0 && git checkout HEAD^0 &&
H=$(git rev-parse --verify HEAD) && H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) && M=$(git show-ref -s --verify refs/heads/master) &&

View File

@ -7,6 +7,8 @@ test_description='git-clean basic tests'
. ./test-lib.sh . ./test-lib.sh
git config clean.requireForce no
test_expect_success 'setup' ' test_expect_success 'setup' '
mkdir -p src && mkdir -p src &&
@ -37,6 +39,93 @@ test_expect_success 'git-clean' '
' '
test_expect_success 'git-clean src/' '
mkdir -p build docs &&
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
git-clean src/ &&
test -f Makefile &&
test -f README &&
test -f src/part1.c &&
test -f src/part2.c &&
test -f a.out &&
test ! -f src/part3.c &&
test -f docs/manual.txt &&
test -f obj.o &&
test -f build/lib.so
'
test_expect_success 'git-clean src/ src/' '
mkdir -p build docs &&
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
git-clean src/ src/ &&
test -f Makefile &&
test -f README &&
test -f src/part1.c &&
test -f src/part2.c &&
test -f a.out &&
test ! -f src/part3.c &&
test -f docs/manual.txt &&
test -f obj.o &&
test -f build/lib.so
'
test_expect_success 'git-clean with prefix' '
mkdir -p build docs &&
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
(cd src/ && git-clean) &&
test -f Makefile &&
test -f README &&
test -f src/part1.c &&
test -f src/part2.c &&
test -f a.out &&
test ! -f src/part3.c &&
test -f docs/manual.txt &&
test -f obj.o &&
test -f build/lib.so
'
test_expect_success 'git-clean -d with prefix and path' '
mkdir -p build docs src/feature &&
touch a.out src/part3.c src/feature/file.c docs/manual.txt obj.o build/lib.so &&
(cd src/ && git-clean -d feature/) &&
test -f Makefile &&
test -f README &&
test -f src/part1.c &&
test -f src/part2.c &&
test -f a.out &&
test -f src/part3.c &&
test ! -f src/feature/file.c &&
test -f docs/manual.txt &&
test -f obj.o &&
test -f build/lib.so
'
test_expect_success 'git-clean symbolic link' '
mkdir -p build docs &&
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
ln -s docs/manual.txt src/part4.c
git-clean &&
test -f Makefile &&
test -f README &&
test -f src/part1.c &&
test -f src/part2.c &&
test ! -f a.out &&
test ! -f src/part3.c &&
test ! -f src/part4.c &&
test -f docs/manual.txt &&
test -f obj.o &&
test -f build/lib.so
'
test_expect_success 'git-clean -n' ' test_expect_success 'git-clean -n' '
mkdir -p build docs && mkdir -p build docs &&
@ -71,6 +160,24 @@ test_expect_success 'git-clean -d' '
' '
test_expect_success 'git-clean -d src/ examples/' '
mkdir -p build docs examples &&
touch a.out src/part3.c docs/manual.txt obj.o build/lib.so examples/1.c &&
git-clean -d src/ examples/ &&
test -f Makefile &&
test -f README &&
test -f src/part1.c &&
test -f src/part2.c &&
test -f a.out &&
test ! -f src/part3.c &&
test ! -f examples/1.c &&
test -f docs/manual.txt &&
test -f obj.o &&
test -f build/lib.so
'
test_expect_success 'git-clean -x' ' test_expect_success 'git-clean -x' '
mkdir -p build docs && mkdir -p build docs &&
@ -139,6 +246,13 @@ test_expect_success 'git-clean -d -X' '
' '
test_expect_success 'clean.requireForce defaults to true' '
git config --unset clean.requireForce &&
! git-clean
'
test_expect_success 'clean.requireForce' ' test_expect_success 'clean.requireForce' '
git config clean.requireForce true && git config clean.requireForce true &&

View File

@ -0,0 +1,56 @@
#!/bin/sh
#
# Copyright (c) 2007 Eric Wong
test_description='git-svn dcommit clobber series'
. ./lib-git-svn.sh
test_expect_success 'initialize repo' "
mkdir import &&
cd import &&
awk 'BEGIN { for (i = 1; i < 64; i++) { print i } }' > file
svn import -m 'initial' . $svnrepo &&
cd .. &&
git svn init $svnrepo &&
git svn fetch &&
test -e file
"
test_expect_success '(supposedly) non-conflicting change from SVN' "
test x\"\`sed -n -e 58p < file\`\" = x58 &&
test x\"\`sed -n -e 61p < file\`\" = x61 &&
svn co $svnrepo tmp &&
cd tmp &&
perl -i -p -e 's/^58\$/5588/' file &&
perl -i -p -e 's/^61\$/6611/' file &&
test x\"\`sed -n -e 58p < file\`\" = x5588 &&
test x\"\`sed -n -e 61p < file\`\" = x6611 &&
svn commit -m '58 => 5588, 61 => 6611' &&
cd ..
"
test_expect_success 'some unrelated changes to git' "
echo hi > life &&
git update-index --add life &&
git commit -m hi-life &&
echo bye >> life &&
git commit -m bye-life life
"
test_expect_success 'change file but in unrelated area' "
test x\"\`sed -n -e 4p < file\`\" = x4 &&
test x\"\`sed -n -e 7p < file\`\" = x7 &&
perl -i -p -e 's/^4\$/4444/' file &&
perl -i -p -e 's/^7\$/7777/' file &&
test x\"\`sed -n -e 4p < file\`\" = x4444 &&
test x\"\`sed -n -e 7p < file\`\" = x7777 &&
git commit -m '4 => 4444, 7 => 7777' file &&
git svn dcommit &&
svn up tmp &&
cd tmp &&
test x\"\`sed -n -e 4p < file\`\" = x4444 &&
test x\"\`sed -n -e 7p < file\`\" = x7777 &&
test x\"\`sed -n -e 58p < file\`\" = x5588 &&
test x\"\`sed -n -e 61p < file\`\" = x6611
"
test_done

View File

@ -86,4 +86,9 @@ test_expect_success 'verify post-merge ancestry' "
git cat-file commit refs/heads/svn^ | grep '^friend$' git cat-file commit refs/heads/svn^ | grep '^friend$'
" "
test_expect_success 'verify merge commit message' "
git rev-list --pretty=raw -1 refs/heads/svn | \
grep \" Merge branch 'merge' into svn\"
"
test_done test_done

View File

@ -31,7 +31,6 @@ our \$projects_list = "";
our \$export_ok = ""; our \$export_ok = "";
our \$strict_export = ""; our \$strict_export = "";
CGI::Carp::set_programname("gitweb/gitweb.cgi");
EOF EOF
cat >.git/description <<EOF cat >.git/description <<EOF
@ -558,4 +557,27 @@ test_expect_success \
'gitweb_run "p=.git;a=tree;opt=--no-merges"' 'gitweb_run "p=.git;a=tree;opt=--no-merges"'
test_debug 'cat gitweb.log' test_debug 'cat gitweb.log'
# ----------------------------------------------------------------------
# gitweb config and repo config
cat >>gitweb_config.perl <<EOF
\$feature{'blame'}{'override'} = 1;
\$feature{'snapshot'}{'override'} = 1;
EOF
test_expect_success \
'config override: tree view, features disabled in repo config' \
'git config gitweb.blame no &&
git config gitweb.snapshot none &&
gitweb_run "p=.git;a=tree"'
test_debug 'cat gitweb.log'
test_expect_success \
'config override: tree view, features enabled in repo config' \
'git config gitweb.blame yes &&
git config gitweb.snapshot "zip,tgz, tbz2" &&
gitweb_run "p=.git;a=tree"'
test_debug 'cat gitweb.log'
test_done test_done

View File

@ -10,6 +10,12 @@
# hooks.allowunannotated # hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the # This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be. # repository. By default they won't be.
# hooks.allowdeletetag
# This boolean sets whether deleting tags will be allowed in the
# repository. By default they won't be.
# hooks.allowdeletebranch
# This boolean sets whether deleting branches will be allowed in the
# repository. By default they won't be.
# #
# --- Command line # --- Command line
@ -32,18 +38,20 @@ fi
# --- Config # --- Config
allowunannotated=$(git-repo-config --bool hooks.allowunannotated) allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
allowdeletebranch=$(git-repo-config --bool hooks.allowdeletebranch)
allowdeletetag=$(git-repo-config --bool hooks.allowdeletetag)
# check for no description # check for no description
projectdesc=$(sed -e '1p' "$GIT_DIR/description") projectdesc=$(sed -e '1q' "$GIT_DIR/description")
if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then
echo "*** Project description file hasn't been set" >&2 echo "*** Project description file hasn't been set" >&2
exit 1 exit 1
fi fi
# --- Check types # --- Check types
# if $newrev is 0000...0000, it's a commit to delete a branch # if $newrev is 0000...0000, it's a commit to delete a ref.
if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
newrev_type=commit newrev_type=delete
else else
newrev_type=$(git-cat-file -t $newrev) newrev_type=$(git-cat-file -t $newrev)
fi fi
@ -58,15 +66,36 @@ case "$refname","$newrev_type" in
exit 1 exit 1
fi fi
;; ;;
refs/tags/*,delete)
# delete tag
if [ "$allowdeletetag" != "true" ]; then
echo "*** Deleting a tag is not allowed in this repository" >&2
exit 1
fi
;;
refs/tags/*,tag) refs/tags/*,tag)
# annotated tag # annotated tag
;; ;;
refs/heads/*,commit) refs/heads/*,commit)
# branch # branch
;; ;;
refs/heads/*,delete)
# delete branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/remotes/*,commit) refs/remotes/*,commit)
# tracking branch # tracking branch
;; ;;
refs/remotes/*,delete)
# delete tracking branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
exit 1
fi
;;
*) *)
# Anything else (is there anything else?) # Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2

View File

@ -381,7 +381,8 @@ static int disconnect_walker(struct transport *transport)
} }
#ifndef NO_CURL #ifndef NO_CURL
static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
{
const char **argv; const char **argv;
int argc; int argc;
int err; int err;
@ -647,7 +648,8 @@ static int fetch_refs_via_pack(struct transport *transport,
return 0; return 0;
} }
static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
{
struct git_transport_data *data = transport->data; struct git_transport_data *data = transport->data;
struct send_pack_args args; struct send_pack_args args;

3
utf8.c
View File

@ -11,7 +11,8 @@ struct interval {
}; };
/* auxiliary function for binary search in interval table */ /* auxiliary function for binary search in interval table */
static int bisearch(ucs_char_t ucs, const struct interval *table, int max) { static int bisearch(ucs_char_t ucs, const struct interval *table, int max)
{
int min = 0; int min = 0;
int mid; int mid;