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.
* post-receive-email example hook fixed was fixed for
non-fast-forward updates.
* post-receive-email example hook was fixed for non-fast-forward
updates.
* Documentation updates for supported (but previously undocumented)
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
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.

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.
* 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.
@ -46,6 +49,28 @@ Updates since v1.5.3
* 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
------------------
@ -54,6 +79,6 @@ this release, unless otherwise noted.
--
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`
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:
- 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 attach your patch, but read in the mail
body, unless you cannot teach your mailer to
@ -31,13 +28,15 @@ Checklist (and a short version for the impatient):
corrupt whitespaces.
- provide additional information (which is unsuitable for
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
make some other user interface change, the associated
documentation should be updated as well.
- if your name is not writable in ASCII, make sure that
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:

View file

@ -345,8 +345,8 @@ branch.<name>.mergeoptions::
supported.
clean.requireForce::
A boolean to make git-clean do nothing unless given -f or -n. Defaults
to false.
A boolean to make git-clean do nothing unless given -f
or -n. Defaults to true.
color.branch::
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
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::
The default merge strategy to use when pulling multiple branches
at once.

View file

@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit
SYNOPSIS
--------
'git-cherry-pick' [--edit] [-n] [-x] <commit>
'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>
DESCRIPTION
-----------
@ -44,6 +44,13 @@ OPTIONS
described above, and `-r` was to disable it. Now the
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::
Usually the command automatically creates a commit with
a commit log message stating which commit was

View file

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

View file

@ -11,6 +11,10 @@ SYNOPSIS
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
creates refs to them in the .git/lost-found/ directory. Commits and
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
--------
[verse]
'git-reset' [--mixed | --soft | --hard] [<commit>]
'git-reset' [--mixed] <commit> [--] <paths>...
'git-reset' [--mixed | --soft | --hard] [-q] [<commit>]
'git-reset' [--mixed] [-q] <commit> [--] <paths>...
DESCRIPTION
-----------
@ -45,6 +45,9 @@ OPTIONS
switched to. Any changes to tracked files in the working tree
since <commit> are lost.
-q::
Be quiet, only report errors.
<commit>::
Commit to make the current HEAD.

View file

@ -7,7 +7,7 @@ git-revert - Revert an existing commit
SYNOPSIS
--------
'git-revert' [--edit | --no-edit] [-n] <commit>
'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>
DESCRIPTION
-----------
@ -27,6 +27,13 @@ OPTIONS
message prior committing the revert. This is the default if
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::
With this option, `git-revert` will not start the commit
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.
--suppress-from, --no-suppress-from::
If this is set, do not add the From: address to the cc: list, if it
shows up in a From: line.
If this is set, do not add the From: address to the cc: list.
Default is the value of 'sendemail.suppressfrom' configuration value;
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
# (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"
# that tells runtime paths to dynamic libraries;
# "-Wl,-rpath=/path/lib" is used instead.
@ -298,7 +300,7 @@ DIFF_OBJS = \
LIB_OBJS = \
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 \
interpolate.o hash.o \
pretty.o interpolate.o hash.o \
lockfile.o \
patch-ids.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
endif
ifdef NO_DEFLATE_BOUND
BASIC_CFLAGS += -DNO_DEFLATE_BOUND
endif
ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h"
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)
$(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)
$(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;
}
if (!incremental)
setup_pager();
if (!blame_move_score)
blame_move_score = BLAME_DEFAULT_MOVE_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
* or "--contents".
*/
setup_work_tree();
sb.final = fake_working_tree_commit(path, contents_from);
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);
if (!incremental)
setup_pager();
assign_blame(&sb, &revs, opt);
if (incremental)

View file

@ -148,7 +148,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
if (!force &&
!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"
"If you are sure you want to delete it, "
"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);
if (commit && !parse_commit(commit)) {
pretty_print_commit(CMIT_FMT_ONELINE, commit,
&subject, 0, NULL, NULL, 0);
&subject, 0, NULL, NULL, 0, 0);
sub = subject.buf;
}
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();
read_cache();
refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
if (active_cache_changed) {
if (write_cache(fd, active_cache, active_nr) ||
close(fd) ||
commit_locked_index(lock_file))
; /*
* silently ignore it -- we haven't mucked
* with the real index.
*/
}
if (active_cache_changed &&
!write_cache(fd, active_cache, active_nr) && !close(fd))
commit_locked_index(lock_file);
rollback_lock_file(lock_file);
}

View file

@ -32,7 +32,7 @@ static const char fetch_pack_usage[] =
#define MAX_IN_VAIN 256
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)
{
@ -178,7 +178,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
(multi_ack ? " multi_ack" : ""),
(use_sideband == 2 ? " side-band-64k" : ""),
(use_sideband == 1 ? " side-band" : ""),
(use_thin_pack ? " thin-pack" : ""),
(args.use_thin_pack ? " thin-pack" : ""),
(args.no_progress ? " no-progress" : ""),
" ofs-delta");
else

View file

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

View file

@ -175,7 +175,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
char buf[80];
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, "auto", &auto_gc, "enable auto-gc mode"),
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);
}
argv[argc++] = name;
if (argc < MAXARGS && !ce_stage(ce))
if (argc < MAXARGS)
continue;
status = flush_grep(opt, argc, nr, argv, &kept);
if (0 < status)

View file

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

View file

@ -525,11 +525,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
break;
}
if (require_work_tree && !is_inside_work_tree()) {
const char *work_tree = get_git_work_tree();
if (!work_tree || chdir(work_tree))
die("This operation must be run in a work tree");
}
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
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,
const char *msg, const char *patch)
{
int peek;
keep_subject = ks;
metainfo_charset = encoding;
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 *));
s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));
do {
peek = fgetc(in);
} while (isspace(peek));
ungetc(peek, in);
/* process the email header */
while (read_one_header_line(line, sizeof(line), fin))
check_header(line, sizeof(line), p_hdr_data, 1);

View file

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

View file

@ -57,7 +57,7 @@ struct object_entry {
* nice "minimum seek" order.
*/
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 int non_empty;
@ -577,7 +577,7 @@ static off_t write_one(struct sha1file *f,
e->idx.offset = 0;
return 0;
}
written_list[nr_written++] = e;
written_list[nr_written++] = &e->idx;
/* make sure off_t is sufficiently large not to wrap */
if (offset > offset + size)
@ -599,7 +599,7 @@ static void write_pack_file(void)
if (do_progress)
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 {
unsigned char sha1[20];
@ -651,9 +651,8 @@ static void write_pack_file(void)
umask(mode);
mode = 0444 & ~mode;
idx_tmp_name = write_idx_file(NULL,
(struct pack_idx_entry **) written_list,
nr_written, sha1);
idx_tmp_name = write_idx_file(NULL, written_list,
nr_written, sha1);
snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
base_name, sha1_to_hex(sha1));
if (adjust_perm(pack_tmp_name, mode))
@ -677,7 +676,7 @@ static void write_pack_file(void)
/* mark written objects as written to previous pack */
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;
} while (nr_remaining && i < nr_objects);
@ -1768,6 +1767,12 @@ static int git_pack_config(const char *k, const char *v)
#endif
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);
}

View file

@ -7,8 +7,12 @@
#include "builtin.h"
#include "remote.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 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 i;
int flags = 0;
int all = 0;
int dry_run = 0;
int force = 0;
int tags = 0;
const char *repo = NULL; /* default repository */
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
struct option options[] = {
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] != '-') {
repo = arg;
i++;
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;
continue;
}
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);
argc = parse_options(argc, argv, options, push_usage, 0);
if (force)
flags |= TRANSPORT_PUSH_FORCE;
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);
}
set_refspecs(argv + i, argc - i);
if ((flags & TRANSPORT_PUSH_ALL) && refspec)
usage(push_usage);
usage_with_options(push_usage, options);
return do_push(repo, flags);
}

View file

@ -18,7 +18,7 @@
#include "tree.h"
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)
{
@ -113,10 +113,17 @@ static int update_index_refresh(void)
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,
struct diff_options *opt, void *data)
{
int i;
struct update_cb_data *cb = data;
/* do_diff_cache() mangled the index */
discard_cache();
@ -133,29 +140,34 @@ static void update_index_from_diff(struct diff_queue_struct *q,
} else
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,
unsigned char *tree_sha1)
{
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
struct diff_options opt;
struct update_cb_data cb;
memset(&opt, 0, sizeof(opt));
diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
opt.output_format = DIFF_FORMAT_CALLBACK;
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();
if (do_diff_cache(tree_sha1, &opt))
return 1;
diffcore_std(&opt);
diff_flush(&opt);
return write_cache(index_fd, active_cache, active_nr) ||
close(index_fd) ||
commit_locked_index(lock);
return cb.exit_code;
}
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 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";
unsigned char sha1[20], *orig = NULL, sha1_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);
setenv("GIT_REFLOG_ACTION", reflog_action, 0);
if (i < argc) {
while (i < argc) {
if (!strcmp(argv[i], "--mixed")) {
reset_type = MIXED;
i++;
@ -198,6 +210,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
reset_type = HARD;
i++;
}
else if (!strcmp(argv[i], "-q")) {
quiet = 1;
i++;
}
else
break;
}
if (i < argc && argv[i][0] != '-')
@ -258,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
switch (reset_type) {
case HARD:
if (!update_ref_status)
if (!update_ref_status && !quiet)
print_new_head_line(commit);
break;
case SOFT: /* Nothing else to do. */

View file

@ -86,7 +86,8 @@ static void show_commit(struct commit *commit)
struct strbuf buf;
strbuf_init(&buf, 0);
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)
printf("%s%c", buf.buf, hdr_termination);
strbuf_release(&buf);

View file

@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = {
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 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('x', NULL, &no_replay, "append commit name when cherry-picking"),
OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
OPT_END(),
};
if (parse_options(argc, argv, options, usage_str, 0) != 1)
usage_with_options(usage_str, options);
arg = argv[0];
if (get_sha1(arg, sha1))
die ("Cannot find '%s'", arg);
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)
{
unsigned char head[20];
struct commit *base, *next;
struct commit *base, *next, *parent;
int i;
char *oneline, *reencoded_message = NULL;
const char *message, *encoding;
@ -261,8 +263,29 @@ static int revert_or_cherry_pick(int argc, const char **argv)
if (!commit->parents)
die ("Cannot %s a root commit", me);
if (commit->parents->next)
die ("Cannot %s a multi-parent commit.", me);
if (commit->parents->next) {
/* 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))
die ("Cannot get commit message for %s",
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, ' ');
base = commit;
next = commit->parents->item;
next = parent;
add_to_msg("Revert \"");
add_to_msg(oneline_body + 1);
add_to_msg("\"\n\nThis reverts commit ");
add_to_msg(sha1_to_hex(commit->object.sha1));
add_to_msg(".\n");
} else {
base = commit->parents->item;
base = parent;
next = commit;
set_author_ident_env(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)
usage_with_options(builtin_rm_usage, builtin_rm_options);
if (!index_only)
setup_work_tree();
pathspec = get_pathspec(prefix, argv);
seen = NULL;
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);
if (commit->object.parsed) {
pretty_print_commit(CMIT_FMT_ONELINE, commit,
&pretty, 0, NULL, NULL, 0);
&pretty, 0, NULL, NULL, 0, 0);
pretty_str = pretty.buf;
}
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);
sp = buf = read_sha1_file(sha1, &type, &size);
if (!buf)
buf = read_sha1_file(sha1, &type, &size);
if (!buf || !size)
return 0;
if (!size) {
/* skip header */
sp = strstr(buf, "\n\n");
if (!sp) {
free(buf);
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 */
for (i = 0, sp += 2;
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 */
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];
int fd;
long fpos;

View file

@ -7,7 +7,7 @@
#include SHA1_HEADER
#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)
#endif
@ -222,6 +222,7 @@ extern const char *get_git_work_tree(void);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
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(void);
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 "pkt-line.h"
#include "utf8.h"
#include "interpolate.h"
#include "diff.h"
#include "revision.h"
@ -27,46 +26,6 @@ struct sort_node
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,
const unsigned char *sha1,
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_list *top = *stack;

View file

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

View file

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

View file

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

View file

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

View file

@ -73,7 +73,7 @@ fi \
AC_ARG_WITH([lib],
[AS_HELP_STRING([--with-lib=ARG],
[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]); \
else \
GIT_CONF_APPEND_LINE(lib=$withval); \
@ -182,6 +182,26 @@ AC_SUBST(NEEDS_LIBICONV)
AC_SUBST(NO_ICONV)
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,
# Patrick Mauritz).
AC_CHECK_LIB([c], [socket],
@ -245,9 +265,9 @@ AC_RUN_IFELSE(
[AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
[[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)
exit(1);
return 1;
else if (strcmp(buf, "12345"))
exit(2);]])],
return 2;]])],
[ac_cv_c_c99_format=yes],
[ac_cv_c_c99_format=no])
])

View file

@ -71,6 +71,79 @@ def isP4Exec(kind):
a plus sign, it is also executable"""
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'):
cmd = "p4 -G %s" % cmd
if verbose:
@ -494,18 +567,23 @@ class P4Submit(Command):
else:
print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
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()
filesToDelete = set()
editedFiles = set()
filesToChangeExecBit = {}
for line in diff:
modifier = line[0]
path = line[1:].strip()
diff = parseDiffTreeEntry(line)
modifier = diff['status']
path = diff['src']
if modifier == "M":
system("p4 edit \"%s\"" % path)
if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
filesToChangeExecBit[path] = diff['dst_mode']
editedFiles.add(path)
elif modifier == "A":
filesToAdd.add(path)
filesToChangeExecBit[path] = diff['dst_mode']
if path in filesToDelete:
filesToDelete.remove(path)
elif modifier == "D":
@ -513,9 +591,11 @@ class P4Submit(Command):
if path in filesToAdd:
filesToAdd.remove(path)
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 edit \"%s\"" % (dest))
if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
filesToChangeExecBit[dest] = diff['dst_mode']
os.unlink(dest)
editedFiles.add(dest)
filesToDelete.add(src)
@ -568,6 +648,11 @@ class P4Submit(Command):
system("p4 revert \"%s\"" % f)
system("p4 delete \"%s\"" % f)
# Set/clear executable bits
for f in filesToChangeExecBit.keys():
mode = filesToChangeExecBit[f]
setP4ExecBit(f, mode)
logMessage = ""
if not self.directSubmit:
logMessage = extractLogMessageFromGitCommit(id)

View file

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

View file

@ -406,7 +406,8 @@ static struct daemon_service daemon_service[] = {
{ "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;
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
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);
}
static void make_service_overridable(const char *name, int ena) {
static void make_service_overridable(const char *name, int ena)
{
int i;
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
if (!strcmp(daemon_service[i].name, name)) {
@ -540,7 +542,7 @@ static int execute(struct sockaddr *addr)
if (addr->sa_family == AF_INET) {
struct sockaddr_in *sin_addr = (void *) addr;
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
} else if (addr && addr->sa_family == AF_INET6) {
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);
strcat(buf, "]");
port = sin6_addr->sin6_port;
port = ntohs(sin6_addr->sin6_port);
#endif
}
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;
}
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;
ent = xmalloc(sizeof(*ent) + len + 1);

View file

@ -275,7 +275,8 @@ exit_if_skipped_commits () {
if expr "$_tried" : ".*[|].*" > /dev/null ; then
echo "There are only 'skip'ped commit left to test."
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!"
exit 2
fi

View file

@ -20,12 +20,16 @@ require_work_tree
ignored=
ignoredonly=
cleandir=
disabled="`git config --bool clean.requireForce`"
rmf="rm -f --"
rmrf="rm -rf --"
rm_refuse="echo Not removing"
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
do
case "$1" in

View file

@ -14,7 +14,7 @@ die() {
}
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() {
@ -160,6 +160,9 @@ while
*,--depth)
shift
depth="--depth=$1";;
*,--)
shift
break ;;
*,-*) usage ;;
*) break ;;
esac

View file

@ -381,4 +381,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result)
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

View file

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

View file

@ -15,7 +15,7 @@ browser="`git config --get instaweb.browser`"
port=`git config --get instaweb.port`
module_path="`git config --get instaweb.modulepath`"
conf=$GIT_DIR/gitweb/httpd.conf
conf="$GIT_DIR/gitweb/httpd.conf"
# Defaults:
@ -32,7 +32,7 @@ start_httpd () {
httpd_only="`echo $httpd | cut -f1 -d' '`"
if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
then
$httpd $fqgitdir/gitweb/httpd.conf
$httpd "$fqgitdir/gitweb/httpd.conf"
else
# many httpds are installed in /usr/sbin or /usr/local/sbin
# these days and those are not in most users $PATHs
@ -185,14 +185,14 @@ server.pid-file = "$fqgitdir/pid"
cgi.assign = ( ".cgi" => "" )
mimetype.assign = ( ".css" => "text/css" )
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 () {
test -z "$module_path" && module_path=/usr/lib/apache2/modules
mkdir -p "$GIT_DIR/gitweb/logs"
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
cat > "$conf" <<EOF
ServerName "git-instaweb"
@ -245,7 +245,7 @@ EOF
}
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\) $projects_list =.*#\1 $projects_list = $projectroot;#
s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#'
@ -265,8 +265,8 @@ gitweb_css () {
EOFGITWEB
}
gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi
gitweb_css $GIT_DIR/gitweb/gitweb.css
gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
gitweb_css "$GIT_DIR/gitweb/gitweb.css"
case "$httpd" in
*lighttpd*)
@ -285,6 +285,5 @@ webrick)
esac
start_httpd
test -z "$browser" && browser=echo
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'
. git-sh-setup
echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2
if [ "$#" != "0" ]
then
usage

View file

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

View file

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

View file

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

View file

@ -73,7 +73,7 @@ resolve_relative_url ()
module_name()
{
# 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 \
git config --get-regexp '^submodule\..*\.path$' |
sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )

View file

@ -252,7 +252,7 @@ sub usage {
next if $cmd && $cmd ne $_;
next if /^multi-/; # don't show deprecated commands
print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n";
foreach (keys %{$cmd{$_}->[2]}) {
foreach (sort keys %{$cmd{$_}->[2]}) {
# mixed-case options are for .git/config only
next if /[A-Z]/ && /^[a-z]+$/i;
# prints out arguments as they should be passed:
@ -406,7 +406,8 @@ sub cmd_dcommit {
"If these changes depend on each other, re-running ",
"without --no-rebase will be required."
}
foreach my $d (@$linear_refs) {
while (1) {
my $d = shift @$linear_refs or last;
unless (defined $last_rev) {
(undef, $last_rev, undef) = cmt_metadata("$d~1");
unless (defined $last_rev) {
@ -439,14 +440,14 @@ sub cmd_dcommit {
# we always want to rebase against the current HEAD,
# not any head that was passed to us
my @diff = command('diff-tree', 'HEAD',
my @diff = command('diff-tree', $d,
$gs->refname, '--');
my @finish;
if (@diff) {
@finish = rebase_cmd();
print STDERR "W: HEAD and ", $gs->refname,
print STDERR "W: $d and ", $gs->refname,
" differ, using @finish:\n",
"@diff";
join("\n", @diff), "\n";
} else {
print "No changes between current HEAD and ",
$gs->refname,
@ -455,6 +456,45 @@ sub cmd_dcommit {
@finish = qw/reset --mixed/;
}
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;
}
}

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();
if (p->option & USE_PAGER)
setup_pager();
if (p->option & NEED_WORK_TREE) {
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("%s must be run in a work tree", p->cmd);
}
if (p->option & NEED_WORK_TREE)
setup_work_tree();
trace_argv_printf(argv, argc, "trace: built-in: git");
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-parse", cmd_rev_parse, RUN_SETUP },
{ "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 },
{ "send-pack", cmd_send_pack, RUN_SETUP },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },

View file

@ -611,6 +611,15 @@ (%)
);
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'};
my ($use_pathinfo) = gitweb_check_feature('pathinfo');
@ -1423,20 +1432,121 @@ sub git_get_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 {
my ($key, $type) = @_;
# key sanity check
return unless ($key);
$key =~ s/^gitweb\.//;
return if ($key =~ m/\W/);
my @x = (git_cmd(), 'config');
if (defined $type) { push @x, $type; }
push @x, "--get";
push @x, "gitweb.$key";
my $val = qx(@x);
chomp $val;
return ($val);
# type sanity check
if (defined $type) {
$type =~ s/^--//;
$type = undef
unless ($type eq 'bool' || $type eq 'int');
}
# 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
@ -1496,7 +1606,9 @@ sub git_get_path_by_hash {
sub git_get_project_description {
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>;
close $fd;
if (defined $descr) {
@ -1508,7 +1620,11 @@ sub git_get_project_description {
sub git_get_project_url_list {
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>;
close $fd;
@ -1990,12 +2106,12 @@ sub parse_difftree_raw_line {
$res{'to_mode'} = $2;
$res{'from_id'} = $3;
$res{'to_id'} = $4;
$res{'status'} = $5;
$res{'status'} = $res{'status_str'} = $5;
$res{'similarity'} = $6;
if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied
($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7);
} 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'
@ -2006,6 +2122,7 @@ sub parse_difftree_raw_line {
$res{'to_mode'} = pop @{$res{'from_mode'}};
$res{'from_id'} = [ split(' ', $3) ];
$res{'to_id'} = pop @{$res{'from_id'}};
$res{'status_str'} = $4;
$res{'status'} = [ split('', $4) ];
$res{'to_file'} = unquote($5);
}
@ -2062,7 +2179,10 @@ sub parse_from_to_diffinfo {
fill_from_file_info($diffinfo, @parents)
unless exists $diffinfo->{'from_file'};
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
$from->{'href'}[$i] = href(action=>"blob",
hash_base=>$parents[$i],
@ -2074,7 +2194,7 @@ sub parse_from_to_diffinfo {
}
} else {
# 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
$from->{'href'} = href(action=>"blob", hash_base=>$hash_parent,
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
$to->{'href'} = href(action=>"blob", hash_base=>$hash,
hash=>$diffinfo->{'to_id'},
@ -2505,7 +2625,7 @@ sub format_paging_nav {
if ($page > 0) {
$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");
} else {
$paging_nav .= " &sdot; prev";
@ -2513,7 +2633,7 @@ sub format_paging_nav {
if ($nrevs >= (100 * ($page+1)-1)) {
$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");
} else {
$paging_nav .= " &sdot; next";
@ -2818,7 +2938,7 @@ sub fill_from_file_info {
sub is_deleted {
my $diffinfo = shift;
return $diffinfo->{'to_id'} eq ('0' x 40);
return $diffinfo->{'status_str'} =~ /D/;
}
# does patch correspond to [previous] difftree raw line
@ -2829,7 +2949,7 @@ sub is_patch_split {
my ($diffinfo, $patchinfo) = @_;
return defined $diffinfo && defined $patchinfo
&& ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'};
&& $diffinfo->{'to_file'} eq $patchinfo->{'to_file'};
}
@ -3898,11 +4018,11 @@ sub git_blame2 {
or die_error(undef, "Open git-blame failed");
git_header_html();
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") .
" | " .
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)},
"history") .
$cgi->a({-href => href(action=>"history", -replay=>1)},
"history") .
" | " .
$cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
"HEAD");
@ -4178,18 +4298,15 @@ sub git_blob {
if (defined $file_name) {
if ($have_blame) {
$formats_nav .=
$cgi->a({-href => href(action=>"blame", hash_base=>$hash_base,
hash=>$hash, file_name=>$file_name)},
$cgi->a({-href => href(action=>"blame", -replay=>1)},
"blame") .
" | ";
}
$formats_nav .=
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
hash=>$hash, file_name=>$file_name)},
$cgi->a({-href => href(action=>"history", -replay=>1)},
"history") .
" | " .
$cgi->a({-href => href(action=>"blob_plain",
hash=>$hash, file_name=>$file_name)},
$cgi->a({-href => href(action=>"blob_plain", -replay=>1)},
"raw") .
" | " .
$cgi->a({-href => href(action=>"blob",
@ -4197,7 +4314,8 @@ sub git_blob {
"HEAD");
} else {
$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_header_div('commit', esc_html($co{'title'}), $hash_base);
@ -4260,8 +4378,7 @@ sub git_tree {
my @views_nav = ();
if (defined $file_name) {
push @views_nav,
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
hash=>$hash, file_name=>$file_name)},
$cgi->a({-href => href(action=>"history", -replay=>1)},
"history"),
$cgi->a({-href => href(action=>"tree",
hash_base=>"HEAD", file_name=>$file_name)},
@ -4435,7 +4552,7 @@ sub git_log {
}
if ($#commitlist >= 100) {
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");
print "</div>\n";
}
@ -4667,8 +4784,8 @@ sub git_blobdiff {
}
%diffinfo = parse_difftree_raw_line($difftree[0]);
$file_parent ||= $diffinfo{'from_file'} || $file_name || $diffinfo{'file'};
$file_name ||= $diffinfo{'to_file'} || $diffinfo{'file'};
$file_parent ||= $diffinfo{'from_file'} || $file_name;
$file_name ||= $diffinfo{'to_file'};
$hash_parent ||= $diffinfo{'from_id'};
$hash ||= $diffinfo{'to_id'};
@ -4729,10 +4846,7 @@ sub git_blobdiff {
# header
if ($format eq 'html') {
my $formats_nav =
$cgi->a({-href => href(action=>"blobdiff_plain",
hash=>$hash, hash_parent=>$hash_parent,
hash_base=>$hash_base, hash_parent_base=>$hash_parent_base,
file_name=>$file_name, file_parent=>$file_parent)},
$cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)},
"raw");
git_header_html(undef, $expires);
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@ -4806,8 +4920,7 @@ sub git_commitdiff {
my $formats_nav;
if ($format eq 'html') {
$formats_nav =
$cgi->a({-href => href(action=>"commitdiff_plain",
hash=>$hash, hash_parent=>$hash_parent)},
$cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
"raw");
if (defined $hash_parent &&
@ -5002,27 +5115,20 @@ sub git_history {
file_name=>$file_name)},
"first");
$paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
file_name=>$file_name, page=>$page-1),
$cgi->a({-href => href(-replay=>1, page=>$page-1),
-accesskey => "p", -title => "Alt-p"}, "prev");
} else {
$paging_nav .= "first";
$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 = '';
if ($#commitlist >= 100) {
$next_link =
$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
file_name=>$file_name, page=>$page+1),
$cgi->a({-href => href(-replay=>1, page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next");
$paging_nav .= " &sdot; $next_link";
} else {
$paging_nav .= " &sdot; next";
}
git_header_html();
@ -5092,30 +5198,23 @@ sub git_search {
searchtext=>$searchtext, searchtype=>$searchtype)},
"first");
$paging_nav .= " &sdot; " .
$cgi->a({-href => href(action=>"search", hash=>$hash,
searchtext=>$searchtext, searchtype=>$searchtype,
page=>$page-1),
$cgi->a({-href => href(-replay=>1, page=>$page-1),
-accesskey => "p", -title => "Alt-p"}, "prev");
} else {
$paging_nav .= "first";
$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 = '';
if ($#commitlist >= 100) {
$next_link =
$cgi->a({-href => href(action=>"search", hash=>$hash,
searchtext=>$searchtext, searchtype=>$searchtype,
page=>$page+1),
$cgi->a({-href => href(-replay=>1, page=>$page+1),
-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);
@ -5314,7 +5413,7 @@ sub git_shortlog {
my $next_link = '';
if ($#commitlist >= 100) {
$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");
}

3
help.c
View file

@ -79,7 +79,8 @@ static void uniq(struct cmdnames *cmds)
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 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 */
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) ||
!ref_newer(ref->peer_ref->new_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
* an ancestor of what we are trying to
* push. Either way this can be losing
* commits at the remote end and likely
* we were not up to date to begin with.
*/
error("remote '%s' is not a strict "
"subset of local ref '%s'. "
"maybe you are not up-to-date and "
error("remote '%s' is not an ancestor of\n"
"local '%s'.\n"
"Maybe you are not up-to-date and "
"need to pull first?",
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 i, fix_thin_pack = 0;
@ -693,6 +704,8 @@ int main(int argc, char **argv)
struct pack_idx_entry **idx_objects;
unsigned char sha1[20];
git_config(git_index_pack_config);
for (i = 1; i < argc; i++) {
char *arg = argv[i];

View file

@ -125,6 +125,18 @@ static unsigned int digits_in_number(unsigned int number)
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)
{
struct strbuf msgbuf;
@ -273,7 +285,8 @@ void show_log(struct rev_info *opt, const char *sep)
*/
strbuf_init(&msgbuf, 0);
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)
append_signoff(&msgbuf, opt->add_signoff);

View file

@ -22,6 +22,41 @@ enum parse_opt_option_flags {
struct option;
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 {
enum parse_opt_type type;
int short_name;
@ -32,8 +67,6 @@ struct option {
int flags;
parse_opt_cb *callback;
/* holds default value for PARSE_OPT_OPTARG,
though callbacks can use it like they want */
intptr_t defval;
};

View file

@ -812,7 +812,7 @@ sub _cmd_exec {
$self->wc_subdir() and chdir($self->wc_subdir());
}
_execv_git_cmd(@args);
die "exec failed: $!";
die qq[exec "@args" failed: $!];
}
# 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, '\'');
while (*src) {
size_t len = strcspn(src, "'\\");
size_t len = strcspn(src, "'!");
strbuf_add(dst, src, len);
src += len;
while (need_bs_quote(*src)) {
@ -131,7 +131,8 @@ static signed char const sq_lookup[256] = {
/* 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;
}

10
setup.c
View file

@ -206,6 +206,16 @@ static const char *set_work_tree(const char *dir)
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
* not, since the config can only be read _after_ this function was called.

View file

@ -23,12 +23,12 @@
* that way:
*
* strbuf_grow(sb, SOME_SIZE);
* // ... here the memory areay starting at sb->buf, and of length
* // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at
* // least SOME_SIZE
* ... Here, the memory array starting at sb->buf, and of length
* ... strbuf_avail(sb) is all yours, and you are sure that
* ... strbuf_avail(sb) is at least SOME_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
* 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 &&
GIT_AUTHOR_DATE="2005-05-26 23:45" \
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'
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: A U Thor <a.u.thor@example.com>
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
'
# 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

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

View file

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

View file

@ -7,6 +7,8 @@ test_description='git-clean basic tests'
. ./test-lib.sh
git config clean.requireForce no
test_expect_success 'setup' '
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' '
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' '
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' '
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$'
"
test_expect_success 'verify merge commit message' "
git rev-list --pretty=raw -1 refs/heads/svn | \
grep \" Merge branch 'merge' into svn\"
"
test_done

View file

@ -31,7 +31,6 @@ our \$projects_list = "";
our \$export_ok = "";
our \$strict_export = "";
CGI::Carp::set_programname("gitweb/gitweb.cgi");
EOF
cat >.git/description <<EOF
@ -558,4 +557,27 @@ test_expect_success \
'gitweb_run "p=.git;a=tree;opt=--no-merges"'
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

View file

@ -10,6 +10,12 @@
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# 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
@ -32,18 +38,20 @@ fi
# --- Config
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
projectdesc=$(sed -e '1p' "$GIT_DIR/description")
if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
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
exit 1
fi
# --- 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
newrev_type=commit
newrev_type=delete
else
newrev_type=$(git-cat-file -t $newrev)
fi
@ -58,15 +66,36 @@ case "$refname","$newrev_type" in
exit 1
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)
# annotated tag
;;
refs/heads/*,commit)
# 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)
# 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?)
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
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;
int argc;
int err;
@ -647,7 +648,8 @@ static int fetch_refs_via_pack(struct transport *transport,
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 send_pack_args args;

3
utf8.c
View file

@ -11,7 +11,8 @@ struct interval {
};
/* 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 mid;