test-dump-cache-tree
test-genrandom
test-match-trees
+test-parse-options
test-sha1
common-cmds.h
*.tar.gz
--- /dev/null
+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).
ASCIIDOC=asciidoc
ASCIIDOC_EXTRA =
-ifdef ASCIIDOC8
-ASCIIDOC_EXTRA += -a asciidoc7compatible
-endif
INSTALL?=install
RM ?= rm -f
DOC_REF = origin/man
-include ../config.mak.autogen
-include ../config.mak
+ifdef ASCIIDOC8
+ASCIIDOC_EXTRA += -a asciidoc7compatible
+endif
+ifdef DOCBOOK_XSL_172
+ASCIIDOC_EXTRA += -a docbook-xsl-172
+endif
+
#
# Please note that there is a minor bug in asciidoc.
# The version after 6.0.3 _will_ include the patch found here:
* 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".
* "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.
--- /dev/null
+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
* 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.
* 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
------------------
--
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
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
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:
endif::backend-docbook[]
ifdef::backend-docbook[]
+ifndef::docbook-xsl-172[]
# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
+# v1.72 breaks with this because it replaces dots not in roff requests.
[listingblock]
<example><title>{title}</title>
<literallayout>
endif::doctype-manpage[]
</literallayout>
{title#}</example>
+endif::docbook-xsl-172[]
endif::backend-docbook[]
ifdef::doctype-manpage[]
use File::Compare qw(compare);
sub format_one {
- my ($out, $name) = @_;
+ my ($out, $nameattr) = @_;
+ my ($name, $attr) = @$nameattr;
my ($state, $description);
$state = 0;
open I, '<', "$name.txt" or die "No such file $name.txt";
die "No description found in $name.txt";
}
if (my ($verify_name, $text) = ($description =~ /^($name) - (.*)/)) {
- print $out "gitlink:$name\[1\]::\n";
- print $out "\t$text.\n\n";
+ print $out "gitlink:$name\[1\]::\n\t";
+ if ($attr) {
+ print $out "($attr) ";
+ }
+ print $out "$text.\n\n";
}
else {
die "Description does not match $name: $description";
next if /^#/;
chomp;
- my ($name, $cat) = /^(\S+)\s+(.*)$/;
- push @{$cmds{$cat}}, $name;
+ my ($name, $cat, $attr) = /^(\S+)\s+(.*?)(?:\s+(.*))?$/;
+ push @{$cmds{$cat}}, [$name, $attr];
}
for my $cat (qw(ancillaryinterrogators
git-init mainporcelain
git-instaweb ancillaryinterrogators
gitk mainporcelain
-git-local-fetch synchingrepositories
git-log mainporcelain
-git-lost-found ancillarymanipulators
+git-lost-found ancillarymanipulators deprecated
git-ls-files plumbinginterrogators
git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
git-show-index plumbinginterrogators
git-show-ref plumbinginterrogators
git-sh-setup purehelpers
-git-ssh-fetch synchingrepositories
-git-ssh-upload synchingrepositories
git-stash mainporcelain
git-status mainporcelain
git-stripspace purehelpers
git-svn foreignscminterface
git-symbolic-ref plumbingmanipulators
git-tag mainporcelain
-git-tar-tree plumbinginterrogators
+git-tar-tree plumbinginterrogators deprecated
git-unpack-file plumbinginterrogators
git-unpack-objects plumbingmanipulators
git-update-index plumbingmanipulators
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
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.
------------------------------------------------
$ git checkout mybranch
$ echo "Work, work, work" >>hello
-$ git commit -m 'Some work.' -i hello
+$ git commit -m "Some work." -i hello
------------------------------------------------
Here, we just added another line to `hello`, and we used a shorthand for
------------
$ echo "Play, play, play" >>hello
$ echo "Lots of fun" >>example
-$ git commit -m 'Some fun.' -i hello example
+$ git commit -m "Some fun." -i hello example
------------
since the master branch is obviously in a much better mood.
environment, is `git show-branch`.
------------------------------------------------
-$ git show-branch --topo-order master mybranch
+$ git-show-branch --topo-order --more=1 master mybranch
* [master] Merge work in mybranch
! [mybranch] Some work.
--
- [master] Merge work in mybranch
*+ [mybranch] Some work.
+* [master^] Some fun.
------------------------------------------------
The first two lines indicate that it is showing the two branches
commits from the master branch. The string inside brackets
before the commit log message is a short name you can use to
name the commit. In the above example, 'master' and 'mybranch'
-are branch heads. 'master~1' is the first parent of 'master'
+are branch heads. 'master^' is the first parent of 'master'
branch head. Please see 'git-rev-parse' documentation if you
see more complex cases.
+[NOTE]
+Without the '--more=1' option, 'git-show-branch' would not output the
+'[master^]' commit, as '[mybranch]' commit is a common ancestor of
+both 'master' and 'mybranch' tips. Please see 'git-show-branch'
+documentation for details.
+
+[NOTE]
+If there were more commits on the 'master' branch after the merge, the
+merge commit itself would not be shown by 'git-show-branch' by
+default. You would need to provide '--sparse' option to make the
+merge commit visible in this case.
+
Now, let's pretend you are the one who did all the work in
`mybranch`, and the fruit of your hard work has finally been merged
to the `master` branch. Let's go back to `mybranch`, and run
that does not even support directory index would suffice. But
you must prepare your repository with `git-update-server-info`
to help dumb transport downloaders.
-+
-There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
-programs, which are 'commit walkers'; they outlived their
-usefulness when git Native and SSH transports were introduced,
-and are not used by `git pull` or `git push` scripts.
Once you fetch from the remote repository, you `merge` that
with your current branch.
and bring ourselves back to the pre-merge state:
------------
-$ git show-branch --more=3 master mybranch
+$ git show-branch --more=2 master mybranch
! [master] Merge work in mybranch
* [mybranch] Merge work in mybranch
--
This is the same `git-read-tree` command we have already seen,
but it takes three trees, unlike previous examples. This reads
the contents of each tree into different 'stage' in the index
-file (the first tree goes to stage 1, the second stage 2,
+file (the first tree goes to stage 1, the second to stage 2,
etc.). After reading three trees into three stages, the paths
that are the same in all three stages are 'collapsed' into stage
0. Also paths that are the same in two of three stages are
'commit-fix' next, like this:
------------
-$ git merge -m 'Merge fix in diff-fix' diff-fix
-$ git merge -m 'Merge fix in commit-fix' commit-fix
+$ git merge -m "Merge fix in diff-fix" diff-fix
+$ git merge -m "Merge fix in commit-fix" commit-fix
------------
Which would result in:
-The output format from "git-diff-index", "git-diff-tree" and
-"git-diff-files" are very similar.
+The output format from "git-diff-index", "git-diff-tree",
+"git-diff-files" and "git diff --raw" are very similar.
These commands all compare two sets of things; what is
compared differs:
diff format for merges
----------------------
-"git-diff-tree" and "git-diff-files" can take '-c' or '--cc' option
+"git-diff-tree", "git-diff-files" and "git-diff --raw"
+can take '-c' or '--cc' option
to generate diff output also for merge commits. The output differs
from the format described above in the following way:
--------------------------
When "git-diff-index", "git-diff-tree", or "git-diff-files" are run
-with a '-p' option, they do not produce the output described above;
-instead they produce a patch file. You can customize the creation
-of such patches via the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS
-environment variables.
+with a '-p' option, or "git diff" without the '--raw' option, they
+do not produce the output described above; instead they produce a
+patch file. You can customize the creation of such patches via the
+GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables.
What the -p option produces is slightly different from the traditional
diff format.
combined diff format
--------------------
-git-diff-tree and git-diff-files can take '-c' or '--cc' option
-to produce 'combined diff', which looks like this:
+"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or
+'--cc' option to produce 'combined diff', which looks like this:
------------
diff --combined describe.c
$ cd frotz
$ git-init
$ git add . <1>
-$ git commit -m 'import of frotz source tree.'
+$ git commit -m "import of frotz source tree."
$ git tag v2.43 <2>
------------
+
$ git checkout maint
$ git cherry-pick master~4 <9>
$ compile/test
-$ git tag -s -m 'GIT 0.99.9x' v0.99.9x <10>
+$ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
$ git fetch ko && git show-branch master maint 'tags/ko-*' <11>
$ git push ko <12>
$ git push ko v0.99.9x <13>
and `dir/file2`) can be given to add all files in the
directory, recursively.
--n::
+-n, \--dry-run::
Don't actually add the file(s), just show if they exist.
--v::
+-v, \--verbose::
Be verbose.
-f::
--------
gitlink:git-status[1]
gitlink:git-rm[1]
+gitlink:git-reset[1]
gitlink:git-mv[1]
gitlink:git-commit[1]
gitlink:git-update-index[1]
-a::
List both remote-tracking branches and local branches.
--v::
+-v, --verbose::
Show sha1 and commit subject line for each head.
--abbrev=<length>::
'--track' were given.
--no-track::
- When -b is given and a branch is created off a remote branch,
+ When a branch is created off a remote branch,
set up configuration so that git-pull will not retrieve data
from the remote branch, ignoring the branch.autosetupmerge
configuration variable.
SYNOPSIS
--------
-'git-cherry-pick' [--edit] [-n] [-x] <commit>
+'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>
DESCRIPTION
-----------
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
'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
-----------
--------
When recording your own work, the contents of modified files in
your working tree are temporarily stored to a staging area
-called the "index" with gitlink:git-add[1]. Removal
-of a file is staged with gitlink:git-rm[1]. After building the
-state to be committed incrementally with these commands, `git
-commit` (without any pathname parameter) is used to record what
+called the "index" with gitlink:git-add[1]. A file can be
+reverted back, only in the index but not in the working tree,
+to that of the last commit with `git-reset HEAD -- <file>`,
+which effectively reverts `git-add` and prevents the changes to
+this file from participating in the next commit. After building
+the state to be committed incrementally with these commands,
+`git commit` (without any pathname parameter) is used to record what
has been staged so far. This is the most basic form of the
command. An example:
the diff to the named paths (you can give directory
names and get diff for all files under them).
+Output format
+-------------
+include::diff-format.txt[]
EXAMPLES
--------
does this in the '.git-rewrite/' directory but you can override
that choice by this parameter.
--f\|--force::
+-f|--force::
`git filter-branch` refuses to start with an existing temporary
directory or when there are already refs starting with
'refs/original/', unless forced.
DESCRIPTION
-----------
Acts as a filter, extracting the commit ID stored in archives created by
-git-tar-tree. It reads only the first 1024 bytes of input, thus its
+gitlink:git-archive[1]. It reads only the first 1024 bytes of input, thus its
runtime is not influenced by the size of <tarfile> very much.
If no commit ID is found, git-get-tar-commit-id quietly exists with a
return code of 1. This can happen if <tarfile> had not been created
-using git-tar-tree or if the first parameter of git-tar-tree had been
+using git-archive or if the <treeish> parameter of git-archive had been
a tree ID instead of a commit ID or tag.
+++ /dev/null
-git-local-fetch(1)
-==================
-
-NAME
-----
-git-local-fetch - Duplicate another git repository on a local system
-
-
-SYNOPSIS
---------
-[verse]
-'git-local-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n]
- commit-id path
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Duplicates another git repository on a local system.
-
-OPTIONS
--------
--c::
- Get the commit objects.
--t::
- Get trees associated with the commit objects.
--a::
- Get all the objects.
--v::
- Report what is downloaded.
--s::
- Instead of regular file-to-file copying use symbolic links to the objects
- in the remote repository.
--l::
- Before attempting symlinks (if -s is specified) or file-to-file copying the
- remote objects, try to hardlink the remote objects into the local
- repository.
--n::
- Never attempt to file-to-file copy remote objects. Only useful with
- -s or -l command-line options.
-
--w <filename>::
- Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
- the local end after the transfer is complete.
-
---stdin::
- Instead of a commit id on the command line (which is not expected in this
- case), 'git-local-fetch' expects lines on stdin in the format
-
- <commit-id>['\t'<filename-as-in--w>]
-
---recover::
- Verify that everything reachable from target is fetched. Used after
- an earlier fetch is interrupted.
-
-Author
-------
-Written by Junio C Hamano <junkio@cox.net>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
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,
condition. An error happens when a source is neither existing nor
controlled by GIT, or when it would overwrite an existing
file unless '-f' is given.
--n::
+-n, \--dry-run::
Do nothing; only show what would happen
--------
[verse]
'git-push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
- [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]
+ [--repo=all] [-f | --force] [-v | --verbose] [<repository> <refspec>...]
DESCRIPTION
-----------
transfer spends extra cycles to minimize the number of
objects to be sent and meant to be used on slower connection.
--v::
+-v, \--verbose::
Run verbosely.
include::urls-remotes.txt[]
Fetch updates for a named set of remotes in the repository as defined by
remotes.<group>. If a named group is not specified on the command line,
the configuration parameter remotes.default will get used; if
-remotes.default is not defined, all remotes which do not the
+remotes.default is not defined, all remotes which do not have the
configuration parameter remote.<name>.skipDefaultUpdate set to true will
be updated. (See gitlink:git-config[1]).
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
-----------
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.
------------
$ git checkout feature ;# you were working in "feature" branch and
$ work work work ;# got interrupted
-$ git commit -a -m 'snapshot WIP' <1>
+$ git commit -a -m "snapshot WIP" <1>
$ git checkout master
$ fix fix fix
$ git commit ;# commit with real log
SYNOPSIS
--------
-'git-revert' [--edit | --no-edit] [-n] <commit>
+'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>
DESCRIPTION
-----------
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.
-f::
Override the up-to-date check.
--n::
+-n, \--dry-run::
Don't actually remove the file(s), just show if they exist in
the index.
\--ignore-unmatch::
Exit with a zero status even if no files matched.
-\--quiet::
+-q, \--quiet::
git-rm normally outputs one line (in the form of an "rm" command)
for each file removed. This option suppresses that output.
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.
+++ /dev/null
-git-ssh-fetch(1)
-================
-
-NAME
-----
-git-ssh-fetch - Fetch from a remote repository over ssh connection
-
-
-
-SYNOPSIS
---------
-'git-ssh-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pulls from a remote repository over ssh connection, invoking
-git-ssh-upload on the other end. It functions identically to
-git-ssh-upload, aside from which end you run it on.
-
-
-OPTIONS
--------
-commit-id::
- Either the hash or the filename under [URL]/refs/ to
- pull.
-
--c::
- Get the commit objects.
--t::
- Get trees associated with the commit objects.
--a::
- Get all the objects.
--v::
- Report what is downloaded.
--w::
- Writes the commit-id into the filename under $GIT_DIR/refs/ on
- the local end after the transfer is complete.
-
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
+++ /dev/null
-git-ssh-upload(1)
-=================
-
-NAME
-----
-git-ssh-upload - Push to a remote repository over ssh connection
-
-
-SYNOPSIS
---------
-'git-ssh-upload' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.
-
-Pushes from a remote repository over ssh connection, invoking
-git-ssh-fetch on the other end. It functions identically to
-git-ssh-fetch, aside from which end you run it on.
-
-OPTIONS
--------
-commit-id::
- Id of commit to push.
-
--c::
- Get the commit objects.
--t::
- Get tree associated with the requested commit object.
--a::
- Get all the objects.
--v::
- Report what is uploaded.
--w::
- Writes the commit-id into the filename under [URL]/refs/ on
- the remote end after the transfer is complete.
-
-Author
-------
-Written by Daniel Barkalow <barkalow@iabervon.org>
-
-Documentation
---------------
-Documentation by Daniel Barkalow
-
-GIT
----
-Part of the gitlink:git[7] suite
OPTIONS
-------
--s\|--strip-comments::
+-s|--strip-comments::
In addition to empty lines, also strip lines starting with '#'.
<stream>::
OPTIONS
-------
--q::
+-q, --quiet::
Do not issue an error message if the <name> is not a
symbolic ref but a detached HEAD; instead exit with
non-zero status silently.
`filter`
^^^^^^^^
-A `filter` attribute can be set to a string value. This names
+A `filter` attribute can be set to a string value that names a
filter driver specified in the configuration.
-A filter driver consists of `clean` command and `smudge`
+A filter driver consists of a `clean` command and a `smudge`
command, either of which can be left unspecified. Upon
-checkout, when `smudge` command is specified, the command is fed
-the blob object from its standard input, and its standard output
-is used to update the worktree file. Similarly, `clean` command
-is used to convert the contents of worktree file upon checkin.
+checkout, when the `smudge` command is specified, the command is
+fed the blob object from its standard input, and its standard
+output is used to update the worktree file. Similarly, the
+`clean` command is used to convert the contents of worktree file
+upon checkin.
-Missing filter driver definition in the config is not an error
+A missing filter driver definition in the config is not an error
but makes the filter a no-op passthru.
The content filtering is done to massage the content into a
shape that is more convenient for the platform, filesystem, and
-the user to use. The keyword here is "more convenient" and not
+the user to use. The key phrase here is "more convenient" and not
"turning something unusable into usable". In other words, the
intent is that if someone unsets the filter driver definition,
or does not have the appropriate filter program, the project
--- /dev/null
+Date: Fri, 9 Nov 2007 08:28:38 -0800 (PST)
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Subject: corrupt object on git-gc
+Abstract: Some tricks to reconstruct blob objects in order to fix
+ a corrupted repository.
+
+On Fri, 9 Nov 2007, Yossi Leybovich wrote:
+>
+> Did not help still the repository look for this object?
+> Any one know how can I track this object and understand which file is it
+
+So exactly *because* the SHA1 hash is cryptographically secure, the hash
+itself doesn't actually tell you anything, in order to fix a corrupt
+object you basically have to find the "original source" for it.
+
+The easiest way to do that is almost always to have backups, and find the
+same object somewhere else. Backups really are a good idea, and git makes
+it pretty easy (if nothing else, just clone the repository somewhere else,
+and make sure that you do *not* use a hard-linked clone, and preferably
+not the same disk/machine).
+
+But since you don't seem to have backups right now, the good news is that
+especially with a single blob being corrupt, these things *are* somewhat
+debuggable.
+
+First off, move the corrupt object away, and *save* it. The most common
+cause of corruption so far has been memory corruption, but even so, there
+are people who would be interested in seeing the corruption - but it's
+basically impossible to judge the corruption until we can also see the
+original object, so right now the corrupt object is useless, but it's very
+interesting for the future, in the hope that you can re-create a
+non-corrupt version.
+
+So:
+
+> ib]$ mv .git/objects/4b/9458b3786228369c63936db65827de3cc06200 ../
+
+This is the right thing to do, although it's usually best to save it under
+it's full SHA1 name (you just dropped the "4b" from the result ;).
+
+Let's see what that tells us:
+
+> ib]$ git-fsck --full
+> broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+> to blob 4b9458b3786228369c63936db65827de3cc06200
+> missing blob 4b9458b3786228369c63936db65827de3cc06200
+
+Ok, I removed the "dangling commit" messages, because they are just
+messages about the fact that you probably have rebased etc, so they're not
+at all interesting. But what remains is still very useful. In particular,
+we now know which tree points to it!
+
+Now you can do
+
+ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+
+which will show something like
+
+ 100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore
+ 100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap
+ 100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING
+ 100644 blob ee909f2cc49e54f0799a4739d24c4cb9151ae453 CREDITS
+ 040000 tree 0f5f709c17ad89e72bdbbef6ea221c69807009f6 Documentation
+ 100644 blob 1570d248ad9237e4fa6e4d079336b9da62d9ba32 Kbuild
+ 100644 blob 1c7c229a092665b11cd46a25dbd40feeb31661d9 MAINTAINERS
+ ...
+
+and you should now have a line that looks like
+
+ 10064 blob 4b9458b3786228369c63936db65827de3cc06200 my-magic-file
+
+in the output. This already tells you a *lot* it tells you what file the
+corrupt blob came from!
+
+Now, it doesn't tell you quite enough, though: it doesn't tell what
+*version* of the file didn't get correctly written! You might be really
+lucky, and it may be the version that you already have checked out in your
+working tree, in which case fixing this problem is really simple, just do
+
+ git hash-object -w my-magic-file
+
+again, and if it outputs the missing SHA1 (4b945..) you're now all done!
+
+But that's the really lucky case, so let's assume that it was some older
+version that was broken. How do you tell which version it was?
+
+The easiest way to do it is to do
+
+ git log --raw --all --full-history -- subdirectory/my-magic-file
+
+and that will show you the whole log for that file (please realize that
+the tree you had may not be the top-level tree, so you need to figure out
+which subdirectory it was in on your own), and because you're asking for
+raw output, you'll now get something like
+
+ commit abc
+ Author:
+ Date:
+ ..
+ :100644 100644 4b9458b... newsha... M somedirectory/my-magic-file
+
+
+ commit xyz
+ Author:
+ Date:
+
+ ..
+ :100644 100644 oldsha... 4b9458b... M somedirectory/my-magic-file
+
+and this actually tells you what the *previous* and *subsequent* versions
+of that file were! So now you can look at those ("oldsha" and "newsha"
+respectively), and hopefully you have done commits often, and can
+re-create the missing my-magic-file version by looking at those older and
+newer versions!
+
+If you can do that, you can now recreate the missing object with
+
+ git hash-object -w <recreated-file>
+
+and your repository is good again!
+
+(Btw, you could have ignored the fsck, and started with doing a
+
+ git log --raw --all
+
+and just looked for the sha of the missing object (4b9458b..) in that
+whole thing. It's up to you - git does *have* a lot of information, it is
+just missing one particular blob version.
+
+Trying to recreate trees and especially commits is *much* harder. So you
+were lucky that it's a blob. It's quite possible that you can recreate the
+thing.
+
+ Linus
If you run "git branch" at this point, you'll see that git has
temporarily moved you to a new branch named "bisect". This branch
points to a commit (with commit id 65934...) that is reachable from
-v2.6.19 but not from v2.6.18. Compile and test it, and see whether
+"master" but not from v2.6.18. Compile and test it, and see whether
it crashes. Assume it does crash. Then:
-------------------------------------------------
fundamentally different ways to fix the problem:
1. You can create a new commit that undoes whatever was done
- by the previous commit. This is the correct thing if your
+ by the old commit. This is the correct thing if your
mistake has already been made public.
2. You can go back and modify the old commit. You should
-------------------------------------------------
This lists the commits reachable from the previous version of the head.
-This syntax can be used to with any git command that accepts a commit,
+This syntax can be used with any git command that accepts a commit,
not just with git log. Some other examples:
-------------------------------------------------
# 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.
#
# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
#
+# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
+#
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
# MakeMaker (e.g. using ActiveState under Cygwin).
#
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
- mailmap.h remote.h transport.h diffcore.h hash.h
+ mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
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 \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
- transport.o bundle.o walker.o
+ transport.o bundle.o walker.o parse-options.o
BUILTIN_OBJS = \
builtin-add.o \
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
$(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)
### Testing rules
-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X
+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X
all:: $(TEST_PROGRAMS)
test-delta$X: diff-delta.o patch-delta.o
+test-parse-options$X: parse-options.o
+
.PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
test-%$X: test-%.o $(GITLIBS)
### Check documentation
#
check-docs::
- @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \
+ @(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \
do \
case "$$v" in \
git-merge-octopus | git-merge-ours | git-merge-recursive | \
- git-merge-resolve | git-merge-stupid | \
+ git-merge-resolve | git-merge-stupid | git-merge-subtree | \
git-add--interactive | git-fsck-objects | git-init-db | \
+ git-rebase--interactive | \
git-repo-config | git-fetch--tool ) continue ;; \
esac ; \
test -f "Documentation/$$v.txt" || \
git) ;; \
*) echo "no link: $$v";; \
esac ; \
- done | sort
+ done; \
+ ( \
+ sed -e '1,/^__DATA__/d' \
+ -e 's/[ ].*//' \
+ -e 's/^/listed /' Documentation/cmd-list.perl; \
+ ls -1 Documentation/git*txt | \
+ sed -e 's|Documentation/|documented |' \
+ -e 's/\.txt//'; \
+ ) | while read how cmd; \
+ do \
+ case "$$how,$$cmd" in \
+ *,git-citool | \
+ *,git-gui | \
+ documented,gitattributes | \
+ documented,gitignore | \
+ documented,gitmodules | \
+ documented,git-tools | \
+ sentinel,not,matching,is,ok ) continue ;; \
+ esac; \
+ case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
+ *" $$cmd "*) ;; \
+ *) echo "removed but $$how: $$cmd" ;; \
+ esac; \
+ done ) | sort
### Make sure built-ins do not have dups and listed in git.c
#
#include "commit.h"
#include "revision.h"
#include "run-command.h"
+#include "parse-options.h"
-static const char builtin_add_usage[] =
-"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <filepattern>...";
+static const char * const builtin_add_usage[] = {
+ "git-add [options] [--] <filepattern>...",
+ NULL
+};
static int take_worktree_changes;
static const char *excludes_file;
static const char ignore_error[] =
"The following paths are ignored by one of your .gitignore files:\n";
+static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
+static int add_interactive = 0;
+
+static struct option builtin_add_options[] = {
+ OPT__DRY_RUN(&show_only),
+ OPT__VERBOSE(&verbose),
+ OPT_GROUP(""),
+ OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
+ OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
+ OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
+ OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
+ OPT_END(),
+};
+
int cmd_add(int argc, const char **argv, const char *prefix)
{
- int i, newfd;
- int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
+ int i, newfd, orig_argc = argc;
const char **pathspec;
struct dir_struct dir;
- int add_interactive = 0;
- for (i = 1; i < argc; i++) {
- if (!strcmp("--interactive", argv[i]) ||
- !strcmp("-i", argv[i]))
- add_interactive++;
- }
+ argc = parse_options(argc, argv, builtin_add_options,
+ builtin_add_usage, 0);
if (add_interactive) {
- if (argc != 2)
+ if (add_interactive != 1 || orig_argc != 2)
die("add --interactive does not take any parameters");
exit(interactive_add());
}
newfd = hold_locked_index(&lock_file, 1);
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-n")) {
- show_only = 1;
- continue;
- }
- if (!strcmp(arg, "-f")) {
- ignored_too = 1;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- verbose = 1;
- continue;
- }
- if (!strcmp(arg, "-u")) {
- take_worktree_changes = 1;
- continue;
- }
- if (!strcmp(arg, "--refresh")) {
- refresh_only = 1;
- continue;
- }
- usage(builtin_add_usage);
- }
-
if (take_worktree_changes) {
if (read_cache() < 0)
die("index file corrupt");
- add_files_to_cache(verbose, prefix, argv + i);
+ add_files_to_cache(verbose, prefix, argv);
goto finish;
}
- if (argc <= i) {
+ if (argc == 0) {
fprintf(stderr, "Nothing specified, nothing added.\n");
fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
return 0;
}
- pathspec = get_pathspec(prefix, argv + i);
+ pathspec = get_pathspec(prefix, argv);
if (refresh_only) {
refresh(verbose, pathspec);
argv[unk++] = arg;
}
- if (!incremental)
- setup_pager();
-
if (!blame_move_score)
blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
if (!blame_copy_score)
else if (i != argc - 1)
usage(blame_usage); /* garbage at end */
+ setup_work_tree();
if (!has_path_in_work_tree(path))
die("cannot stat path %s: %s",
path, strerror(errno));
* 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), ":");
}
read_mailmap(&mailmap, ".mailmap", NULL);
+ if (!incremental)
+ setup_pager();
+
assign_blame(&sb, &revs, opt);
if (incremental)
#include "commit.h"
#include "builtin.h"
#include "remote.h"
-
-static const char builtin_branch_usage[] =
- "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
+#include "parse-options.h"
+
+static const char * const builtin_branch_usage[] = {
+ "git-branch [options] [-r | -a]",
+ "git-branch [options] [-l] [-f] <branchname> [<start-point>]",
+ "git-branch [options] [-r] (-d | -D) <branchname>",
+ "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>",
+ NULL
+};
#define REF_UNKNOWN_TYPE 0x00
#define REF_LOCAL_BRANCH 0x01
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]);
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),
int rename = 0, force_rename = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
int reflog = 0, track;
- int kinds = REF_LOCAL_BRANCH;
- int i;
+ int kinds = REF_LOCAL_BRANCH, kind_remote = 0, kind_any = 0;
+
+ struct option options[] = {
+ OPT_GROUP("Generic options"),
+ OPT__VERBOSE(&verbose),
+ OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"),
+ OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
+ OPT_BOOLEAN('r', NULL, &kind_remote, "act on remote-tracking branches"),
+ OPT__ABBREV(&abbrev),
+
+ OPT_GROUP("Specific git-branch actions:"),
+ OPT_BOOLEAN('a', NULL, &kind_any, "list both remote-tracking and local branches"),
+ OPT_BOOLEAN('d', NULL, &delete, "delete fully merged branch"),
+ OPT_BOOLEAN('D', NULL, &force_delete, "delete branch (even if not merged)"),
+ OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
+ OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
+ OPT_BOOLEAN('m', NULL, &rename, "move/rename a branch and its reflog"),
+ OPT_BOOLEAN('M', NULL, &force_rename, "move/rename a branch, even if target exists"),
+ OPT_END(),
+ };
git_config(git_branch_config);
track = branch_track;
-
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "--track")) {
- track = 1;
- continue;
- }
- if (!strcmp(arg, "--no-track")) {
- track = 0;
- continue;
- }
- if (!strcmp(arg, "-d")) {
- delete = 1;
- continue;
- }
- if (!strcmp(arg, "-D")) {
- delete = 1;
- force_delete = 1;
- continue;
- }
- if (!strcmp(arg, "-f")) {
- force_create = 1;
- continue;
- }
- if (!strcmp(arg, "-m")) {
- rename = 1;
- continue;
- }
- if (!strcmp(arg, "-M")) {
- rename = 1;
- force_rename = 1;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- kinds = REF_REMOTE_BRANCH;
- continue;
- }
- if (!strcmp(arg, "-a")) {
- kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH;
- continue;
- }
- if (!strcmp(arg, "-l")) {
- reflog = 1;
- continue;
- }
- if (!prefixcmp(arg, "--no-abbrev")) {
- abbrev = 0;
- continue;
- }
- if (!prefixcmp(arg, "--abbrev=")) {
- abbrev = strtoul(arg + 9, NULL, 10);
- if (abbrev < MINIMUM_ABBREV)
- abbrev = MINIMUM_ABBREV;
- else if (abbrev > 40)
- abbrev = 40;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- verbose = 1;
- continue;
- }
- if (!strcmp(arg, "--color")) {
- branch_use_color = 1;
- continue;
- }
- if (!strcmp(arg, "--no-color")) {
- branch_use_color = 0;
- continue;
- }
- usage(builtin_branch_usage);
- }
+ argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
+
+ delete |= force_delete;
+ rename |= force_rename;
+ if (kind_remote)
+ kinds = REF_REMOTE_BRANCH;
+ if (kind_any)
+ kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH;
+ if (abbrev && abbrev < MINIMUM_ABBREV)
+ abbrev = MINIMUM_ABBREV;
+ else if (abbrev > 40)
+ abbrev = 40;
if ((delete && rename) || (delete && force_create) ||
(rename && force_create))
- usage(builtin_branch_usage);
+ usage_with_options(builtin_branch_usage, options);
head = resolve_ref("HEAD", head_sha1, 0, NULL);
if (!head)
head = xstrdup(head);
if (!strcmp(head, "HEAD")) {
detached = 1;
- }
- else {
+ } else {
if (prefixcmp(head, "refs/heads/"))
die("HEAD not found below refs/heads!");
head += 11;
}
if (delete)
- return delete_branches(argc - i, argv + i, force_delete, kinds);
- else if (i == argc)
+ return delete_branches(argc, argv, force_delete, kinds);
+ else if (argc == 0)
print_ref_list(kinds, detached, verbose, abbrev);
- else if (rename && (i == argc - 1))
- rename_branch(head, argv[i], force_rename);
- else if (rename && (i == argc - 2))
- rename_branch(argv[i], argv[i + 1], force_rename);
- else if (i == argc - 1 || i == argc - 2)
- create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head,
+ else if (rename && (argc == 1))
+ rename_branch(head, argv[0], force_rename);
+ else if (rename && (argc == 2))
+ rename_branch(argv[0], argv[1], force_rename);
+ else if (argc <= 2)
+ create_branch(argv[0], (argc == 2) ? argv[1] : head,
force_create, reflog, track);
else
- usage(builtin_branch_usage);
+ usage_with_options(builtin_branch_usage, options);
return 0;
}
#include "cache.h"
#include "builtin.h"
-
-static const char count_objects_usage[] = "git-count-objects [-v]";
+#include "parse-options.h"
static void count_objects(DIR *d, char *path, int len, int verbose,
unsigned long *loose,
}
}
-int cmd_count_objects(int ac, const char **av, const char *prefix)
+static char const * const count_objects_usage[] = {
+ "git-count-objects [-v]",
+ NULL
+};
+
+int cmd_count_objects(int argc, const char **argv, const char *prefix)
{
- int i;
- int verbose = 0;
+ int i, verbose = 0;
const char *objdir = get_object_directory();
int len = strlen(objdir);
char *path = xmalloc(len + 50);
unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
unsigned long loose_size = 0;
+ struct option opts[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_END(),
+ };
- for (i = 1; i < ac; i++) {
- const char *arg = av[i];
- if (*arg != '-')
- break;
- else if (!strcmp(arg, "-v"))
- verbose = 1;
- else
- usage(count_objects_usage);
- }
-
+ argc = parse_options(argc, argv, opts, count_objects_usage, 0);
/* we do not take arguments other than flags for now */
- if (i < ac)
- usage(count_objects_usage);
+ if (argc)
+ usage_with_options(count_objects_usage, opts);
memcpy(path, objdir, len);
if (len && objdir[len-1] != '/')
path[len++] = '/';
#include "refs.h"
#include "builtin.h"
#include "exec_cmd.h"
+#include "parse-options.h"
#define SEEN (1u<<0)
#define MAX_TAGS (FLAG_BITS - 1)
-static const char describe_usage[] =
-"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
+static const char * const describe_usage[] = {
+ "git-describe [options] <committish>*",
+ NULL
+};
static int debug; /* Display lots of verbose info */
static int all; /* Default to annotated tags only */
int cmd_describe(int argc, const char **argv, const char *prefix)
{
- int i;
int contains = 0;
+ struct option options[] = {
+ OPT_BOOLEAN(0, "contains", &contains, "find the tag that comes after the commit"),
+ OPT_BOOLEAN(0, "debug", &debug, "debug search strategy on stderr"),
+ OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"),
+ OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"),
+ OPT__ABBREV(&abbrev),
+ OPT_INTEGER(0, "candidates", &max_candidates,
+ "consider <n> most recent tags (default: 10)"),
+ OPT_END(),
+ };
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (*arg != '-')
- break;
- else if (!strcmp(arg, "--contains"))
- contains = 1;
- else if (!strcmp(arg, "--debug"))
- debug = 1;
- else if (!strcmp(arg, "--all"))
- all = 1;
- else if (!strcmp(arg, "--tags"))
- tags = 1;
- else if (!prefixcmp(arg, "--abbrev=")) {
- abbrev = strtoul(arg + 9, NULL, 10);
- if (abbrev != 0 && (abbrev < MINIMUM_ABBREV || 40 < abbrev))
- abbrev = DEFAULT_ABBREV;
- }
- else if (!prefixcmp(arg, "--candidates=")) {
- max_candidates = strtoul(arg + 13, NULL, 10);
- if (max_candidates < 1)
- max_candidates = 1;
- else if (max_candidates > MAX_TAGS)
- max_candidates = MAX_TAGS;
- }
- else
- usage(describe_usage);
- }
+ argc = parse_options(argc, argv, options, describe_usage, 0);
+ if (max_candidates < 1)
+ max_candidates = 1;
+ else if (max_candidates > MAX_TAGS)
+ max_candidates = MAX_TAGS;
save_commit_buffer = 0;
if (contains) {
- const char **args = xmalloc((4 + argc - i) * sizeof(char*));
+ const char **args = xmalloc((4 + argc) * sizeof(char*));
args[0] = "name-rev";
args[1] = "--name-only";
args[2] = "--tags";
- memcpy(args + 3, argv + i, (argc - i) * sizeof(char*));
- args[3 + argc - i] = NULL;
- return cmd_name_rev(3 + argc - i, args, prefix);
+ memcpy(args + 3, argv, argc * sizeof(char*));
+ args[3 + argc] = NULL;
+ return cmd_name_rev(3 + argc, args, prefix);
}
- if (argc <= i)
+ if (argc == 0) {
describe("HEAD", 1);
- else
- while (i < argc) {
- describe(argv[i], (i == argc - 1));
- i++;
+ } else {
+ while (argc-- > 0) {
+ describe(*argv++, argc == 0);
}
-
+ }
return 0;
}
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);
}
#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)
{
(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
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)
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;
}
* 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;
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);
}
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);
depth = argv[i];
continue;
}
- if (!strcmp(arg, "--quiet")) {
+ if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1;
continue;
}
#include "tree.h"
#include "blob.h"
#include "quote.h"
+#include "parse-options.h"
/* Quoting styles */
#define QUOTE_NONE 0
* Make sure the format string is well formed, and parse out
* the used atoms.
*/
-static void verify_format(const char *format)
+static int verify_format(const char *format)
{
const char *cp, *sp;
for (cp = format; *cp && (sp = find_next(cp)); ) {
const char *ep = strchr(sp, ')');
if (!ep)
- die("malformatted format string %s", sp);
+ return error("malformatted format string %s", sp);
/* sp points at "%(" and ep points at the closing ")" */
parse_atom(sp + 2, ep);
cp = ep + 1;
}
+ return 0;
}
/*
if (!eol)
return "";
eol++;
- if (eol[1] == '\n')
+ if (*eol == '\n')
return ""; /* end of header */
buf = eol;
}
return sort;
}
-int cmd_for_each_ref(int ac, const char **av, const char *prefix)
+int opt_parse_sort(const struct option *opt, const char *arg, int unset)
+{
+ struct ref_sort **sort_tail = opt->value;
+ struct ref_sort *s;
+ int len;
+
+ if (!arg) /* should --no-sort void the list ? */
+ return -1;
+
+ *sort_tail = s = xcalloc(1, sizeof(*s));
+ sort_tail = &s->next;
+
+ if (*arg == '-') {
+ s->reverse = 1;
+ arg++;
+ }
+ len = strlen(arg);
+ s->atom = parse_atom(arg, arg+len);
+ return 0;
+}
+
+static char const * const for_each_ref_usage[] = {
+ "git-for-each-ref [options] [<pattern>]",
+ NULL
+};
+
+int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
int i, num_refs;
- const char *format = NULL;
+ const char *format = "%(objectname) %(objecttype)\t%(refname)";
struct ref_sort *sort = NULL, **sort_tail = &sort;
- int maxcount = 0;
- int quote_style = -1; /* unspecified yet */
+ int maxcount = 0, quote_style;
+ int quote_shell = 0, quote_perl = 0, quote_python = 0, quote_tcl = 0;
struct refinfo **refs;
struct grab_ref_cbdata cbdata;
- for (i = 1; i < ac; i++) {
- const char *arg = av[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!prefixcmp(arg, "--format=")) {
- if (format)
- die("more than one --format?");
- format = arg + 9;
- continue;
- }
- if (!strcmp(arg, "-s") || !strcmp(arg, "--shell") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_SHELL;
- continue;
- }
- if (!strcmp(arg, "-p") || !strcmp(arg, "--perl") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_PERL;
- continue;
- }
- if (!strcmp(arg, "--python") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_PYTHON;
- continue;
- }
- if (!strcmp(arg, "--tcl") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_TCL;
- continue;
- }
- if (!prefixcmp(arg, "--count=")) {
- if (maxcount)
- die("more than one --count?");
- maxcount = atoi(arg + 8);
- if (maxcount <= 0)
- die("The number %s did not parse", arg);
- continue;
- }
- if (!prefixcmp(arg, "--sort=")) {
- struct ref_sort *s = xcalloc(1, sizeof(*s));
- int len;
-
- s->next = NULL;
- *sort_tail = s;
- sort_tail = &s->next;
-
- arg += 7;
- if (*arg == '-') {
- s->reverse = 1;
- arg++;
- }
- len = strlen(arg);
- sort->atom = parse_atom(arg, arg+len);
- continue;
- }
- break;
+ struct option opts[] = {
+ OPT_BOOLEAN('s', "shell", "e_shell, "quote placeholders suitably for shells"),
+ OPT_BOOLEAN('p', "perl", "e_perl, "quote placeholders suitably for perl"),
+ OPT_BOOLEAN( 0 , "python", "e_python, "quote placeholders suitably for python"),
+ OPT_BOOLEAN( 0 , "tcl", "e_tcl, "quote placeholders suitably for tcl"),
+
+ OPT_GROUP(""),
+ OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
+ OPT_STRING( 0 , "format", &format, "format", "format to use for the output"),
+ OPT_CALLBACK(0 , "sort", sort_tail, "key",
+ "field name to sort on", &opt_parse_sort),
+ OPT_END(),
+ };
+
+ parse_options(argc, argv, opts, for_each_ref_usage, 0);
+ if (maxcount < 0) {
+ error("invalid --count argument: `%d'", maxcount);
+ usage_with_options(for_each_ref_usage, opts);
}
- if (quote_style < 0)
- quote_style = QUOTE_NONE;
+ if (quote_shell + quote_perl + quote_python + quote_tcl > 1) {
+ error("more than one quoting style ?");
+ usage_with_options(for_each_ref_usage, opts);
+ }
+ if (verify_format(format))
+ usage_with_options(for_each_ref_usage, opts);
+ quote_style = QUOTE_SHELL * quote_shell + QUOTE_PERL * quote_perl +
+ QUOTE_PYTHON * quote_python + QUOTE_TCL * quote_tcl;
if (!sort)
sort = default_sort();
sort_atom_limit = used_atom_cnt;
- if (!format)
- format = "%(objectname) %(objecttype)\t%(refname)";
-
- verify_format(format);
memset(&cbdata, 0, sizeof(cbdata));
- cbdata.grab_pattern = av + i;
+ cbdata.grab_pattern = argv;
for_each_ref(grab_single_ref, &cbdata);
refs = cbdata.grab_array;
num_refs = cbdata.grab_cnt;
#include "pack.h"
#include "cache-tree.h"
#include "tree-walk.h"
+#include "parse-options.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
return err;
}
-static const char fsck_usage[] =
-"git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] "
-"[--strict] [--verbose] <head-sha1>*]";
+static char const * const fsck_usage[] = {
+ "git-fsck [options] [<object>...]",
+ NULL
+};
+
+static struct option fsck_opts[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"),
+ OPT_BOOLEAN(0, "tags", &show_tags, "report tags"),
+ OPT_BOOLEAN(0, "root", &show_root, "report root nodes"),
+ OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"),
+ OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"),
+ OPT_BOOLEAN(0, "full", &check_full, "also consider alternate objects"),
+ OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"),
+ OPT_BOOLEAN(0, "lost-found", &write_lost_and_found,
+ "write dangling objects in .git/lost-found"),
+ OPT_END(),
+};
int cmd_fsck(int argc, const char **argv, const char *prefix)
{
track_object_refs = 1;
errors_found = 0;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (!strcmp(arg, "--unreachable")) {
- show_unreachable = 1;
- continue;
- }
- if (!strcmp(arg, "--tags")) {
- show_tags = 1;
- continue;
- }
- if (!strcmp(arg, "--root")) {
- show_root = 1;
- continue;
- }
- if (!strcmp(arg, "--cache")) {
- keep_cache_objects = 1;
- continue;
- }
- if (!strcmp(arg, "--no-reflogs")) {
- include_reflogs = 0;
- continue;
- }
- if (!strcmp(arg, "--full")) {
- check_full = 1;
- continue;
- }
- if (!strcmp(arg, "--strict")) {
- check_strict = 1;
- continue;
- }
- if (!strcmp(arg, "--verbose")) {
- verbose = 1;
- continue;
- }
- if (!strcmp(arg, "--lost-found")) {
- check_full = 1;
- include_reflogs = 0;
- write_lost_and_found = 1;
- continue;
- }
- if (*arg == '-')
- usage(fsck_usage);
+ argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0);
+ if (write_lost_and_found) {
+ check_full = 1;
+ include_reflogs = 0;
}
fsck_head_link();
verify_pack(p, 0);
for (p = packed_git; p; p = p->next) {
- uint32_t i, num;
+ uint32_t j, num;
if (open_pack_index(p))
continue;
num = p->num_objects;
- for (i = 0; i < num; i++)
- fsck_sha1(nth_packed_object_sha1(p, i));
+ for (j = 0; j < num; j++)
+ fsck_sha1(nth_packed_object_sha1(p, j));
}
}
heads = 0;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
-
- if (*arg == '-')
- continue;
-
if (!get_sha1(arg, head_sha1)) {
struct object *obj = lookup_object(head_sha1);
}
if (keep_cache_objects) {
- int i;
read_cache();
for (i = 0; i < active_nr; i++) {
unsigned int mode;
#include "builtin.h"
#include "cache.h"
+#include "parse-options.h"
#include "run-command.h"
#define FAILED_RUN "failed to run %s"
-static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
+static const char * const builtin_gc_usage[] = {
+ "git-gc [options]",
+ NULL
+};
static int pack_refs = 1;
static int aggressive_window = -1;
int cmd_gc(int argc, const char **argv, const char *prefix)
{
- int i;
int prune = 0;
+ int aggressive = 0;
int auto_gc = 0;
char buf[80];
+ struct option builtin_gc_options[] = {
+ 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()
+ };
+
git_config(gc_config);
if (pack_refs < 0)
pack_refs = !is_bare_repository();
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (!strcmp(arg, "--prune")) {
- prune = 1;
- continue;
- }
- if (!strcmp(arg, "--aggressive")) {
- append_option(argv_repack, "-f", MAX_ADD);
- if (aggressive_window > 0) {
- sprintf(buf, "--window=%d", aggressive_window);
- append_option(argv_repack, buf, MAX_ADD);
- }
- continue;
- }
- if (!strcmp(arg, "--auto")) {
- auto_gc = 1;
- continue;
+ argc = parse_options(argc, argv, builtin_gc_options, builtin_gc_usage, 0);
+ if (argc > 0)
+ usage_with_options(builtin_gc_usage, builtin_gc_options);
+
+ if (aggressive) {
+ append_option(argv_repack, "-f", MAX_ADD);
+ if (aggressive_window > 0) {
+ sprintf(buf, "--window=%d", aggressive_window);
+ append_option(argv_repack, buf, MAX_ADD);
}
- break;
}
- if (i != argc)
- usage(builtin_gc_usage);
if (auto_gc) {
/*
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)
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);
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);
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;
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);
{
DIR *dir;
struct dirent *dent;
+ char name[PATH_MAX];
+ char *subs[] = { "cur", "new", NULL };
+ char **sub;
+
+ 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;
+ }
- if ((dir = opendir(path)) == NULL) {
- error("cannot opendir %s (%s)", path, 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);
+ }
- while ((dent = readdir(dir)) != NULL) {
- if (dent->d_name[0] == '.')
- continue;
- path_list_insert(dent->d_name, list);
+ closedir(dir);
}
- closedir(dir);
-
return 0;
}
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));
fclose(f);
}
- path_list_clear(&list, 1);
-
ret = skip;
out:
+ path_list_clear(&list, 1);
return ret;
}
{
char name[PATH_MAX];
int ret = -1;
+ int peek;
FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
int file_done = 0;
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) {
#include "dir.h"
#include "cache-tree.h"
#include "path-list.h"
+#include "parse-options.h"
-static const char builtin_mv_usage[] =
-"git-mv [-n] [-f] (<source> <destination> | [-k] <source>... <destination>)";
+static const char * const builtin_mv_usage[] = {
+ "git-mv [options] <source>... <destination>",
+ NULL
+};
static const char **copy_pathspec(const char *prefix, const char **pathspec,
int count, int base_name)
int cmd_mv(int argc, const char **argv, const char *prefix)
{
- int i, newfd, count;
+ int i, newfd;
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+ struct option builtin_mv_options[] = {
+ OPT__DRY_RUN(&show_only),
+ OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
+ OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
+ OPT_END(),
+ };
const char **source, **destination, **dest_path;
enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
struct stat st;
if (read_cache() < 0)
die("index file corrupt");
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ argc = parse_options(argc, argv, builtin_mv_options, builtin_mv_usage, 0);
+ if (--argc < 1)
+ usage_with_options(builtin_mv_usage, builtin_mv_options);
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-n")) {
- show_only = 1;
- continue;
- }
- if (!strcmp(arg, "-f")) {
- force = 1;
- continue;
- }
- if (!strcmp(arg, "-k")) {
- ignore_errors = 1;
- continue;
- }
- usage(builtin_mv_usage);
- }
- count = argc - i - 1;
- if (count < 1)
- usage(builtin_mv_usage);
-
- source = copy_pathspec(prefix, argv + i, count, 0);
- modes = xcalloc(count, sizeof(enum update_mode));
- dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0);
+ source = copy_pathspec(prefix, argv, argc, 0);
+ modes = xcalloc(argc, sizeof(enum update_mode));
+ dest_path = copy_pathspec(prefix, argv + argc, 1, 0);
if (dest_path[0][0] == '\0')
/* special case: "." was normalized to "" */
- destination = copy_pathspec(dest_path[0], argv + i, count, 1);
+ destination = copy_pathspec(dest_path[0], argv, argc, 1);
else if (!lstat(dest_path[0], &st) &&
S_ISDIR(st.st_mode)) {
dest_path[0] = add_slash(dest_path[0]);
- destination = copy_pathspec(dest_path[0], argv + i, count, 1);
+ destination = copy_pathspec(dest_path[0], argv, argc, 1);
} else {
- if (count != 1)
- usage(builtin_mv_usage);
+ if (argc != 1)
+ usage_with_options(builtin_mv_usage, builtin_mv_options);
destination = dest_path;
}
/* Checking */
- for (i = 0; i < count; i++) {
+ for (i = 0; i < argc; i++) {
const char *src = source[i], *dst = destination[i];
int length, src_is_dir;
const char *bad = NULL;
if (last - first > 0) {
source = xrealloc(source,
- (count + last - first)
+ (argc + last - first)
* sizeof(char *));
destination = xrealloc(destination,
- (count + last - first)
+ (argc + last - first)
* sizeof(char *));
modes = xrealloc(modes,
- (count + last - first)
+ (argc + last - first)
* sizeof(enum update_mode));
}
for (j = 0; j < last - first; j++) {
const char *path =
active_cache[first + j]->name;
- source[count + j] = path;
- destination[count + j] =
+ source[argc + j] = path;
+ destination[argc + j] =
prefix_path(dst, dst_len,
path + length);
- modes[count + j] = INDEX;
+ modes[argc + j] = INDEX;
}
- count += last - first;
+ argc += last - first;
}
} else if (lstat(dst, &st) == 0) {
bad = "destination exists";
if (bad) {
if (ignore_errors) {
- if (--count > 0) {
+ if (--argc > 0) {
memmove(source + i, source + i + 1,
- (count - i) * sizeof(char *));
+ (argc - i) * sizeof(char *));
memmove(destination + i,
destination + i + 1,
- (count - i) * sizeof(char *));
+ (argc - i) * sizeof(char *));
}
} else
die ("%s, source=%s, destination=%s",
}
}
- for (i = 0; i < count; i++) {
+ for (i = 0; i < argc; i++) {
const char *src = source[i], *dst = destination[i];
enum update_mode mode = modes[i];
if (show_only || verbose)
path_list_insert(dst, &added);
}
- if (show_only) {
+ if (show_only) {
show_list("Changed : ", &changed);
show_list("Adding : ", &added);
show_list("Deleting : ", &deleted);
#include "commit.h"
#include "tag.h"
#include "refs.h"
+#include "parse-options.h"
#define CUTOFF_DATE_SLOP 86400 /* one day */
-static const char name_rev_usage[] =
- "git-name-rev [--tags | --refs=<pattern>] ( --all | --stdin | committish [committish...] )\n";
-
typedef struct rev_name {
const char *tip_name;
int generation;
}
}
+static char const * const name_rev_usage[] = {
+ "git-name-rev [options] ( --all | --stdin | <commit>... )",
+ NULL
+};
+
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = { 0, 0, NULL };
- int as_is = 0, all = 0, transform_stdin = 0;
+ int all = 0, transform_stdin = 0;
struct name_ref_data data = { 0, 0, NULL };
+ struct option opts[] = {
+ OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
+ OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"),
+ OPT_STRING(0, "refs", &data.ref_filter, "pattern",
+ "only use refs matching <pattern>"),
+ OPT_GROUP(""),
+ OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
+ OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
+ OPT_END(),
+ };
git_config(git_default_config);
+ argc = parse_options(argc, argv, opts, name_rev_usage, 0);
+ if (!!all + !!transform_stdin + !!argc > 1) {
+ error("Specify either a list, or --all, not both!");
+ usage_with_options(name_rev_usage, opts);
+ }
+ if (all || transform_stdin)
+ cutoff = 0;
- if (argc < 2)
- usage(name_rev_usage);
-
- for (--argc, ++argv; argc; --argc, ++argv) {
+ for (; argc; argc--, argv++) {
unsigned char sha1[20];
struct object *o;
struct commit *commit;
- if (!as_is && (*argv)[0] == '-') {
- if (!strcmp(*argv, "--")) {
- as_is = 1;
- continue;
- } else if (!strcmp(*argv, "--name-only")) {
- data.name_only = 1;
- continue;
- } else if (!strcmp(*argv, "--tags")) {
- data.tags_only = 1;
- continue;
- } else if (!prefixcmp(*argv, "--refs=")) {
- data.ref_filter = *argv + 7;
- continue;
- } else if (!strcmp(*argv, "--all")) {
- if (argc > 1)
- die("Specify either a list, or --all, not both!");
- all = 1;
- cutoff = 0;
- continue;
- } else if (!strcmp(*argv, "--stdin")) {
- if (argc > 1)
- die("Specify either a list, or --stdin, not both!");
- transform_stdin = 1;
- cutoff = 0;
- continue;
- }
- usage(name_rev_usage);
- }
-
if (get_sha1(*argv, sha1)) {
fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
*argv);
}
commit = (struct commit *)o;
-
if (cutoff > commit->date)
cutoff = commit->date;
-
add_object_array((struct object *)commit, *argv, &revs);
}
* 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;
static int local;
static int incremental;
static int allow_ofs_delta;
-static const char *pack_tmp_name, *idx_tmp_name;
-static char tmpname[PATH_MAX];
static const char *base_name;
static int progress = 1;
static int window = 10;
static int delta_search_threads = 1;
static int pack_to_stdout;
static int num_preferred_base;
-static struct progress progress_state;
+static struct progress *progress_state;
static int pack_compression_level = Z_DEFAULT_COMPRESSION;
static int pack_compression_seen;
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)
return offset + size;
}
-static int open_object_dir_tmp(const char *path)
-{
- snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path);
- return xmkstemp(tmpname);
-}
-
/* forward declaration for write_pack_file */
static int adjust_perm(const char *path, mode_t mode);
uint32_t nr_remaining = nr_result;
if (do_progress)
- start_progress(&progress_state, "Writing %u objects...", "", nr_result);
- written_list = xmalloc(nr_objects * sizeof(struct object_entry *));
+ progress_state = start_progress("Writing objects", nr_result);
+ written_list = xmalloc(nr_objects * sizeof(*written_list));
do {
unsigned char sha1[20];
+ char *pack_tmp_name = NULL;
if (pack_to_stdout) {
- f = sha1fd(1, "<stdout>");
+ f = sha1fd_throughput(1, "<stdout>", progress_state);
} else {
- int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
+ char tmpname[PATH_MAX];
+ int fd;
+ snprintf(tmpname, sizeof(tmpname),
+ "%s/tmp_pack_XXXXXX", get_object_directory());
+ fd = xmkstemp(tmpname);
pack_tmp_name = xstrdup(tmpname);
f = sha1fd(fd, pack_tmp_name);
}
if (!offset_one)
break;
offset = offset_one;
- if (do_progress)
- display_progress(&progress_state, written);
+ display_progress(progress_state, written);
}
/*
if (pack_to_stdout || nr_written == nr_remaining) {
sha1close(f, sha1, 1);
} else {
- sha1close(f, sha1, 0);
- fixup_pack_header_footer(f->fd, sha1, pack_tmp_name, nr_written);
- close(f->fd);
+ int fd = sha1close(f, NULL, 0);
+ fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written);
+ close(fd);
}
if (!pack_to_stdout) {
mode_t mode = umask(0);
+ char *idx_tmp_name, tmpname[PATH_MAX];
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))
if (rename(idx_tmp_name, tmpname))
die("unable to rename temporary index file: %s",
strerror(errno));
+ free(idx_tmp_name);
+ free(pack_tmp_name);
puts(sha1_to_hex(sha1));
}
/* 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);
free(written_list);
- if (do_progress)
- stop_progress(&progress_state);
+ stop_progress(&progress_state);
if (written != nr_result)
die("wrote %u objects while expecting %u", written, nr_result);
/*
else
object_ix[-1 - ix] = nr_objects;
- if (progress)
- display_progress(&progress_state, nr_objects);
+ display_progress(progress_state, nr_objects);
if (name && no_try_delta(name))
entry->no_try_delta = 1;
progress_lock();
(*processed)++;
- if (progress)
- display_progress(&progress_state, *processed);
+ display_progress(progress_state, *processed);
progress_unlock();
/*
delta_list[n++] = entry;
}
- if (nr_deltas) {
+ if (nr_deltas && n > 1) {
unsigned nr_done = 0;
if (progress)
- start_progress(&progress_state,
- "Deltifying %u objects...", "",
- nr_deltas);
+ progress_state = start_progress("Compressing objects",
+ nr_deltas);
qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
- if (progress)
- stop_progress(&progress_state);
+ stop_progress(&progress_state);
if (nr_done != nr_deltas)
die("inconsistency with delta count");
}
#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);
}
prepare_packed_git();
if (progress)
- start_progress(&progress_state, "Generating pack...",
- "Counting objects: ", 0);
+ progress_state = start_progress("Counting objects", 0);
if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
rp_av[rp_ac] = NULL;
get_object_list(rp_ac, rp_av);
}
- if (progress) {
- stop_progress(&progress_state);
- fprintf(stderr, "Done counting %u objects.\n", nr_objects);
- }
+ stop_progress(&progress_state);
if (non_empty && !nr_result)
return 0;
- if (progress && (nr_objects != nr_result))
- fprintf(stderr, "Result has %u objects.\n", nr_result);
if (nr_result)
prepare_pack(window, depth);
write_pack_file();
#include "refs.h"
#include "object.h"
#include "tag.h"
-
-static const char builtin_pack_refs_usage[] =
-"git-pack-refs [--all] [--prune | --no-prune]";
+#include "parse-options.h"
struct ref_to_prune {
struct ref_to_prune *next;
return 0;
}
+static char const * const pack_refs_usage[] = {
+ "git-pack-refs [options]",
+ NULL
+};
+
int cmd_pack_refs(int argc, const char **argv, const char *prefix)
{
- int i;
- unsigned int flags;
-
- flags = PACK_REFS_PRUNE;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (!strcmp(arg, "--prune")) {
- flags |= PACK_REFS_PRUNE; /* now the default */
- continue;
- }
- if (!strcmp(arg, "--no-prune")) {
- flags &= ~PACK_REFS_PRUNE;
- continue;
- }
- if (!strcmp(arg, "--all")) {
- flags |= PACK_REFS_ALL;
- continue;
- }
- /* perhaps other parameters later... */
- break;
- }
- if (i != argc)
- usage(builtin_pack_refs_usage);
-
+ int all = 0, prune = 1;
+ unsigned int flags = 0;
+ struct option opts[] = {
+ OPT_BOOLEAN(0, "all", &all, "pack everything"),
+ OPT_BOOLEAN(0, "prune", &prune, "prune loose refs (default)"),
+ OPT_END(),
+ };
+
+ if (parse_options(argc, argv, opts, pack_refs_usage, 0))
+ usage_with_options(pack_refs_usage, opts);
+ if (prune)
+ flags |= PACK_REFS_PRUNE;
+ if (all)
+ flags |= PACK_REFS_ALL;
return pack_refs(flags);
}
#include "builtin.h"
#include "cache.h"
+#include "progress.h"
static const char prune_packed_usage[] =
"git-prune-packed [-n] [-q]";
#define DRY_RUN 01
#define VERBOSE 02
+static struct progress *progress;
+
static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
{
struct dirent *de;
printf("rm -f %s\n", pathname);
else if (unlink(pathname) < 0)
error("unable to unlink %s", pathname);
+ display_progress(progress, i + 1);
}
pathname[len] = 0;
rmdir(pathname);
const char *dir = get_object_directory();
int len = strlen(dir);
+ if (opts == VERBOSE)
+ progress = start_progress_delay("Removing duplicate objects",
+ 256, 95, 2);
+
if (len > PATH_MAX - 42)
die("impossible object directory");
memcpy(pathname, dir, len);
sprintf(pathname + len, "%02x/", i);
d = opendir(pathname);
- if (opts == VERBOSE && (d || i == 255))
- fprintf(stderr, "Removing unused objects %d%%...\015",
- ((i+1) * 100) / 256);
if (!d)
continue;
prune_dir(i, d, pathname, len + 3, opts);
closedir(d);
}
- if (opts == VERBOSE)
- fprintf(stderr, "\nDone.\n");
+ stop_progress(&progress);
}
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
#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;
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 (verbose)
+ flags |= TRANSPORT_PUSH_VERBOSE;
+ 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);
}
#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)
{
static int unmerged_files(void)
{
- char b;
- ssize_t len;
- struct child_process cmd;
- const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL};
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.argv = argv_ls_files;
- cmd.git_cmd = 1;
- cmd.out = -1;
-
- if (start_command(&cmd))
- die("Could not run sub-command: git ls-files");
-
- len = xread(cmd.out, &b, 1);
- if (len < 0)
- die("Could not read output from git ls-files: %s",
- strerror(errno));
- finish_command(&cmd);
-
- return len;
+ int i;
+ read_cache();
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (ce_stage(ce))
+ return 1;
+ }
+ return 0;
}
static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
printf("\n");
}
-static int update_index_refresh(void)
+static int update_index_refresh(int fd, struct lock_file *index_lock)
{
- const char *argv_update_index[] = {"update-index", "--refresh", NULL};
- return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
+ int result;
+
+ if (!index_lock) {
+ index_lock = xcalloc(1, sizeof(struct lock_file));
+ fd = hold_locked_index(index_lock, 1);
+ }
+
+ if (read_cache() < 0)
+ return error("Could not read index");
+ result = refresh_cache(0) ? 1 : 0;
+ if (write_cache(fd, active_cache, active_nr) ||
+ close(fd) ||
+ commit_locked_index(index_lock))
+ return error ("Could not refresh index");
+ return result;
}
static void update_index_from_diff(struct diff_queue_struct *q,
struct diff_options *opt, void *data)
{
int i;
+ int *discard_flag = data;
/* do_diff_cache() mangled the index */
discard_cache();
+ *discard_flag = 1;
read_cache();
for (i = 0; i < q->nr; i++) {
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 lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ int index_fd, index_was_discarded = 0;
struct diff_options opt;
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 = &index_was_discarded;
index_fd = hold_locked_index(lock, 1);
+ index_was_discarded = 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);
+
+ if (!index_was_discarded)
+ /* The index is still clobbered from do_diff_cache() */
+ discard_cache();
+ return update_index_refresh(index_fd, lock);
}
static void prepend_reflog_action(const char *action, char *buf, size_t size)
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];
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++;
reset_type = HARD;
i++;
}
+ else if (!strcmp(argv[i], "-q")) {
+ quiet = 1;
+ i++;
+ }
+ else
+ break;
}
if (i < argc && argv[i][0] != '-')
else if (reset_type != NONE)
die("Cannot do %s reset with paths.",
reset_type_names[reset_type]);
- if (read_from_tree(prefix, argv + i, sha1))
- return 1;
- return update_index_refresh() ? 1 : 0;
+ return read_from_tree(prefix, argv + i, sha1);
}
if (reset_type == NONE)
reset_type = MIXED; /* by default */
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. */
break;
case MIXED: /* Report what has not been updated. */
- update_index_refresh();
+ update_index_refresh(0, NULL);
break;
}
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);
#include "run-command.h"
#include "exec_cmd.h"
#include "utf8.h"
+#include "parse-options.h"
/*
* This implements the builtins revert and cherry-pick.
* Copyright (c) 2005 Junio C Hamano
*/
-static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] <commit-ish>";
+static const char * const revert_usage[] = {
+ "git-revert [options] <commit-ish>",
+ NULL
+};
-static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] <commit-ish>";
+static const char * const cherry_pick_usage[] = {
+ "git-cherry-pick [options] <commit-ish>",
+ NULL
+};
-static int edit;
-static int replay;
+static int edit, no_replay, no_commit, needed_deref, mainline;
static enum { REVERT, CHERRY_PICK } action;
-static int no_commit;
static struct commit *commit;
-static int needed_deref;
static const char *me;
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
-static void parse_options(int argc, const char **argv)
+static void parse_args(int argc, const char **argv)
{
- const char *usage_str = action == REVERT ?
- revert_usage : cherry_pick_usage;
+ const char * const * usage_str =
+ action == REVERT ? revert_usage : cherry_pick_usage;
unsigned char sha1[20];
const char *arg;
- int i;
+ int noop;
+ struct option options[] = {
+ OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
+ 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 (argc < 2)
- usage(usage_str);
-
- for (i = 1; i < argc; i++) {
- arg = argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit"))
- no_commit = 1;
- else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit"))
- edit = 1;
- else if (!strcmp(arg, "--no-edit"))
- edit = 0;
- else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-"
- "to-expose-my-private-commit-object-name"))
- replay = 0;
- else if (strcmp(arg, "-r"))
- usage(usage_str);
- }
- if (i != argc - 1)
- usage(usage_str);
- arg = argv[argc - 1];
if (get_sha1(arg, sha1))
die ("Cannot find '%s'", arg);
commit = (struct commit *)parse_object(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;
git_config(git_default_config);
me = action == REVERT ? "revert" : "cherry-pick";
setenv(GIT_REFLOG_ACTION, me, 0);
- parse_options(argc, argv);
+ parse_args(argc, argv);
/* this is copied from the shell script, but it's never triggered... */
- if (action == REVERT && replay)
+ if (action == REVERT && !no_replay)
die("revert is incompatible with replay");
if (no_commit) {
/*
* We do not intend to commit immediately. We just want to
- * merge the differences in.
+ * merge the differences in, so let's compute the tree
+ * that represents the "current" state for merge-recursive
+ * to work on.
*/
if (write_tree(head, 0, NULL))
die ("Your index file is unmerged.");
if (get_sha1("HEAD", head))
die ("You do not have a valid HEAD");
wt_status_prepare(&s);
- if (s.commitable || s.workdir_dirty)
+ if (s.commitable)
die ("Dirty index: cannot %s", me);
discard_cache();
}
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));
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);
- if (!replay) {
+ if (no_replay) {
add_to_msg("(cherry picked from commit ");
add_to_msg(sha1_to_hex(commit->object.sha1));
add_to_msg(")\n");
{
if (isatty(0))
edit = 1;
+ no_replay = 1;
action = REVERT;
return revert_or_cherry_pick(argc, argv);
}
int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
{
- replay = 1;
+ no_replay = 0;
action = CHERRY_PICK;
return revert_or_cherry_pick(argc, argv);
}
#include "dir.h"
#include "cache-tree.h"
#include "tree-walk.h"
+#include "parse-options.h"
-static const char builtin_rm_usage[] =
-"git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...";
+static const char * const builtin_rm_usage[] = {
+ "git-rm [options] [--] <file>...",
+ NULL
+};
static struct {
int nr, alloc;
static struct lock_file lock_file;
+static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
+static int ignore_unmatch = 0;
+
+static struct option builtin_rm_options[] = {
+ OPT__DRY_RUN(&show_only),
+ OPT__QUIET(&quiet),
+ OPT_BOOLEAN( 0 , "cached", &index_only, "only remove from the index"),
+ OPT_BOOLEAN('f', NULL, &force, "override the up-to-date check"),
+ OPT_BOOLEAN('r', NULL, &recursive, "allow recursive removal"),
+ OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
+ "exit with a zero status even if nothing matched"),
+ OPT_END(),
+};
+
int cmd_rm(int argc, const char **argv, const char *prefix)
{
int i, newfd;
- int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
- int ignore_unmatch = 0;
const char **pathspec;
char *seen;
if (read_cache() < 0)
die("index file corrupt");
- for (i = 1 ; i < argc ; i++) {
- const char *arg = argv[i];
+ argc = parse_options(argc, argv, builtin_rm_options, builtin_rm_usage, 0);
+ if (!argc)
+ usage_with_options(builtin_rm_usage, builtin_rm_options);
- if (*arg != '-')
- break;
- else if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- else if (!strcmp(arg, "-n"))
- show_only = 1;
- else if (!strcmp(arg, "--cached"))
- index_only = 1;
- else if (!strcmp(arg, "-f"))
- force = 1;
- else if (!strcmp(arg, "-r"))
- recursive = 1;
- else if (!strcmp(arg, "--quiet"))
- quiet = 1;
- else if (!strcmp(arg, "--ignore-unmatch"))
- ignore_unmatch = 1;
- else
- usage(builtin_rm_usage);
- }
- if (argc <= i)
- usage(builtin_rm_usage);
+ if (!index_only)
+ setup_work_tree();
- pathspec = get_pathspec(prefix, argv + i);
+ pathspec = get_pathspec(prefix, argv);
seen = NULL;
for (i = 0; pathspec[i] ; i++)
/* nothing */;
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] "))
#include "builtin.h"
#include "cache.h"
#include "refs.h"
+#include "parse-options.h"
-static const char git_symbolic_ref_usage[] =
-"git-symbolic-ref [-q] [-m <reason>] name [ref]";
+static const char * const git_symbolic_ref_usage[] = {
+ "git-symbolic-ref [options] name [ref]",
+ NULL
+};
static void check_symref(const char *HEAD, int quiet)
{
{
int quiet = 0;
const char *msg = NULL;
+ struct option options[] = {
+ OPT__QUIET(&quiet),
+ OPT_STRING('m', NULL, &msg, "reason", "reason of the update"),
+ OPT_END(),
+ };
git_config(git_default_config);
-
- while (1 < argc) {
- const char *arg = argv[1];
- if (arg[0] != '-')
- break;
- else if (!strcmp("-q", arg))
- quiet = 1;
- else if (!strcmp("-m", arg)) {
- argc--;
- argv++;
- if (argc <= 1)
- break;
- msg = argv[1];
- if (!*msg)
- die("Refusing to perform update with empty message");
- }
- else if (!strcmp("--", arg)) {
- argc--;
- argv++;
- break;
- }
- else
- die("unknown option %s", arg);
- argc--;
- argv++;
- }
-
+ argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0);
+ if (msg &&!*msg)
+ die("Refusing to perform update with empty message");
switch (argc) {
- case 2:
- check_symref(argv[1], quiet);
+ case 1:
+ check_symref(argv[0], quiet);
break;
- case 3:
- create_symref(argv[1], argv[2], msg);
+ case 2:
+ create_symref(argv[0], argv[1], msg);
break;
default:
- usage(git_symbolic_ref_usage);
+ usage_with_options(git_symbolic_ref_usage, options);
}
return 0;
}
}
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 &&
static void unpack_all(void)
{
int i;
- struct progress progress;
+ struct progress *progress = NULL;
struct pack_header *hdr = fill(sizeof(struct pack_header));
unsigned nr_objects = ntohl(hdr->hdr_entries);
use(sizeof(struct pack_header));
if (!quiet)
- start_progress(&progress, "Unpacking %u objects...", "", nr_objects);
+ progress = start_progress("Unpacking objects", nr_objects);
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
for (i = 0; i < nr_objects; i++) {
unpack_one(i);
- if (!quiet)
- display_progress(&progress, i + 1);
+ display_progress(progress, i + 1);
}
- if (!quiet)
- stop_progress(&progress);
+ stop_progress(&progress);
if (delta_list)
die("unresolved deltas left after unpacking");
#include "cache.h"
#include "refs.h"
#include "builtin.h"
+#include "parse-options.h"
-static const char git_update_ref_usage[] =
-"git-update-ref [-m <reason>] (-d <refname> <value> | [--no-deref] <refname> <value> [<oldval>])";
+static const char * const git_update_ref_usage[] = {
+ "git-update-ref [options] -d <refname> <oldval>",
+ "git-update-ref [options] <refname> <newval> [<oldval>]",
+ NULL
+};
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
- const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
+ const char *refname, *value, *oldval, *msg=NULL;
unsigned char sha1[20], oldsha1[20];
- int i, delete, ref_flags;
+ int delete = 0, no_deref = 0;
+ struct option options[] = {
+ OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"),
+ OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"),
+ OPT_BOOLEAN( 0 , "no-deref", &no_deref,
+ "update <refname> not the one it points to"),
+ OPT_END(),
+ };
- delete = 0;
- ref_flags = 0;
git_config(git_default_config);
+ argc = parse_options(argc, argv, options, git_update_ref_usage, 0);
+ if (msg && !*msg)
+ die("Refusing to perform update with empty message.");
- for (i = 1; i < argc; i++) {
- if (!strcmp("-m", argv[i])) {
- if (i+1 >= argc)
- usage(git_update_ref_usage);
- msg = argv[++i];
- if (!*msg)
- die("Refusing to perform update with empty message.");
- continue;
- }
- if (!strcmp("-d", argv[i])) {
- delete = 1;
- continue;
- }
- if (!strcmp("--no-deref", argv[i])) {
- ref_flags |= REF_NODEREF;
- continue;
- }
- if (!refname) {
- refname = argv[i];
- continue;
- }
- if (!value) {
- value = argv[i];
- continue;
- }
- if (!oldval) {
- oldval = argv[i];
- continue;
- }
- }
- if (!refname || !value)
- usage(git_update_ref_usage);
+ if (argc < 2 || argc > 3)
+ usage_with_options(git_update_ref_usage, options);
+ refname = argv[0];
+ value = argv[1];
+ oldval = argv[2];
if (get_sha1(value, sha1))
die("%s: not a valid SHA1", value);
if (delete) {
if (oldval)
- usage(git_update_ref_usage);
+ usage_with_options(git_update_ref_usage, options);
return delete_ref(refname, sha1);
}
die("%s: not a valid old SHA1", oldval);
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
- ref_flags, DIE_ON_ERR);
+ no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
}
}
/* 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;
#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
#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);
#include "commit.h"
#include "pkt-line.h"
#include "utf8.h"
-#include "interpolate.h"
#include "diff.h"
#include "revision.h"
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)
}
}
-/*
- * 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;
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.
#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>
#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>
NO_STRLCPY=@NO_STRLCPY@
NO_SETENV=@NO_SETENV@
NO_ICONV=@NO_ICONV@
+NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
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); \
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],
[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])
])
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:
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":
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)
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)
#
# 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
# ------
# 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.
# 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
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)
# 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
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
#
# 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
#
# 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:
# \
# * --- 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,
# 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.
-
- # 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
+ # 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
fast_forward=""
rev=""
for rev in $(git rev-list $newrev..$oldrev)
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")
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
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
#
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)
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
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
#
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
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
}
# ---------------------------- main()
# --- Constants
-EMAILPREFIX="[SCM] "
LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------"
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"
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
* able to verify hasn't been messed with afterwards.
*/
#include "cache.h"
+#include "progress.h"
#include "csum-file.h"
static void sha1flush(struct sha1file *f, unsigned int count)
for (;;) {
int ret = xwrite(f->fd, buf, count);
if (ret > 0) {
+ display_throughput(f->tp, ret);
buf = (char *) buf + ret;
count -= ret;
if (count)
int sha1close(struct sha1file *f, unsigned char *result, int final)
{
+ int fd;
unsigned offset = f->offset;
if (offset) {
SHA1_Update(&f->ctx, f->buffer, offset);
sha1flush(f, offset);
f->offset = 0;
}
- if (!final)
- return 0; /* only want to flush (no checksum write, no close) */
- SHA1_Final(f->buffer, &f->ctx);
- if (result)
- hashcpy(result, f->buffer);
- sha1flush(f, 20);
- if (close(f->fd))
- die("%s: sha1 file error on close (%s)", f->name, strerror(errno));
+ if (final) {
+ /* write checksum and close fd */
+ SHA1_Final(f->buffer, &f->ctx);
+ if (result)
+ hashcpy(result, f->buffer);
+ sha1flush(f, 20);
+ if (close(f->fd))
+ die("%s: sha1 file error on close (%s)",
+ f->name, strerror(errno));
+ fd = 0;
+ } else
+ fd = f->fd;
free(f);
- return 0;
+ return fd;
}
int sha1write(struct sha1file *f, void *buf, unsigned int count)
}
struct sha1file *sha1fd(int fd, const char *name)
+{
+ return sha1fd_throughput(fd, name, NULL);
+}
+
+struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp)
{
struct sha1file *f;
unsigned len;
f->fd = fd;
f->error = 0;
f->offset = 0;
+ f->tp = tp;
f->do_crc = 0;
SHA1_Init(&f->ctx);
return f;
#ifndef CSUM_FILE_H
#define CSUM_FILE_H
+struct progress;
+
/* A SHA1-protected file */
struct sha1file {
int fd, error;
unsigned int offset, namelen;
SHA_CTX ctx;
+ struct progress *tp;
char name[PATH_MAX];
int do_crc;
uint32_t crc32;
};
extern struct sha1file *sha1fd(int fd, const char *name);
+extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);
extern int sha1close(struct sha1file *, unsigned char *, int);
extern int sha1write(struct sha1file *, void *, unsigned int);
extern void crc32_begin(struct sha1file *);
{ "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)) {
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)) {
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;
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);
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);
#define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
+#define DEPTH_BITS 13
+#define MAX_DEPTH ((1<<DEPTH_BITS)-1)
struct object_entry
{
struct object_entry *next;
uint32_t offset;
- unsigned type : TYPE_BITS;
- unsigned pack_id : PACK_ID_BITS;
+ uint32_t type : TYPE_BITS,
+ pack_id : PACK_ID_BITS,
+ depth : DEPTH_BITS;
unsigned char sha1[20];
};
unsigned pos = sizeof(hdr) - 1;
delta_count_by_type[type]++;
- last->depth++;
+ e->depth = last->depth + 1;
hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
write_or_die(pack_data->pack_fd, hdr, hdrlen);
write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
pack_size += sizeof(hdr) - pos;
} else {
- if (last)
- last->depth = 0;
+ e->depth = 0;
hdrlen = encode_header(type, dat->len, hdr);
write_or_die(pack_data->pack_fd, hdr, hdrlen);
pack_size += hdrlen;
strbuf_swap(&last->data, dat);
}
last->offset = e->offset;
+ last->depth = e->depth;
}
return 0;
}
if (myoe && myoe->pack_id != MAX_PACK_ID) {
if (myoe->type != OBJ_TREE)
die("Not a tree: %s", sha1_to_hex(sha1));
- t->delta_depth = 0;
+ t->delta_depth = myoe->depth;
buf = gfi_unpack_entry(myoe, &size);
} else {
enum object_type type;
}
else if (!prefixcmp(a, "--max-pack-size="))
max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
- else if (!prefixcmp(a, "--depth="))
+ else if (!prefixcmp(a, "--depth=")) {
max_depth = strtoul(a + 8, NULL, 0);
+ if (max_depth > MAX_DEPTH)
+ die("--depth cannot exceed %u", MAX_DEPTH);
+ }
else if (!prefixcmp(a, "--active-branches="))
max_active_branches = strtoul(a + 18, NULL, 0);
else if (!prefixcmp(a, "--import-marks="))
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
ignored=
ignoredonly=
cleandir=
-disabled="`git config --bool clean.requireForce`"
rmf="rm -f --"
rmrf="rm -rf --"
rm_refuse="echo Not removing"
echo1="echo"
+disabled=$(git config --bool clean.requireForce)
+
while test $# != 0
do
case "$1" in
cleandir=1
;;
-f)
- disabled=
+ disabled=false
;;
-n)
- disabled=
+ disabled=false
rmf="echo Would remove"
rmrf="echo Would remove"
rm_refuse="echo Would not remove"
shift
done
-if [ "$disabled" = true ]; then
- echo "clean.requireForce set and -n or -f not given; refusing to clean"
- exit 1
-fi
+# 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".
+case "$disabled" in
+"")
+ die "clean.requireForce not set and -n or -f not given; refusing to clean"
+ ;;
+"true")
+ die "clean.requireForce set and -n or -f not given; refusing to clean"
+ ;;
+esac
case "$ignored,$ignoredonly" in
1,1) usage;;
if [ -z "$ignored" ]; then
excl="--exclude-per-directory=.gitignore"
+ excl_info= excludes_file=
if [ -f "$GIT_DIR/info/exclude" ]; then
excl_info="--exclude-from=$GIT_DIR/info/exclude"
fi
+ if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl"
+ then
+ excludes_file="--exclude-from=$cfg_excl"
+ fi
if [ "$ignoredonly" ]; then
excl="$excl --ignored"
fi
fi
-git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
+git ls-files --others --directory \
+ $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \
+ -- "$@" |
while read -r file; do
if [ -d "$file" -a ! -L "$file" ]; then
if [ -z "$cleandir" ]; then
}
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() {
*,--depth)
shift
depth="--depth=$1";;
+ *,--)
+ shift
+ break ;;
*,-*) usage ;;
*) break ;;
esac
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
}
}
- $user="anonymous" unless defined $user;
+ # if username is not explicit in CVSROOT, then use current user, as cvs would
+ $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
my $rr2 = "-";
unless ($port) {
$rr2 = ":pserver:$user\@$serv:$repo";
$state = 4;
} elsif ($state == 4 and s/^Branch:\s+//) {
s/\s+$//;
+ tr/_/\./ if ( $opt_u );
s/[\/]/$opt_s/g;
$branch = $_;
$state = 5;
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:
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
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"
}
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'";#'
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*)
esac
start_httpd
-test -z "$browser" && browser=echo
url=http://127.0.0.1:$port
-$browser $url || echo $url
+"$browser" $url || echo $url
SUBDIRECTORY_OK='Yes'
. git-sh-setup
+echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2
+
if [ "$#" != "0" ]
then
usage
-s|--strategy)
case "$#,$1" in
*,*=*)
- STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;;
+ STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
1,*)
usage ;;
*)
fullbases="$fullbases pack-$name"
chmod a-w "$PACKTMP-$name.pack"
chmod a-w "$PACKTMP-$name.idx"
- if test "$quiet" != '-q'; then
- echo "Pack pack-$name created."
- fi
mkdir -p "$PACKDIR" || exit
for sfx in pack idx
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
--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.
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 = $_;
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;
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;
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' )
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:
sub cmd_dcommit {
my $head = shift;
+ git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
+ 'Cannot dcommit with a dirty index. Commit your changes first'
+ . "or stash them with `git stash'.\n";
$head ||= 'HEAD';
my @refs;
my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
"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) {
# 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,
@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;
}
}
]
}
+sub escape_uri_only {
+ my ($uri) = @_;
+ my @tmp;
+ foreach (split m{/}, $uri) {
+ s/([^\w.-])/sprintf("%%%02X",ord($1))/eg;
+ push @tmp, $_;
+ }
+ join('/', @tmp);
+}
+
+sub escape_url {
+ my ($url) = @_;
+ if ($url =~ m#^(https?)://([^/]+)(.*)$#) {
+ my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
+ $url = "$scheme://$domain$uri";
+ }
+ $url;
+}
+
sub new {
my ($class, $url) = @_;
$url =~ s!/+$!!;
$Git::SVN::Prompt::_no_auth_cache = 1;
}
} # no warnings 'once'
- my $self = SVN::Ra->new(url => $url, auth => $baton,
+ my $self = SVN::Ra->new(url => escape_url($url), auth => $baton,
config => $config,
pool => SVN::Pool->new,
auth_provider_callbacks => $callbacks);
+ $self->{url} = $url;
$self->{svn_path} = $url;
$self->{repos_root} = $self->get_repos_root;
$self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##;
my $full_url = $self->{url};
my $old_url = $full_url;
- $full_url .= "/$path" if length $path;
+ $full_url .= '/' . escape_uri_only($path) if length $path;
my ($ra, $reparented);
if ($old_url ne $full_url) {
if ($old_url !~ m#^svn(\+ssh)?://#) {
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);
{ "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 },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
);
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');
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
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) {
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;
$date{'mday-time'} = sprintf "%d %s %02d:%02d",
$mday, $months[$mon], $hour ,$min;
$date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ",
- 1900+$year, $mon, $mday, $hour ,$min, $sec;
+ 1900+$year, 1+$mon, $mday, $hour ,$min, $sec;
$tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
my $local = $epoch + ((int $1 + ($2/60)) * 3600);
$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'
$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);
}
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],
}
} 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'},
}
}
- $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'},
if ($page > 0) {
$paging_nav .= " ⋅ " .
- $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 .= " ⋅ prev";
if ($nrevs >= (100 * ($page+1)-1)) {
$paging_nav .= " ⋅ " .
- $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 .= " ⋅ next";
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
my ($diffinfo, $patchinfo) = @_;
return defined $diffinfo && defined $patchinfo
- && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'};
+ && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'};
}
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");
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",
"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);
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)},
}
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";
}
}
%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'};
# 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))) {
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 &&
file_name=>$file_name)},
"first");
$paging_nav .= " ⋅ " .
- $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 .= " ⋅ prev";
}
- if ($#commitlist >= 100) {
- $paging_nav .= " ⋅ " .
- $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 .= " ⋅ 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 .= " ⋅ $next_link";
+ } else {
+ $paging_nav .= " ⋅ next";
}
git_header_html();
searchtext=>$searchtext, searchtype=>$searchtype)},
"first");
$paging_nav .= " ⋅ " .
- $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 .= " ⋅ prev";
}
+ my $next_link = '';
if ($#commitlist >= 100) {
- $paging_nav .= " ⋅ " .
- $cgi->a({-href => href(action=>"search", hash=>$hash,
- searchtext=>$searchtext, searchtype=>$searchtype,
- page=>$page+1),
+ $next_link =
+ $cgi->a({-href => href(-replay=>1, page=>$page+1),
-accesskey => "n", -title => "Alt-n"}, "next");
+ $paging_nav .= " ⋅ $next_link";
} else {
$paging_nav .= " ⋅ next";
}
- my $next_link = '';
+
if ($#commitlist >= 100) {
- $next_link =
- $cgi->a({-href => href(action=>"search", hash=>$hash,
- searchtext=>$searchtext, searchtype=>$searchtype,
- page=>$page+1),
- -accesskey => "n", -title => "Alt-n"}, "next");
}
git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
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");
}
int prefix_length = -1;
int no_more_flags = 0;
+ git_config(git_default_config);
+
for (i = 1 ; i < argc; i++) {
if (!no_more_flags && argv[i][0] == '-') {
if (!strcmp(argv[i], "-t")) {
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;
/* 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);
}
}
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);
static int from_stdin;
static int verbose;
-static struct progress progress;
+static struct progress *progress;
/* We always read in 4kB chunks. */
static unsigned char input_buffer[4096];
die("early EOF");
die("read error on input: %s", strerror(errno));
}
+ if (from_stdin)
+ display_throughput(progress, ret);
input_len += ret;
} while (input_len < min);
return input_buffer;
consumed_bytes += bytes;
}
-static const char *open_pack_file(const char *pack_name)
+static char *open_pack_file(char *pack_name)
{
if (from_stdin) {
input_fd = 0;
static void *get_data_from_pack(struct object_entry *obj)
{
- unsigned long from = obj[0].idx.offset + obj[0].hdr_size;
+ off_t from = obj[0].idx.offset + obj[0].hdr_size;
unsigned long len = obj[1].idx.offset - from;
unsigned long rdy = 0;
unsigned char *src, *data;
* - remember base (SHA1 or offset) for all deltas.
*/
if (verbose)
- start_progress(&progress, "Indexing %u objects...", "", nr_objects);
+ progress = start_progress(
+ from_stdin ? "Receiving objects" : "Indexing objects",
+ nr_objects);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
data = unpack_raw_entry(obj, &delta->base);
} else
sha1_object(data, obj->size, obj->type, obj->idx.sha1);
free(data);
- if (verbose)
- display_progress(&progress, i+1);
+ display_progress(progress, i+1);
}
objects[i].idx.offset = consumed_bytes;
- if (verbose)
- stop_progress(&progress);
+ stop_progress(&progress);
/* Check pack integrity */
flush();
* for some more deltas.
*/
if (verbose)
- start_progress(&progress, "Resolving %u deltas...", "", nr_deltas);
+ progress = start_progress("Resolving deltas", nr_deltas);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
union delta_base base;
obj->size, obj->type);
}
free(data);
- if (verbose)
- display_progress(&progress, nr_resolved_deltas);
+ display_progress(progress, nr_resolved_deltas);
}
}
die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
append_obj_to_pack(d->base.sha1, data, size, type);
free(data);
- if (verbose)
- display_progress(&progress, nr_resolved_deltas);
+ display_progress(progress, nr_resolved_deltas);
}
free(sorted_by_pos);
}
}
}
+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;
- const char *curr_pack, *pack_name = NULL;
- const char *curr_index, *index_name = NULL;
+ char *curr_pack, *pack_name = NULL;
+ char *curr_index, *index_name = NULL;
const char *keep_name = NULL, *keep_msg = NULL;
char *index_name_buf = NULL, *keep_name_buf = NULL;
struct pack_idx_entry **idx_objects;
unsigned char sha1[20];
+ git_config(git_index_pack_config);
+
for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ char *arg = argv[i];
if (*arg == '-') {
if (!strcmp(arg, "--stdin")) {
deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
parse_pack_objects(sha1);
if (nr_deltas == nr_resolved_deltas) {
- if (verbose)
- stop_progress(&progress);
+ stop_progress(&progress);
/* Flush remaining pack final 20-byte SHA1. */
flush();
} else {
(nr_objects + nr_unresolved + 1)
* sizeof(*objects));
fix_unresolved_deltas(nr_unresolved);
- if (verbose) {
- stop_progress(&progress);
+ stop_progress(&progress);
+ if (verbose)
fprintf(stderr, "%d objects were added to complete this thin pack.\n",
nr_objects - nr_objects_initial);
- }
fixup_pack_header_footer(output_fd, sha1,
curr_pack, nr_objects);
}
free(objects);
free(index_name_buf);
free(keep_name_buf);
+ if (pack_name == NULL)
+ free(curr_pack);
+ if (index_name == NULL)
+ free(curr_index);
return 0;
}
}
for (i = 0; i < objects.nr; i++)
show_object(&objects.objects[i]);
+ free(objects.objects);
+ if (revs->pending.nr) {
+ free(revs->pending.objects);
+ revs->pending.nr = 0;
+ revs->pending.alloc = 0;
+ revs->pending.objects = NULL;
+ }
}
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;
*/
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);
* the SHA1 hash of sorted object names. The objects array passed in
* will be sorted by SHA1 on exit.
*/
-const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1)
+char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
+ int nr_objects, unsigned char *sha1)
{
struct sha1file *f;
struct pack_idx_entry **sorted_by_sha, **list, **last;
off_t offset;
};
-extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
+extern char *write_idx_file(char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
extern int verify_pack(struct packed_git *, int);
extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t);
--- /dev/null
+#include "git-compat-util.h"
+#include "parse-options.h"
+
+#define OPT_SHORT 1
+#define OPT_UNSET 2
+
+struct optparse_t {
+ const char **argv;
+ int argc;
+ const char *opt;
+};
+
+static inline const char *get_arg(struct optparse_t *p)
+{
+ if (p->opt) {
+ const char *res = p->opt;
+ p->opt = NULL;
+ return res;
+ }
+ p->argc--;
+ return *++p->argv;
+}
+
+static inline const char *skip_prefix(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+static int opterror(const struct option *opt, const char *reason, int flags)
+{
+ if (flags & OPT_SHORT)
+ return error("switch `%c' %s", opt->short_name, reason);
+ if (flags & OPT_UNSET)
+ return error("option `no-%s' %s", opt->long_name, reason);
+ return error("option `%s' %s", opt->long_name, reason);
+}
+
+static int get_value(struct optparse_t *p,
+ const struct option *opt, int flags)
+{
+ const char *s, *arg;
+ arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
+
+ if (p->opt && (flags & OPT_UNSET))
+ return opterror(opt, "takes no value", flags);
+
+ switch (opt->type) {
+ case OPTION_BOOLEAN:
+ if (!(flags & OPT_SHORT) && p->opt)
+ return opterror(opt, "takes no value", flags);
+ if (flags & OPT_UNSET)
+ *(int *)opt->value = 0;
+ else
+ (*(int *)opt->value)++;
+ return 0;
+
+ case OPTION_STRING:
+ if (flags & OPT_UNSET) {
+ *(const char **)opt->value = (const char *)NULL;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) {
+ *(const char **)opt->value = (const char *)opt->defval;
+ return 0;
+ }
+ if (!arg)
+ return opterror(opt, "requires a value", flags);
+ *(const char **)opt->value = get_arg(p);
+ return 0;
+
+ case OPTION_CALLBACK:
+ if (flags & OPT_UNSET)
+ return (*opt->callback)(opt, NULL, 1);
+ if (opt->flags & PARSE_OPT_NOARG) {
+ if (p->opt && !(flags & OPT_SHORT))
+ return opterror(opt, "takes no value", flags);
+ return (*opt->callback)(opt, NULL, 0);
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-'))
+ return (*opt->callback)(opt, NULL, 0);
+ if (!arg)
+ return opterror(opt, "requires a value", flags);
+ return (*opt->callback)(opt, get_arg(p), 0);
+
+ case OPTION_INTEGER:
+ if (flags & OPT_UNSET) {
+ *(int *)opt->value = 0;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) {
+ *(int *)opt->value = opt->defval;
+ return 0;
+ }
+ if (!arg)
+ return opterror(opt, "requires a value", flags);
+ *(int *)opt->value = strtol(get_arg(p), (char **)&s, 10);
+ if (*s)
+ return opterror(opt, "expects a numerical value", flags);
+ return 0;
+
+ default:
+ die("should not happen, someone must be hit on the forehead");
+ }
+}
+
+static int parse_short_opt(struct optparse_t *p, const struct option *options)
+{
+ for (; options->type != OPTION_END; options++) {
+ if (options->short_name == *p->opt) {
+ p->opt = p->opt[1] ? p->opt + 1 : NULL;
+ return get_value(p, options, OPT_SHORT);
+ }
+ }
+ return error("unknown switch `%c'", *p->opt);
+}
+
+static int parse_long_opt(struct optparse_t *p, const char *arg,
+ const struct option *options)
+{
+ const char *arg_end = strchr(arg, '=');
+ const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
+ int abbrev_flags = 0, ambiguous_flags = 0;
+
+ if (!arg_end)
+ arg_end = arg + strlen(arg);
+
+ for (; options->type != OPTION_END; options++) {
+ const char *rest;
+ int flags = 0;
+
+ if (!options->long_name)
+ continue;
+
+ rest = skip_prefix(arg, options->long_name);
+ if (!rest) {
+ /* abbreviated? */
+ if (!strncmp(options->long_name, arg, arg_end - arg)) {
+is_abbreviated:
+ if (abbrev_option) {
+ /*
+ * If this is abbreviated, it is
+ * ambiguous. So when there is no
+ * exact match later, we need to
+ * error out.
+ */
+ ambiguous_option = abbrev_option;
+ ambiguous_flags = abbrev_flags;
+ }
+ if (!(flags & OPT_UNSET) && *arg_end)
+ p->opt = arg_end + 1;
+ abbrev_option = options;
+ abbrev_flags = flags;
+ continue;
+ }
+ /* negated and abbreviated very much? */
+ if (!prefixcmp("no-", arg)) {
+ flags |= OPT_UNSET;
+ goto is_abbreviated;
+ }
+ /* negated? */
+ if (strncmp(arg, "no-", 3))
+ continue;
+ flags |= OPT_UNSET;
+ rest = skip_prefix(arg + 3, options->long_name);
+ /* abbreviated and negated? */
+ if (!rest && !prefixcmp(options->long_name, arg + 3))
+ goto is_abbreviated;
+ if (!rest)
+ continue;
+ }
+ if (*rest) {
+ if (*rest != '=')
+ continue;
+ p->opt = rest + 1;
+ }
+ return get_value(p, options, flags);
+ }
+
+ if (ambiguous_option)
+ return error("Ambiguous option: %s "
+ "(could be --%s%s or --%s%s)",
+ arg,
+ (ambiguous_flags & OPT_UNSET) ? "no-" : "",
+ ambiguous_option->long_name,
+ (abbrev_flags & OPT_UNSET) ? "no-" : "",
+ abbrev_option->long_name);
+ if (abbrev_option)
+ return get_value(p, abbrev_option, abbrev_flags);
+ return error("unknown option `%s'", arg);
+}
+
+int parse_options(int argc, const char **argv, const struct option *options,
+ const char * const usagestr[], int flags)
+{
+ struct optparse_t args = { argv + 1, argc - 1, NULL };
+ int j = 0;
+
+ for (; args.argc; args.argc--, args.argv++) {
+ const char *arg = args.argv[0];
+
+ if (*arg != '-' || !arg[1]) {
+ argv[j++] = args.argv[0];
+ continue;
+ }
+
+ if (arg[1] != '-') {
+ args.opt = arg + 1;
+ do {
+ if (*args.opt == 'h')
+ usage_with_options(usagestr, options);
+ if (parse_short_opt(&args, options) < 0)
+ usage_with_options(usagestr, options);
+ } while (args.opt);
+ continue;
+ }
+
+ if (!arg[2]) { /* "--" */
+ if (!(flags & PARSE_OPT_KEEP_DASHDASH)) {
+ args.argc--;
+ args.argv++;
+ }
+ break;
+ }
+
+ if (!strcmp(arg + 2, "help"))
+ usage_with_options(usagestr, options);
+ if (parse_long_opt(&args, arg + 2, options))
+ usage_with_options(usagestr, options);
+ }
+
+ memmove(argv + j, args.argv, args.argc * sizeof(*argv));
+ argv[j + args.argc] = NULL;
+ return j + args.argc;
+}
+
+#define USAGE_OPTS_WIDTH 24
+#define USAGE_GAP 2
+
+void usage_with_options(const char * const *usagestr,
+ const struct option *opts)
+{
+ fprintf(stderr, "usage: %s\n", *usagestr++);
+ while (*usagestr && **usagestr)
+ fprintf(stderr, " or: %s\n", *usagestr++);
+ while (*usagestr)
+ fprintf(stderr, " %s\n", *usagestr++);
+
+ if (opts->type != OPTION_GROUP)
+ fputc('\n', stderr);
+
+ for (; opts->type != OPTION_END; opts++) {
+ size_t pos;
+ int pad;
+
+ if (opts->type == OPTION_GROUP) {
+ fputc('\n', stderr);
+ if (*opts->help)
+ fprintf(stderr, "%s\n", opts->help);
+ continue;
+ }
+
+ pos = fprintf(stderr, " ");
+ if (opts->short_name)
+ pos += fprintf(stderr, "-%c", opts->short_name);
+ if (opts->long_name && opts->short_name)
+ pos += fprintf(stderr, ", ");
+ if (opts->long_name)
+ pos += fprintf(stderr, "--%s", opts->long_name);
+
+ switch (opts->type) {
+ case OPTION_INTEGER:
+ if (opts->flags & PARSE_OPT_OPTARG)
+ pos += fprintf(stderr, " [<n>]");
+ else
+ pos += fprintf(stderr, " <n>");
+ break;
+ case OPTION_CALLBACK:
+ if (opts->flags & PARSE_OPT_NOARG)
+ break;
+ /* FALLTHROUGH */
+ case OPTION_STRING:
+ if (opts->argh) {
+ if (opts->flags & PARSE_OPT_OPTARG)
+ pos += fprintf(stderr, " [<%s>]", opts->argh);
+ else
+ pos += fprintf(stderr, " <%s>", opts->argh);
+ } else {
+ if (opts->flags & PARSE_OPT_OPTARG)
+ pos += fprintf(stderr, " [...]");
+ else
+ pos += fprintf(stderr, " ...");
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (pos <= USAGE_OPTS_WIDTH)
+ pad = USAGE_OPTS_WIDTH - pos;
+ else {
+ fputc('\n', stderr);
+ pad = USAGE_OPTS_WIDTH;
+ }
+ fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+ }
+ fputc('\n', stderr);
+
+ exit(129);
+}
+
+/*----- some often used options -----*/
+#include "cache.h"
+int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
+{
+ int v;
+
+ if (!arg) {
+ v = unset ? 0 : DEFAULT_ABBREV;
+ } else {
+ v = strtol(arg, (char **)&arg, 10);
+ if (*arg)
+ return opterror(opt, "expects a numerical value", 0);
+ if (v && v < MINIMUM_ABBREV)
+ v = MINIMUM_ABBREV;
+ else if (v > 40)
+ v = 40;
+ }
+ *(int *)(opt->value) = v;
+ return 0;
+}
--- /dev/null
+#ifndef PARSE_OPTIONS_H
+#define PARSE_OPTIONS_H
+
+enum parse_opt_type {
+ OPTION_END,
+ OPTION_GROUP,
+ OPTION_BOOLEAN,
+ OPTION_STRING,
+ OPTION_INTEGER,
+ OPTION_CALLBACK,
+};
+
+enum parse_opt_flags {
+ PARSE_OPT_KEEP_DASHDASH = 1,
+};
+
+enum parse_opt_option_flags {
+ PARSE_OPT_OPTARG = 1,
+ PARSE_OPT_NOARG = 2,
+};
+
+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;
+ const char *long_name;
+ void *value;
+ const char *argh;
+ const char *help;
+
+ int flags;
+ parse_opt_cb *callback;
+ intptr_t defval;
+};
+
+#define OPT_END() { OPTION_END }
+#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
+#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
+#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) }
+#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
+#define OPT_CALLBACK(s, l, v, a, h, f) \
+ { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
+
+/* parse_options() will filter out the processed options and leave the
+ * non-option argments in argv[].
+ * Returns the number of arguments left in argv[].
+ */
+extern int parse_options(int argc, const char **argv,
+ const struct option *options,
+ const char * const usagestr[], int flags);
+
+extern NORETURN void usage_with_options(const char * const *usagestr,
+ const struct option *options);
+
+/*----- some often used options -----*/
+extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
+
+#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
+#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")
+#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run")
+#define OPT__ABBREV(var) \
+ { OPTION_CALLBACK, 0, "abbrev", (var), "n", \
+ "use <n> digits to display SHA-1s", \
+ PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+
+#endif
$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..])
--- /dev/null
+#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);
+}
+/*
+ * Simple text-based progress display module for GIT
+ *
+ * Copyright (c) 2007 by Nicolas Pitre <nico@cam.org>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
#include "git-compat-util.h"
#include "progress.h"
+#define TP_IDX_MAX 8
+
+struct throughput {
+ struct timeval prev_tv;
+ off_t total;
+ unsigned long count;
+ unsigned long avg_bytes;
+ unsigned long last_bytes[TP_IDX_MAX];
+ unsigned int avg_misecs;
+ unsigned int last_misecs[TP_IDX_MAX];
+ unsigned int idx;
+ char display[32];
+};
+
+struct progress {
+ const char *title;
+ int last_value;
+ unsigned total;
+ unsigned last_percent;
+ unsigned delay;
+ unsigned delayed_percent_treshold;
+ struct throughput *throughput;
+};
+
static volatile sig_atomic_t progress_update;
static void progress_interval(int signum)
progress_update = 0;
}
-int display_progress(struct progress *progress, unsigned n)
+static int display(struct progress *progress, unsigned n, int done)
{
+ char *eol, *tp;
+
if (progress->delay) {
- char buf[80];
if (!progress_update || --progress->delay)
return 0;
if (progress->total) {
return 0;
}
}
- if (snprintf(buf, sizeof(buf),
- progress->delayed_title, progress->total))
- fprintf(stderr, "%s\n", buf);
}
+
+ progress->last_value = n;
+ tp = (progress->throughput) ? progress->throughput->display : "";
+ eol = done ? ", done. \n" : " \r";
if (progress->total) {
unsigned percent = n * 100 / progress->total;
if (percent != progress->last_percent || progress_update) {
progress->last_percent = percent;
- fprintf(stderr, "%s%4u%% (%u/%u) done\r",
- progress->prefix, percent, n, progress->total);
+ fprintf(stderr, "%s: %3u%% (%u/%u)%s%s",
+ progress->title, percent, n,
+ progress->total, tp, eol);
progress_update = 0;
- progress->need_lf = 1;
return 1;
}
} else if (progress_update) {
- fprintf(stderr, "%s%u\r", progress->prefix, n);
+ fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol);
progress_update = 0;
- progress->need_lf = 1;
return 1;
}
+
return 0;
}
-void start_progress(struct progress *progress, const char *title,
- const char *prefix, unsigned total)
+void display_throughput(struct progress *progress, unsigned long n)
{
- char buf[80];
- progress->prefix = prefix;
- progress->total = total;
- progress->last_percent = -1;
- progress->delay = 0;
- progress->need_lf = 0;
- if (snprintf(buf, sizeof(buf), title, total))
- fprintf(stderr, "%s\n", buf);
- set_progress_signal();
+ struct throughput *tp;
+ struct timeval tv;
+ unsigned int misecs;
+
+ if (!progress)
+ return;
+ tp = progress->throughput;
+
+ gettimeofday(&tv, NULL);
+
+ if (!tp) {
+ progress->throughput = tp = calloc(1, sizeof(*tp));
+ if (tp)
+ tp->prev_tv = tv;
+ return;
+ }
+
+ tp->total += n;
+ tp->count += n;
+
+ /*
+ * We have x = bytes and y = microsecs. We want z = KiB/s:
+ *
+ * z = (x / 1024) / (y / 1000000)
+ * z = x / y * 1000000 / 1024
+ * z = x / (y * 1024 / 1000000)
+ * z = x / y'
+ *
+ * To simplify things we'll keep track of misecs, or 1024th of a sec
+ * obtained with:
+ *
+ * y' = y * 1024 / 1000000
+ * y' = y / (1000000 / 1024)
+ * y' = y / 977
+ */
+ misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024;
+ misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977;
+
+ if (misecs > 512) {
+ int l = sizeof(tp->display);
+ tp->prev_tv = tv;
+ tp->avg_bytes += tp->count;
+ tp->avg_misecs += misecs;
+
+ if (tp->total > 1 << 30) {
+ l -= snprintf(tp->display, l, ", %u.%2.2u GiB",
+ (int)(tp->total >> 30),
+ (int)(tp->total & ((1 << 30) - 1)) / 10737419);
+ } else if (tp->total > 1 << 20) {
+ l -= snprintf(tp->display, l, ", %u.%2.2u MiB",
+ (int)(tp->total >> 20),
+ ((int)(tp->total & ((1 << 20) - 1))
+ * 100) >> 20);
+ } else if (tp->total > 1 << 10) {
+ l -= snprintf(tp->display, l, ", %u.%2.2u KiB",
+ (int)(tp->total >> 10),
+ ((int)(tp->total & ((1 << 10) - 1))
+ * 100) >> 10);
+ } else {
+ l -= snprintf(tp->display, l, ", %u bytes",
+ (int)tp->total);
+ }
+ snprintf(tp->display + sizeof(tp->display) - l, l,
+ " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs);
+
+ tp->avg_bytes -= tp->last_bytes[tp->idx];
+ tp->avg_misecs -= tp->last_misecs[tp->idx];
+ tp->last_bytes[tp->idx] = tp->count;
+ tp->last_misecs[tp->idx] = misecs;
+ tp->idx = (tp->idx + 1) % TP_IDX_MAX;
+ tp->count = 0;
+
+ if (progress->last_value != -1 && progress_update)
+ display(progress, progress->last_value, 0);
+ }
+}
+
+int display_progress(struct progress *progress, unsigned n)
+{
+ return progress ? display(progress, n, 0) : 0;
}
-void start_progress_delay(struct progress *progress, const char *title,
- const char *prefix, unsigned total,
- unsigned percent_treshold, unsigned delay)
+struct progress *start_progress_delay(const char *title, unsigned total,
+ unsigned percent_treshold, unsigned delay)
{
- progress->prefix = prefix;
+ struct progress *progress = malloc(sizeof(*progress));
+ if (!progress) {
+ /* unlikely, but here's a good fallback */
+ fprintf(stderr, "%s...\n", title);
+ return NULL;
+ }
+ progress->title = title;
progress->total = total;
+ progress->last_value = -1;
progress->last_percent = -1;
progress->delayed_percent_treshold = percent_treshold;
- progress->delayed_title = title;
progress->delay = delay;
- progress->need_lf = 0;
+ progress->throughput = NULL;
set_progress_signal();
+ return progress;
}
-void stop_progress(struct progress *progress)
+struct progress *start_progress(const char *title, unsigned total)
{
+ return start_progress_delay(title, total, 0, 0);
+}
+
+void stop_progress(struct progress **p_progress)
+{
+ struct progress *progress = *p_progress;
+ if (!progress)
+ return;
+ *p_progress = NULL;
+ if (progress->last_value != -1) {
+ /* Force the last update */
+ progress_update = 1;
+ display(progress, progress->last_value, 1);
+ }
clear_progress_signal();
- if (progress->need_lf)
- fputc('\n', stderr);
+ free(progress->throughput);
+ free(progress);
}
#ifndef PROGRESS_H
#define PROGRESS_H
-struct progress {
- const char *prefix;
- unsigned total;
- unsigned last_percent;
- unsigned delay;
- unsigned delayed_percent_treshold;
- const char *delayed_title;
- int need_lf;
-};
+struct progress;
+void display_throughput(struct progress *progress, unsigned long n);
int display_progress(struct progress *progress, unsigned n);
-void start_progress(struct progress *progress, const char *title,
- const char *prefix, unsigned total);
-void start_progress_delay(struct progress *progress, const char *title,
- const char *prefix, unsigned total,
- unsigned percent_treshold, unsigned delay);
-void stop_progress(struct progress *progress);
+struct progress *start_progress(const char *title, unsigned total);
+struct progress *start_progress_delay(const char *title, unsigned total,
+ unsigned percent_treshold, unsigned delay);
+void stop_progress(struct progress **progress);
#endif
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)) {
/* 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;
}
* 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);
return NULL;
}
+void setup_work_tree(void)
+{
+ const char *work_tree, *git_dir;
+ static int initialized = 0;
+
+ if (initialized)
+ return;
+ work_tree = get_git_work_tree();
+ git_dir = get_git_dir();
+ if (!is_absolute_path(git_dir))
+ set_git_dir(make_absolute_path(git_dir));
+ if (!work_tree || chdir(work_tree))
+ die("This operation must be run in a work tree");
+ initialized = 1;
+}
+
/*
* 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.
strcpy(buf, "remote:");
while (1) {
int band, len;
- len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX);
+ len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX);
if (len == 0)
break;
if (len < 1) {
return SIDEBAND_REMOTE_ERROR;
case 2:
buf[7] = ' ';
- safe_write(err, buf, 8+len);
+ len += 8;
+ while (1) {
+ int brk = 8;
+ while (brk < len) {
+ brk++;
+ if (buf[brk-1] == '\n' ||
+ buf[brk-1] == '\r')
+ break;
+ }
+ safe_write(err, buf, brk);
+ if (brk < len) {
+ memmove(buf + 8, buf + brk, len - brk);
+ len = len - brk + 8;
+ } else
+ break;
+ }
continue;
case 1:
safe_write(out, buf+8, len);
int len;
va_list ap;
+ if (!strbuf_avail(sb))
+ strbuf_grow(sb, 64);
va_start(ap, fmt);
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
va_end(ap);
- if (len < 0) {
- len = 0;
- }
+ if (len < 0)
+ die("your vsnprintf is broken");
if (len > strbuf_avail(sb)) {
strbuf_grow(sb, len);
va_start(ap, fmt);
* 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.
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Schindelin
+#
+
+test_description='our own option parser'
+
+. ./test-lib.sh
+
+cat > expect.err << EOF
+usage: test-parse-options <options>
+
+ -b, --boolean get a boolean
+ -i, --integer <n> get a integer
+ -j <n> get a integer, too
+
+string options
+ -s, --string <string>
+ get a string
+ --string2 <str> get another string
+ --st <st> get another string (pervert ordering)
+
+EOF
+
+test_expect_success 'test help' '
+ ! test-parse-options -h > output 2> output.err &&
+ test ! -s output &&
+ git diff expect.err output.err
+'
+
+cat > expect << EOF
+boolean: 2
+integer: 1729
+string: 123
+EOF
+
+test_expect_success 'short options' '
+ test-parse-options -s123 -b -i 1729 -b > output 2> output.err &&
+ git diff expect output &&
+ test ! -s output.err
+'
+cat > expect << EOF
+boolean: 2
+integer: 1729
+string: 321
+EOF
+
+test_expect_success 'long options' '
+ test-parse-options --boolean --integer 1729 --boolean --string2=321 \
+ > output 2> output.err &&
+ test ! -s output.err &&
+ git diff expect output
+'
+
+cat > expect << EOF
+boolean: 1
+integer: 13
+string: 123
+arg 00: a1
+arg 01: b1
+arg 02: --boolean
+EOF
+
+test_expect_success 'intermingled arguments' '
+ test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \
+ > output 2> output.err &&
+ test ! -s output.err &&
+ git diff expect output
+'
+
+cat > expect << EOF
+boolean: 0
+integer: 2
+string: (not set)
+EOF
+
+test_expect_success 'unambiguously abbreviated option' '
+ test-parse-options --int 2 --boolean --no-bo > output 2> output.err &&
+ test ! -s output.err &&
+ git diff expect output
+'
+
+test_expect_success 'unambiguously abbreviated option with "="' '
+ test-parse-options --int=2 > output 2> output.err &&
+ test ! -s output.err &&
+ git diff expect output
+'
+
+test_expect_failure 'ambiguously abbreviated option' '
+ test-parse-options --strin 123;
+ test $? != 129
+'
+
+cat > expect << EOF
+boolean: 0
+integer: 0
+string: 123
+EOF
+
+test_expect_success 'non ambiguous option (after two options it abbreviates)' '
+ test-parse-options --st 123 > output 2> output.err &&
+ test ! -s output.err &&
+ git diff expect output
+'
+
+test_done
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
diff -u expect .git/.dotest-merge/patch &&
diff -u expect2 file1 &&
test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
- test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l)
+ test 0 = $(grep -v "^#" < .git/.dotest-merge/git-rebase-todo | wc -l)
'
test_expect_success 'abort' '
test_expect_success 'cherry-pick after renaming branch' '
git checkout rename2 &&
- EDITOR=: VISUAL=: git cherry-pick added &&
+ git cherry-pick added &&
test -f opos &&
grep "Add extra line at the end" opos
test_expect_success 'revert after renaming branch' '
git checkout rename1 &&
- EDITOR=: VISUAL=: git revert added &&
+ git revert added &&
test -f spoo &&
! grep "Add extra line at the end" spoo
--- /dev/null
+#!/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
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3
'
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3 8859
'
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3
'
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3 8859
'
--- /dev/null
+#!/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="\e$B$O$^$N\e(B \e$B$U$K$*$&\e(B" \
+ git format-patch -s --stdout -1 >output &&
+ grep Content-Type output
+
+'
+
+test_done
+
+
+
+
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
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
--- /dev/null
+#!/bin/sh
+
+test_description='errors in upload-pack'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+corrupt_repo () {
+ object_sha1=$(git rev-parse "$1") &&
+ ob=$(expr "$object_sha1" : "\(..\)") &&
+ ject=$(expr "$object_sha1" : "..\(..*\)") &&
+ rm -f ".git/objects/$ob/$ject"
+}
+
+test_expect_success 'setup and corrupt repository' '
+
+ echo file >file &&
+ git add file &&
+ git rev-parse :file &&
+ git commit -a -m original &&
+ test_tick &&
+ echo changed >file &&
+ git commit -a -m changed &&
+ corrupt_repo HEAD:file
+
+'
+
+test_expect_failure 'fsck fails' '
+
+ git fsck
+'
+
+test_expect_success 'upload-pack fails due to error in pack-objects' '
+
+ ! echo "0032want $(git rev-parse HEAD)
+00000009done
+0000" | git-upload-pack . > /dev/null 2> output.err &&
+ grep "pack-objects died" output.err
+'
+
+test_expect_success 'corrupt repo differently' '
+
+ git hash-object -w file &&
+ corrupt_repo HEAD^^{tree}
+
+'
+
+test_expect_failure 'fsck fails' '
+
+ git fsck
+'
+test_expect_success 'upload-pack fails due to error in rev-list' '
+
+ ! echo "0032want $(git rev-parse HEAD)
+00000009done
+0000" | git-upload-pack . > /dev/null 2> output.err &&
+ grep "waitpid (async) failed" output.err
+'
+
+test_expect_success 'create empty repository' '
+
+ mkdir foo &&
+ cd foo &&
+ git init
+
+'
+
+test_expect_failure 'fetch fails' '
+
+ git fetch .. master
+
+'
+
+test_done
git diff expected actual
'
+cat >expected <<\EOF
+refs/heads/master
+refs/tags/testtag
+EOF
+
+test_expect_success 'Verify ascending sort' '
+ git-for-each-ref --format="%(refname)" --sort=refname >actual &&
+ git diff expected actual
+'
+
+
+cat >expected <<\EOF
+refs/tags/testtag
+refs/heads/master
+EOF
+
+test_expect_success 'Verify descending sort' '
+ git-for-each-ref --format="%(refname)" --sort=-refname >actual &&
+ git diff expected actual
+'
+
+
test_done
check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
'
+test_expect_success 'reset --soft with unmerged index should fail' '
+ touch .git/MERGE_HEAD &&
+ echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" |
+ git update-index --index-info &&
+ ! git reset --soft HEAD &&
+ rm .git/MERGE_HEAD &&
+ git rm --cached -- un
+'
+
test_expect_success \
'giving paths with options different than --mixed should fail' '
! git reset --soft -- first &&
'
+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
+'
+
+cat > expect << EOF
+file2: needs update
+EOF
+
+test_expect_success '--mixed refreshes the index' '
+ echo 123 >> file2 &&
+ git reset --mixed HEAD > output &&
+ git diff --exit-code expect output
+'
+
test_done
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 &&
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
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
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) &&
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) &&
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) &&
. ./test-lib.sh
+git config clean.requireForce no
+
test_expect_success 'setup' '
mkdir -p src &&
'
+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 &&
'
+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 &&
'
+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 &&
'
+test_expect_success 'core.excludesfile' '
+
+ echo excludes >excludes &&
+ echo included >included &&
+ git config core.excludesfile excludes &&
+ output=$(git clean -n excludes included 2>&1) &&
+ expr "$output" : ".*included" >/dev/null &&
+ ! expr "$output" : ".*excludes" >/dev/null
+
+'
+
test_done
'
+author="The Real Author <someguy@his.email.org>"
+test_expect_success 'amend commit to fix author' '
+
+ oldtick=$GIT_AUTHOR_DATE &&
+ test_tick &&
+ git reset --hard &&
+ git cat-file -p HEAD |
+ sed -e "s/author.*/author $author $oldtick/" \
+ -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+ expected &&
+ git commit --amend --author="$author" &&
+ git cat-file -p HEAD > current &&
+ diff expected current
+
+'
+
+test_expect_success 'sign off (1)' '
+
+ echo 1 >positive &&
+ git add positive &&
+ git commit -s -m "thank you" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ (
+ echo thank you
+ echo
+ git var GIT_COMMITTER_IDENT |
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ ) >expected &&
+ diff -u expected actual
+
+'
+
+test_expect_success 'sign off (2)' '
+
+ echo 2 >positive &&
+ git add positive &&
+ existing="Signed-off-by: Watch This <watchthis@example.com>" &&
+ git commit -s -m "thank you
+
+$existing" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ (
+ echo thank you
+ echo
+ echo $existing
+ git var GIT_COMMITTER_IDENT |
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ ) >expected &&
+ diff -u expected actual
+
+'
+
+test_expect_success 'multiple -m' '
+
+ >negative &&
+ git add negative &&
+ git commit -m "one" -m "two" -m "three" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ (
+ echo one
+ echo
+ echo two
+ echo
+ echo three
+ ) >expected &&
+ diff -u expected actual
+
+'
+
test_done
--- /dev/null
+#!/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_expect_failure 'attempt to dcommit with a dirty index' '
+ echo foo >>file &&
+ git add file &&
+ git svn dcommit
+'
+
+test_done
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
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git-svn funky branch names'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svnrepo' "
+ mkdir project project/trunk project/branches project/tags &&
+ echo foo > project/trunk/foo &&
+ svn import -m '$test_description' project \"$svnrepo/pr ject\" &&
+ rm -rf project &&
+ svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \
+ \"$svnrepo/pr ject/branches/fun plugin\" &&
+ svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \
+ \"$svnrepo/pr ject/branches/more fun plugin!\" &&
+ start_httpd
+ "
+
+test_expect_success 'test clone with funky branch names' "
+ git svn clone -s \"$svnrepo/pr ject\" project &&
+ cd project &&
+ git rev-parse 'refs/remotes/fun%20plugin' &&
+ git rev-parse 'refs/remotes/more%20fun%20plugin!' &&
+ cd ..
+ "
+
+test_expect_success 'test dcommit to funky branch' "
+ cd project &&
+ git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
+ echo hello >> foo &&
+ git commit -m 'hello' -- foo &&
+ git svn dcommit &&
+ cd ..
+ "
+
+stop_httpd
+
+test_done
our \$export_ok = "";
our \$strict_export = "";
-CGI::Carp::set_programname("gitweb/gitweb.cgi");
EOF
cat >.git/description <<EOF
'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
tput sgr0 >/dev/null 2>&1 &&
color=t
-test "${test_description}" != "" ||
-error "Test script did not set test_description."
-
while test "$#" -ne 0
do
case "$1" in
-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
immediate=t; shift ;;
-h|--h|--he|--hel|--help)
- echo "$test_description"
- exit 0 ;;
+ help=t; shift ;;
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
verbose=t; shift ;;
-q|--q|--qu|--qui|--quie|--quiet)
say_color info "$*"
}
+test "${test_description}" != "" ||
+error "Test script did not set test_description."
+
+if test "$help" = "t"
+then
+ echo "$test_description"
+ exit 0
+fi
+
exec 5>&1
if test "$verbose" = "t"
then
# 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
# --- 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
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
--- /dev/null
+#include "cache.h"
+#include "parse-options.h"
+
+static int boolean = 0;
+static int integer = 0;
+static char *string = NULL;
+
+int main(int argc, const char **argv)
+{
+ const char *usage[] = {
+ "test-parse-options <options>",
+ NULL
+ };
+ struct option options[] = {
+ OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"),
+ OPT_INTEGER('i', "integer", &integer, "get a integer"),
+ OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
+ OPT_GROUP("string options"),
+ OPT_STRING('s', "string", &string, "string", "get a string"),
+ OPT_STRING(0, "string2", &string, "str", "get another string"),
+ OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
+ OPT_END(),
+ };
+ int i;
+
+ argc = parse_options(argc, argv, options, usage, 0);
+
+ printf("boolean: %d\n", boolean);
+ printf("integer: %d\n", integer);
+ printf("string: %s\n", string ? string : "(not set)");
+
+ for (i = 0; i < argc; i++)
+ printf("arg %02d: %s\n", i, argv[i]);
+
+ return 0;
+}
if (!fd)
return;
- strbuf_init(&buf, 0);
+ strbuf_init(&buf, 64);
va_start(ap, fmt);
len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
va_end(ap);
if (!fd)
return;
- strbuf_init(&buf, 0);
+ strbuf_init(&buf, 64);
va_start(ap, fmt);
len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
va_end(ap);
return;
for (;;) {
- int cmp, len;
+ int cmp = cmp, len;
if (!fgets(buffer, sizeof(buffer), f)) {
fclose(f);
}
#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;
- argv = xmalloc((refspec_nr + 11) * sizeof(char *));
+ argv = xmalloc((refspec_nr + 12) * sizeof(char *));
argv[0] = "http-push";
argc = 1;
if (flags & TRANSPORT_PUSH_ALL)
argv[argc++] = "--force";
if (flags & TRANSPORT_PUSH_DRY_RUN)
argv[argc++] = "--dry-run";
+ if (flags & TRANSPORT_PUSH_VERBOSE)
+ argv[argc++] = "--verbose";
argv[argc++] = transport->url;
while (refspec_nr--)
argv[argc++] = *refspec++;
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;
const char **argv;
char *rem;
int argc;
int err;
- argv = xmalloc((refspec_nr + 11) * sizeof(char *));
+ argv = xmalloc((refspec_nr + 12) * sizeof(char *));
argv[0] = "send-pack";
argc = 1;
if (flags & TRANSPORT_PUSH_ALL)
argv[argc++] = "--force";
if (flags & TRANSPORT_PUSH_DRY_RUN)
argv[argc++] = "--dry-run";
+ if (flags & TRANSPORT_PUSH_VERBOSE)
+ argv[argc++] = "--verbose";
if (data->receivepack) {
char *rp = xmalloc(strlen(data->receivepack) + 16);
sprintf(rp, "--receive-pack=%s", data->receivepack);
#define TRANSPORT_PUSH_ALL 1
#define TRANSPORT_PUSH_FORCE 2
#define TRANSPORT_PUSH_DRY_RUN 4
+#define TRANSPORT_PUSH_VERBOSE 8
/* Returns a transport suitable for the url */
struct transport *transport_get(struct remote *, const char *);
{
unsigned short mask = htons(CE_UPDATE);
unsigned cnt = 0, total = 0;
- struct progress progress;
+ struct progress *progress = NULL;
char last_symlink[PATH_MAX];
if (o->update && o->verbose_update) {
total++;
}
- start_progress_delay(&progress, "Checking %u files out...",
- "", total, 50, 2);
+ progress = start_progress_delay("Checking out files",
+ total, 50, 2);
cnt = 0;
}
while (nr--) {
struct cache_entry *ce = *src++;
- if (total)
- if (!ce->ce_mode || ce->ce_flags & mask)
- display_progress(&progress, ++cnt);
+ if (!ce->ce_mode || ce->ce_flags & mask)
+ display_progress(progress, ++cnt);
if (!ce->ce_mode) {
if (o->update)
unlink_entry(ce->name, last_symlink);
}
}
}
- if (total)
- stop_progress(&progress);;
+ stop_progress(&progress);
}
int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
int buffered = -1;
+ ssize_t sz;
const char *argv[10];
int arg = 0;
pack_objects.git_cmd = 1;
pack_objects.argv = argv;
- if (start_command(&pack_objects)) {
- /* daemon sets things up to ignore TERM */
- kill(rev_list.pid, SIGKILL);
+ if (start_command(&pack_objects))
die("git-upload-pack: unable to fork git-pack-objects");
- }
/* We read from pack_objects.err to capture stderr output for
* progress bar, and pack_objects.out to capture the pack data.
*/
while (1) {
- const char *who;
struct pollfd pfd[2];
- pid_t pid;
- int status;
- ssize_t sz;
int pe, pu, pollsize;
reset_timeout();
pollsize++;
}
- if (pollsize) {
- if (poll(pfd, pollsize, -1) < 0) {
- if (errno != EINTR) {
- error("poll failed, resuming: %s",
- strerror(errno));
- sleep(1);
- }
- continue;
- }
- if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
- /* Data ready; we keep the last byte
- * to ourselves in case we detect
- * broken rev-list, so that we can
- * leave the stream corrupted. This
- * is unfortunate -- unpack-objects
- * would happily accept a valid pack
- * data with trailing garbage, so
- * appending garbage after we pass all
- * the pack data is not good enough to
- * signal breakage to downstream.
- */
- char *cp = data;
- ssize_t outsz = 0;
- if (0 <= buffered) {
- *cp++ = buffered;
- outsz++;
- }
- sz = xread(pack_objects.out, cp,
- sizeof(data) - outsz);
- if (0 < sz)
- ;
- else if (sz == 0) {
- close(pack_objects.out);
- pack_objects.out = -1;
- }
- else
- goto fail;
- sz += outsz;
- if (1 < sz) {
- buffered = data[sz-1] & 0xFF;
- sz--;
- }
- else
- buffered = -1;
- sz = send_client_data(1, data, sz);
- if (sz < 0)
- goto fail;
- }
- if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
- /* Status ready; we ship that in the side-band
- * or dump to the standard error.
- */
- sz = xread(pack_objects.err, progress,
- sizeof(progress));
- if (0 < sz)
- send_client_data(2, progress, sz);
- else if (sz == 0) {
- close(pack_objects.err);
- pack_objects.err = -1;
- }
- else
- goto fail;
+ if (!pollsize)
+ break;
+
+ if (poll(pfd, pollsize, -1) < 0) {
+ if (errno != EINTR) {
+ error("poll failed, resuming: %s",
+ strerror(errno));
+ sleep(1);
}
+ continue;
}
-
- /* See if the children are still there */
- if (rev_list.pid || pack_objects.pid) {
- pid = waitpid(-1, &status, WNOHANG);
- if (!pid)
- continue;
- who = ((pid == rev_list.pid) ? "git-rev-list" :
- (pid == pack_objects.pid) ? "git-pack-objects" :
- NULL);
- if (!who) {
- if (pid < 0) {
- error("git-upload-pack: %s",
- strerror(errno));
- goto fail;
- }
- error("git-upload-pack: we weren't "
- "waiting for %d", pid);
- continue;
+ if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
+ /* Data ready; we keep the last byte to ourselves
+ * in case we detect broken rev-list, so that we
+ * can leave the stream corrupted. This is
+ * unfortunate -- unpack-objects would happily
+ * accept a valid packdata with trailing garbage,
+ * so appending garbage after we pass all the
+ * pack data is not good enough to signal
+ * breakage to downstream.
+ */
+ char *cp = data;
+ ssize_t outsz = 0;
+ if (0 <= buffered) {
+ *cp++ = buffered;
+ outsz++;
+ }
+ sz = xread(pack_objects.out, cp,
+ sizeof(data) - outsz);
+ if (0 < sz)
+ ;
+ else if (sz == 0) {
+ close(pack_objects.out);
+ pack_objects.out = -1;
}
- if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) {
- error("git-upload-pack: %s died with error.",
- who);
+ else
goto fail;
+ sz += outsz;
+ if (1 < sz) {
+ buffered = data[sz-1] & 0xFF;
+ sz--;
}
- if (pid == rev_list.pid)
- rev_list.pid = 0;
- if (pid == pack_objects.pid)
- pack_objects.pid = 0;
- if (rev_list.pid || pack_objects.pid)
- continue;
- }
-
- /* both died happily */
- if (pollsize)
- continue;
-
- /* flush the data */
- if (0 <= buffered) {
- data[0] = buffered;
- sz = send_client_data(1, data, 1);
+ else
+ buffered = -1;
+ sz = send_client_data(1, data, sz);
if (sz < 0)
goto fail;
- fprintf(stderr, "flushed.\n");
}
- if (use_sideband)
- packet_flush(1);
- return;
+ if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
+ /* Status ready; we ship that in the side-band
+ * or dump to the standard error.
+ */
+ sz = xread(pack_objects.err, progress,
+ sizeof(progress));
+ if (0 < sz)
+ send_client_data(2, progress, sz);
+ else if (sz == 0) {
+ close(pack_objects.err);
+ pack_objects.err = -1;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ if (finish_command(&pack_objects)) {
+ error("git-upload-pack: git-pack-objects died with error.");
+ goto fail;
+ }
+ if (finish_async(&rev_list))
+ goto fail; /* error was already reported */
+
+ /* flush the data */
+ if (0 <= buffered) {
+ data[0] = buffered;
+ sz = send_client_data(1, data, 1);
+ if (sz < 0)
+ goto fail;
+ fprintf(stderr, "flushed.\n");
}
+ if (use_sideband)
+ packet_flush(1);
+ return;
+
fail:
- if (pack_objects.pid)
- kill(pack_objects.pid, SIGKILL);
- if (rev_list.pid)
- kill(rev_list.pid, SIGKILL);
send_client_data(3, abort_msg, sizeof(abort_msg));
die("git-upload-pack: %s", abort_msg);
}
static void report(const char *prefix, const char *err, va_list params)
{
- fputs(prefix, stderr);
- vfprintf(stderr, err, params);
- fputs("\n", stderr);
+ char msg[256];
+ vsnprintf(msg, sizeof(msg), err, params);
+ fprintf(stderr, "%s%s\n", prefix, msg);
}
static NORETURN void usage_builtin(const char *err)
};
/* 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;