mirror of
https://github.com/git/git.git
synced 2026-04-01 20:40:08 +02:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -105,6 +105,7 @@ git-reflog
|
||||
git-relink
|
||||
git-remote
|
||||
git-repack
|
||||
git-replace
|
||||
git-repo-config
|
||||
git-request-pull
|
||||
git-rerere
|
||||
|
||||
46
Documentation/RelNotes-1.6.4.1.txt
Normal file
46
Documentation/RelNotes-1.6.4.1.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
GIT v1.6.4.1 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.6.4
|
||||
------------------
|
||||
|
||||
* An unquoted value in the configuration file, when it contains more than
|
||||
one whitespaces in a row, got them replaced with a single space.
|
||||
|
||||
* "git am" used to accept a single piece of e-mail per file (not a mbox)
|
||||
as its input, but multiple input format support in v1.6.4 broke it.
|
||||
Apparently many people have been depending on this feature.
|
||||
|
||||
* The short help text for "git filter-branch" command was a single long
|
||||
line, wrapped by terminals, and was hard to read.
|
||||
|
||||
* The "recursive" strategy of "git merge" segfaulted when a merge has
|
||||
more than one merge-bases, and merging of these merge-bases involves
|
||||
a rename/rename or a rename/add conflict.
|
||||
|
||||
* "git pull --rebase" did not use the right fork point when the
|
||||
repository has already fetched from the upstream that rewinds the
|
||||
branch it is based on in an earlier fetch.
|
||||
|
||||
* Explain the concept of fast-forward more fully in "git push"
|
||||
documentation, and hint to refer to it from an error message when the
|
||||
command refuses an update to protect the user.
|
||||
|
||||
* The default value for pack.deltacachesize, used by "git repack", is now
|
||||
256M, instead of unbounded. Otherwise a repack of a moderately sized
|
||||
repository would needlessly eat into swap.
|
||||
|
||||
* Document how "git repack" (hence "git gc") interacts with a repository
|
||||
that borrows its objects from other repositories (e.g. ones created by
|
||||
"git clone -s").
|
||||
|
||||
* "git show" on an annotated tag lacked a delimiting blank line between
|
||||
the tag itself and the contents of the object it tags.
|
||||
|
||||
* "git verify-pack -v" erroneously reported number of objects with too
|
||||
deep delta depths as "chain length 0" objects.
|
||||
|
||||
* Long names of authors and committers outside US-ASCII were sometimes
|
||||
incorrectly shown in "gitweb".
|
||||
|
||||
Other minor documentation updates are included.
|
||||
@@ -461,6 +461,14 @@ it will be treated as a shell command. For example, defining
|
||||
executed from the top-level directory of a repository, which may
|
||||
not necessarily be the current directory.
|
||||
|
||||
apply.ignorewhitespace::
|
||||
When set to 'change', tells 'git-apply' to ignore changes in
|
||||
whitespace, in the same way as the '--ignore-space-change'
|
||||
option.
|
||||
When set to one of: no, none, never, false tells 'git-apply' to
|
||||
respect all whitespace differences.
|
||||
See linkgit:git-apply[1].
|
||||
|
||||
apply.whitespace::
|
||||
Tells 'git-apply' how to handle whitespaces, in the same way
|
||||
as the '--whitespace' option. See linkgit:git-apply[1].
|
||||
|
||||
@@ -11,7 +11,7 @@ SYNOPSIS
|
||||
[verse]
|
||||
'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
|
||||
[--3way] [--interactive] [--committer-date-is-author-date]
|
||||
[--ignore-date]
|
||||
[--ignore-date] [--ignore-space-change | --ignore-whitespace]
|
||||
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
|
||||
[--reject] [-q | --quiet]
|
||||
[<mbox> | <Maildir>...]
|
||||
@@ -65,6 +65,9 @@ default. You can use `--no-utf8` to override this.
|
||||
it is supposed to apply to and we have those blobs
|
||||
available locally.
|
||||
|
||||
--ignore-date::
|
||||
--ignore-space-change::
|
||||
--ignore-whitespace::
|
||||
--whitespace=<option>::
|
||||
-C<n>::
|
||||
-p<n>::
|
||||
|
||||
@@ -13,6 +13,7 @@ SYNOPSIS
|
||||
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
|
||||
[--allow-binary-replacement | --binary] [--reject] [-z]
|
||||
[-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
|
||||
[--ignore-space-change | --ignore-whitespace ]
|
||||
[--whitespace=<nowarn|warn|fix|error|error-all>]
|
||||
[--exclude=PATH] [--include=PATH] [--directory=<root>]
|
||||
[--verbose] [<patch>...]
|
||||
@@ -149,6 +150,14 @@ patch to each path is used. A patch to a path that does not match any
|
||||
include/exclude pattern is used by default if there is no include pattern
|
||||
on the command line, and ignored if there is any include pattern.
|
||||
|
||||
--ignore-space-change::
|
||||
--ignore-whitespace::
|
||||
When applying a patch, ignore changes in whitespace in context
|
||||
lines if necessary.
|
||||
Context lines will preserve their whitespace, and they will not
|
||||
undergo whitespace fixing regardless of the value of the
|
||||
`--whitespace` option. New lines will still be fixed, though.
|
||||
|
||||
--whitespace=<action>::
|
||||
When applying a patch, detect a new or modified line that has
|
||||
whitespace errors. What are considered whitespace errors is
|
||||
@@ -205,6 +214,10 @@ running `git apply --directory=modules/git-gui`.
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
apply.ignorewhitespace::
|
||||
Set to 'change' if you want changes in whitespace to be ignored by default.
|
||||
Set to one of: no, none, never, false if you want changes in
|
||||
whitespace to be significant.
|
||||
apply.whitespace::
|
||||
When no `--whitespace` flag is given from the command
|
||||
line, this configuration item is used as the default.
|
||||
|
||||
@@ -72,8 +72,16 @@ These objects may be removed by normal git operations (such as 'git-commit')
|
||||
which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
|
||||
If these objects are removed and were referenced by the cloned repository,
|
||||
then the cloned repository will become corrupt.
|
||||
|
||||
|
||||
+
|
||||
Note that running `git repack` without the `-l` option in a repository
|
||||
cloned with `-s` will copy objects from the source repository into a pack
|
||||
in the cloned repository, removing the disk space savings of `clone -s`.
|
||||
It is safe, however, to run `git gc`, which uses the `-l` option by
|
||||
default.
|
||||
+
|
||||
If you want to break the dependency of a repository cloned with `-s` on
|
||||
its source repository, you can simply run `git repack -a` to copy all
|
||||
objects from the source repository into a pack in the cloned repository.
|
||||
|
||||
--reference <repository>::
|
||||
If the reference repository is on the local machine
|
||||
|
||||
@@ -305,6 +305,16 @@ range in addition to the new branch name. The new branch name will
|
||||
point to the top-most revision that a 'git-rev-list' of this range
|
||||
will print.
|
||||
|
||||
If you need to add 'Acked-by' lines to, say, the last 10 commits (none
|
||||
of which is a merge), use this command:
|
||||
|
||||
--------------------------------------------------------
|
||||
git filter-branch --msg-filter '
|
||||
cat &&
|
||||
echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>"
|
||||
' HEAD~10..HEAD
|
||||
--------------------------------------------------------
|
||||
|
||||
*NOTE* the changes introduced by the commits, and which are not reverted
|
||||
by subsequent commits, will still be in the rewritten branch. If you want
|
||||
to throw out _changes_ together with the commits, you should use the
|
||||
|
||||
@@ -268,8 +268,9 @@ OPTIONS
|
||||
exit with the message "Current branch is up to date" in such a
|
||||
situation.
|
||||
|
||||
--ignore-whitespace::
|
||||
--whitespace=<option>::
|
||||
This flag is passed to the 'git-apply' program
|
||||
These flag are passed to the 'git-apply' program
|
||||
(see linkgit:git-apply[1]) that applies the patch.
|
||||
Incompatible with the --interactive option.
|
||||
|
||||
|
||||
71
Documentation/git-replace.txt
Normal file
71
Documentation/git-replace.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
git-replace(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
git-replace - Create, list, delete refs to replace objects
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git replace' [-f] <object> <replacement>
|
||||
'git replace' -d <object>...
|
||||
'git replace' -l [<pattern>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Adds a 'replace' reference in `.git/refs/replace/`
|
||||
|
||||
The name of the 'replace' reference is the SHA1 of the object that is
|
||||
replaced. The content of the replace reference is the SHA1 of the
|
||||
replacement object.
|
||||
|
||||
Unless `-f` is given, the replace reference must not yet exist in
|
||||
`.git/refs/replace/` directory.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-f::
|
||||
If an existing replace ref for the same object exists, it will
|
||||
be overwritten (instead of failing).
|
||||
|
||||
-d::
|
||||
Delete existing replace refs for the given objects.
|
||||
|
||||
-l <pattern>::
|
||||
List replace refs for objects that match the given pattern (or
|
||||
all if no pattern is given).
|
||||
Typing "git replace" without arguments, also lists all replace
|
||||
refs.
|
||||
|
||||
BUGS
|
||||
----
|
||||
Comparing blobs or trees that have been replaced with those that
|
||||
replace them will not work properly. And using 'git reset --hard' to
|
||||
go back to a replaced commit will move the branch to the replacement
|
||||
commit instead of the replaced commit.
|
||||
|
||||
There may be other problems when using 'git rev-list' related to
|
||||
pending objects. And of course things may break if an object of one
|
||||
type is replaced by an object of another type (for example a blob
|
||||
replaced by a commit).
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkgit:git-tag[1]
|
||||
linkgit:git-branch[1]
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Christian Couder <chriscool@tuxfamily.org> and Junio C
|
||||
Hamano <gitster@pobox.com>, based on 'git tag' by Kristian Hogsberg
|
||||
<krh@redhat.com> and Carlos Rica <jasampler@gmail.com>.
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Christian Couder <chriscool@tuxfamily.org> and the
|
||||
git-list <git@vger.kernel.org>, based on 'git tag' documentation.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
||||
@@ -102,9 +102,6 @@ COMMANDS
|
||||
Store Git commit times in the local timezone instead of UTC. This
|
||||
makes 'git log' (even without --date=local) show the same times
|
||||
that `svn log` would in the local timezone.
|
||||
|
||||
--parent;;
|
||||
Fetch only from the SVN parent of the current HEAD.
|
||||
+
|
||||
This doesn't interfere with interoperating with the Subversion
|
||||
repository you cloned from, but if you wish for your local Git
|
||||
@@ -112,6 +109,9 @@ repository to be able to interoperate with someone else's local Git
|
||||
repository, either don't use this option or you should both use it in
|
||||
the same local timezone.
|
||||
|
||||
--parent;;
|
||||
Fetch only from the SVN parent of the current HEAD.
|
||||
|
||||
--ignore-paths=<regex>;;
|
||||
This allows one to specify a Perl regular expression that will
|
||||
cause skipping of all matching paths from checkout from SVN.
|
||||
|
||||
@@ -25,7 +25,13 @@ OPTIONS
|
||||
-v::
|
||||
--verbose::
|
||||
After verifying the pack, show list of objects contained
|
||||
in the pack.
|
||||
in the pack and a histogram of delta chain length.
|
||||
|
||||
-s::
|
||||
--stat-only::
|
||||
Do not verify the pack contents; only show the histogram of delta
|
||||
chain length. With `--verbose`, list of objects is also shown.
|
||||
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
|
||||
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.6.4/git.html[documentation for release 1.6.4]
|
||||
* link:v1.6.4.1/git.html[documentation for release 1.6.4.1]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.6.4.1.txt[1.6.4.1],
|
||||
link:RelNotes-1.6.4.txt[1.6.4].
|
||||
|
||||
* link:v1.6.3.4/git.html[documentation for release 1.6.3.4]
|
||||
|
||||
2
Makefile
2
Makefile
@@ -536,6 +536,7 @@ LIB_OBJS += read-cache.o
|
||||
LIB_OBJS += reflog-walk.o
|
||||
LIB_OBJS += refs.o
|
||||
LIB_OBJS += remote.o
|
||||
LIB_OBJS += replace_object.o
|
||||
LIB_OBJS += rerere.o
|
||||
LIB_OBJS += revision.o
|
||||
LIB_OBJS += run-command.o
|
||||
@@ -625,6 +626,7 @@ BUILTIN_OBJS += builtin-read-tree.o
|
||||
BUILTIN_OBJS += builtin-receive-pack.o
|
||||
BUILTIN_OBJS += builtin-reflog.o
|
||||
BUILTIN_OBJS += builtin-remote.o
|
||||
BUILTIN_OBJS += builtin-replace.o
|
||||
BUILTIN_OBJS += builtin-rerere.o
|
||||
BUILTIN_OBJS += builtin-reset.o
|
||||
BUILTIN_OBJS += builtin-rev-list.o
|
||||
|
||||
173
builtin-apply.c
173
builtin-apply.c
@@ -61,6 +61,13 @@ static enum ws_error_action {
|
||||
static int whitespace_error;
|
||||
static int squelch_whitespace_errors = 5;
|
||||
static int applied_after_fixing_ws;
|
||||
|
||||
static enum ws_ignore {
|
||||
ignore_ws_none,
|
||||
ignore_ws_change,
|
||||
} ws_ignore_action = ignore_ws_none;
|
||||
|
||||
|
||||
static const char *patch_input_file;
|
||||
static const char *root;
|
||||
static int root_len;
|
||||
@@ -97,6 +104,21 @@ static void parse_whitespace_option(const char *option)
|
||||
die("unrecognized whitespace option '%s'", option);
|
||||
}
|
||||
|
||||
static void parse_ignorewhitespace_option(const char *option)
|
||||
{
|
||||
if (!option || !strcmp(option, "no") ||
|
||||
!strcmp(option, "false") || !strcmp(option, "never") ||
|
||||
!strcmp(option, "none")) {
|
||||
ws_ignore_action = ignore_ws_none;
|
||||
return;
|
||||
}
|
||||
if (!strcmp(option, "change")) {
|
||||
ws_ignore_action = ignore_ws_change;
|
||||
return;
|
||||
}
|
||||
die("unrecognized whitespace ignore option '%s'", option);
|
||||
}
|
||||
|
||||
static void set_default_whitespace_mode(const char *whitespace_option)
|
||||
{
|
||||
if (!whitespace_option && !apply_default_whitespace)
|
||||
@@ -214,6 +236,62 @@ static uint32_t hash_line(const char *cp, size_t len)
|
||||
return h;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare lines s1 of length n1 and s2 of length n2, ignoring
|
||||
* whitespace difference. Returns 1 if they match, 0 otherwise
|
||||
*/
|
||||
static int fuzzy_matchlines(const char *s1, size_t n1,
|
||||
const char *s2, size_t n2)
|
||||
{
|
||||
const char *last1 = s1 + n1 - 1;
|
||||
const char *last2 = s2 + n2 - 1;
|
||||
int result = 0;
|
||||
|
||||
if (n1 < 0 || n2 < 0)
|
||||
return 0;
|
||||
|
||||
/* ignore line endings */
|
||||
while ((*last1 == '\r') || (*last1 == '\n'))
|
||||
last1--;
|
||||
while ((*last2 == '\r') || (*last2 == '\n'))
|
||||
last2--;
|
||||
|
||||
/* skip leading whitespace */
|
||||
while (isspace(*s1) && (s1 <= last1))
|
||||
s1++;
|
||||
while (isspace(*s2) && (s2 <= last2))
|
||||
s2++;
|
||||
/* early return if both lines are empty */
|
||||
if ((s1 > last1) && (s2 > last2))
|
||||
return 1;
|
||||
while (!result) {
|
||||
result = *s1++ - *s2++;
|
||||
/*
|
||||
* Skip whitespace inside. We check for whitespace on
|
||||
* both buffers because we don't want "a b" to match
|
||||
* "ab"
|
||||
*/
|
||||
if (isspace(*s1) && isspace(*s2)) {
|
||||
while (isspace(*s1) && s1 <= last1)
|
||||
s1++;
|
||||
while (isspace(*s2) && s2 <= last2)
|
||||
s2++;
|
||||
}
|
||||
/*
|
||||
* If we reached the end on one side only,
|
||||
* lines don't match
|
||||
*/
|
||||
if (
|
||||
((s2 > last2) && (s1 <= last1)) ||
|
||||
((s1 > last1) && (s2 <= last2)))
|
||||
return 0;
|
||||
if ((s1 > last1) && (s2 > last2))
|
||||
break;
|
||||
}
|
||||
|
||||
return !result;
|
||||
}
|
||||
|
||||
static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
|
||||
{
|
||||
ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
|
||||
@@ -1672,10 +1750,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the preimage, and the common lines in postimage,
|
||||
* from buffer buf of length len. If postlen is 0 the postimage
|
||||
* is updated in place, otherwise it's updated on a new buffer
|
||||
* of length postlen
|
||||
*/
|
||||
|
||||
static void update_pre_post_images(struct image *preimage,
|
||||
struct image *postimage,
|
||||
char *buf,
|
||||
size_t len)
|
||||
size_t len, size_t postlen)
|
||||
{
|
||||
int i, ctx;
|
||||
char *new, *old, *fixed;
|
||||
@@ -1694,11 +1779,19 @@ static void update_pre_post_images(struct image *preimage,
|
||||
*preimage = fixed_preimage;
|
||||
|
||||
/*
|
||||
* Adjust the common context lines in postimage, in place.
|
||||
* This is possible because whitespace fixing does not make
|
||||
* the string grow.
|
||||
* Adjust the common context lines in postimage. This can be
|
||||
* done in-place when we are just doing whitespace fixing,
|
||||
* which does not make the string grow, but needs a new buffer
|
||||
* when ignoring whitespace causes the update, since in this case
|
||||
* we could have e.g. tabs converted to multiple spaces.
|
||||
* We trust the caller to tell us if the update can be done
|
||||
* in place (postlen==0) or not.
|
||||
*/
|
||||
new = old = postimage->buf;
|
||||
old = postimage->buf;
|
||||
if (postlen)
|
||||
new = postimage->buf = xmalloc(postlen);
|
||||
else
|
||||
new = old;
|
||||
fixed = preimage->buf;
|
||||
for (i = ctx = 0; i < postimage->nr; i++) {
|
||||
size_t len = postimage->line[i].len;
|
||||
@@ -1773,12 +1866,58 @@ static int match_fragment(struct image *img,
|
||||
!memcmp(img->buf + try, preimage->buf, preimage->len))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* No exact match. If we are ignoring whitespace, run a line-by-line
|
||||
* fuzzy matching. We collect all the line length information because
|
||||
* we need it to adjust whitespace if we match.
|
||||
*/
|
||||
if (ws_ignore_action == ignore_ws_change) {
|
||||
size_t imgoff = 0;
|
||||
size_t preoff = 0;
|
||||
size_t postlen = postimage->len;
|
||||
size_t imglen[preimage->nr];
|
||||
for (i = 0; i < preimage->nr; i++) {
|
||||
size_t prelen = preimage->line[i].len;
|
||||
|
||||
imglen[i] = img->line[try_lno+i].len;
|
||||
if (!fuzzy_matchlines(
|
||||
img->buf + try + imgoff, imglen[i],
|
||||
preimage->buf + preoff, prelen))
|
||||
return 0;
|
||||
if (preimage->line[i].flag & LINE_COMMON)
|
||||
postlen += imglen[i] - prelen;
|
||||
imgoff += imglen[i];
|
||||
preoff += prelen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, the preimage matches with whitespace fuzz. Update it and
|
||||
* the common postimage lines to use the same whitespace as the
|
||||
* target. imgoff now holds the true length of the target that
|
||||
* matches the preimage, and we need to update the line lengths
|
||||
* of the preimage to match the target ones.
|
||||
*/
|
||||
fixed_buf = xmalloc(imgoff);
|
||||
memcpy(fixed_buf, img->buf + try, imgoff);
|
||||
for (i = 0; i < preimage->nr; i++)
|
||||
preimage->line[i].len = imglen[i];
|
||||
|
||||
/*
|
||||
* Update the preimage buffer and the postimage context lines.
|
||||
*/
|
||||
update_pre_post_images(preimage, postimage,
|
||||
fixed_buf, imgoff, postlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ws_error_action != correct_ws_error)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The hunk does not apply byte-by-byte, but the hash says
|
||||
* it might with whitespace fuzz.
|
||||
* it might with whitespace fuzz. We haven't been asked to
|
||||
* ignore whitespace, we were asked to correct whitespace
|
||||
* errors, so let's try matching after whitespace correction.
|
||||
*/
|
||||
fixed_buf = xmalloc(preimage->len + 1);
|
||||
buf = fixed_buf;
|
||||
@@ -1830,7 +1969,7 @@ static int match_fragment(struct image *img,
|
||||
* hunk match. Update the context lines in the postimage.
|
||||
*/
|
||||
update_pre_post_images(preimage, postimage,
|
||||
fixed_buf, buf - fixed_buf);
|
||||
fixed_buf, buf - fixed_buf, 0);
|
||||
return 1;
|
||||
|
||||
unmatch_exit:
|
||||
@@ -3272,6 +3411,8 @@ static int git_apply_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "apply.whitespace"))
|
||||
return git_config_string(&apply_default_whitespace, var, value);
|
||||
else if (!strcmp(var, "apply.ignorewhitespace"))
|
||||
return git_config_string(&apply_default_ignorewhitespace, var, value);
|
||||
return git_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
@@ -3308,6 +3449,16 @@ static int option_parse_z(const struct option *opt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int option_parse_space_change(const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
if (unset)
|
||||
ws_ignore_action = ignore_ws_none;
|
||||
else
|
||||
ws_ignore_action = ignore_ws_change;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int option_parse_whitespace(const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
@@ -3384,6 +3535,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
|
||||
"detect new or modified lines that have whitespace errors",
|
||||
0, option_parse_whitespace },
|
||||
{ OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
|
||||
"ignore changes in whitespace when finding context",
|
||||
PARSE_OPT_NOARG, option_parse_space_change },
|
||||
{ OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
|
||||
"ignore changes in whitespace when finding context",
|
||||
PARSE_OPT_NOARG, option_parse_space_change },
|
||||
OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
|
||||
"apply the patch in reverse"),
|
||||
OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
|
||||
@@ -3408,6 +3565,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
git_config(git_apply_config, NULL);
|
||||
if (apply_default_whitespace)
|
||||
parse_whitespace_option(apply_default_whitespace);
|
||||
if (apply_default_ignorewhitespace)
|
||||
parse_ignorewhitespace_option(apply_default_ignorewhitespace);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, builtin_apply_options,
|
||||
apply_usage, 0);
|
||||
|
||||
@@ -589,6 +589,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
struct alternate_object_database *alt;
|
||||
|
||||
errors_found = 0;
|
||||
read_replace_refs = 0;
|
||||
|
||||
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
|
||||
if (write_lost_and_found) {
|
||||
|
||||
@@ -765,7 +765,6 @@ static void handle_filter(struct strbuf *line)
|
||||
|
||||
static void handle_body(void)
|
||||
{
|
||||
int len = 0;
|
||||
struct strbuf prev = STRBUF_INIT;
|
||||
|
||||
/* Skip up to the first boundary */
|
||||
@@ -775,8 +774,6 @@ static void handle_body(void)
|
||||
}
|
||||
|
||||
do {
|
||||
strbuf_setlen(&line, line.len + len);
|
||||
|
||||
/* process any boundary lines */
|
||||
if (*content_top && is_multipart_boundary(&line)) {
|
||||
/* flush any leftover */
|
||||
@@ -832,10 +829,7 @@ static void handle_body(void)
|
||||
handle_filter(&line);
|
||||
}
|
||||
|
||||
strbuf_reset(&line);
|
||||
if (strbuf_avail(&line) < 100)
|
||||
strbuf_grow(&line, 100);
|
||||
} while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin)));
|
||||
} while (!strbuf_getwholeline(&line, fin, '\n'));
|
||||
|
||||
handle_body_out:
|
||||
strbuf_release(&prev);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "string-list.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
static const char git_mailsplit_usage[] =
|
||||
"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
|
||||
@@ -42,26 +43,8 @@ static int is_from_line(const char *line, int len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Could be as small as 64, enough to hold a Unix "From " line. */
|
||||
static char buf[4096];
|
||||
|
||||
/* We cannot use fgets() because our lines can contain NULs */
|
||||
int read_line_with_nul(char *buf, int size, FILE *in)
|
||||
{
|
||||
int len = 0, c;
|
||||
|
||||
for (;;) {
|
||||
c = getc(in);
|
||||
if (c == EOF)
|
||||
break;
|
||||
buf[len++] = c;
|
||||
if (c == '\n' || len + 1 >= size)
|
||||
break;
|
||||
}
|
||||
buf[len] = '\0';
|
||||
|
||||
return len;
|
||||
}
|
||||
static struct strbuf buf = STRBUF_INIT;
|
||||
static int keep_cr;
|
||||
|
||||
/* Called with the first line (potentially partial)
|
||||
* already in buf[] -- normally that should begin with
|
||||
@@ -71,10 +54,9 @@ int read_line_with_nul(char *buf, int size, FILE *in)
|
||||
static int split_one(FILE *mbox, const char *name, int allow_bare)
|
||||
{
|
||||
FILE *output = NULL;
|
||||
int len = strlen(buf);
|
||||
int fd;
|
||||
int status = 0;
|
||||
int is_bare = !is_from_line(buf, len);
|
||||
int is_bare = !is_from_line(buf.buf, buf.len);
|
||||
|
||||
if (is_bare && !allow_bare)
|
||||
goto corrupt;
|
||||
@@ -88,20 +70,23 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
|
||||
* "From " and having something that looks like a date format.
|
||||
*/
|
||||
for (;;) {
|
||||
int is_partial = len && buf[len-1] != '\n';
|
||||
if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
|
||||
buf.buf[buf.len-2] == '\r') {
|
||||
strbuf_setlen(&buf, buf.len-2);
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
if (fwrite(buf, 1, len, output) != len)
|
||||
if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
|
||||
die_errno("cannot write output");
|
||||
|
||||
len = read_line_with_nul(buf, sizeof(buf), mbox);
|
||||
if (len == 0) {
|
||||
if (strbuf_getwholeline(&buf, mbox, '\n')) {
|
||||
if (feof(mbox)) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
die_errno("cannot read mbox");
|
||||
}
|
||||
if (!is_partial && !is_bare && is_from_line(buf, len))
|
||||
if (!is_bare && is_from_line(buf.buf, buf.len))
|
||||
break; /* done with one message */
|
||||
}
|
||||
fclose(output);
|
||||
@@ -166,7 +151,7 @@ static int split_maildir(const char *maildir, const char *dir,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fgets(buf, sizeof(buf), f) == NULL) {
|
||||
if (strbuf_getwholeline(&buf, f, '\n')) {
|
||||
error("cannot read mail %s (%s)", file, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
@@ -203,7 +188,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
|
||||
} while (isspace(peek));
|
||||
ungetc(peek, f);
|
||||
|
||||
if (fgets(buf, sizeof(buf), f) == NULL) {
|
||||
if (strbuf_getwholeline(&buf, f, '\n')) {
|
||||
/* empty stdin is OK */
|
||||
if (f != stdin) {
|
||||
error("cannot read mbox %s", file);
|
||||
@@ -248,6 +233,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
|
||||
nr = strtol(arg+2, NULL, 10);
|
||||
} else if ( arg[1] == 'b' && !arg[2] ) {
|
||||
allow_bare = 1;
|
||||
} else if (!strcmp(arg, "--keep-cr")) {
|
||||
keep_cr = 1;
|
||||
} else if ( arg[1] == 'o' && arg[2] ) {
|
||||
dir = arg+2;
|
||||
} else if ( arg[1] == '-' && !arg[2] ) {
|
||||
|
||||
@@ -2098,6 +2098,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
int rp_ac_alloc = 64;
|
||||
int rp_ac;
|
||||
|
||||
read_replace_refs = 0;
|
||||
|
||||
rp_av = xcalloc(rp_ac_alloc, sizeof(*rp_av));
|
||||
|
||||
rp_av[0] = "pack-objects";
|
||||
|
||||
@@ -140,6 +140,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
|
||||
char *s;
|
||||
|
||||
save_commit_buffer = 0;
|
||||
read_replace_refs = 0;
|
||||
init_revisions(&revs, prefix);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
|
||||
|
||||
159
builtin-replace.c
Normal file
159
builtin-replace.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Builtin "git replace"
|
||||
*
|
||||
* Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
|
||||
*
|
||||
* Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
|
||||
* and Carlos Rica <jasampler@gmail.com> that was itself based on
|
||||
* git-tag.sh and mktag.c by Linus Torvalds.
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "refs.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char * const git_replace_usage[] = {
|
||||
"git replace [-f] <object> <replacement>",
|
||||
"git replace -d <object>...",
|
||||
"git replace -l [<pattern>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int show_reference(const char *refname, const unsigned char *sha1,
|
||||
int flag, void *cb_data)
|
||||
{
|
||||
const char *pattern = cb_data;
|
||||
|
||||
if (!fnmatch(pattern, refname, 0))
|
||||
printf("%s\n", refname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int list_replace_refs(const char *pattern)
|
||||
{
|
||||
if (pattern == NULL)
|
||||
pattern = "*";
|
||||
|
||||
for_each_replace_ref(show_reference, (void *) pattern);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*each_replace_name_fn)(const char *name, const char *ref,
|
||||
const unsigned char *sha1);
|
||||
|
||||
static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
|
||||
{
|
||||
const char **p;
|
||||
char ref[PATH_MAX];
|
||||
int had_error = 0;
|
||||
unsigned char sha1[20];
|
||||
|
||||
for (p = argv; *p; p++) {
|
||||
if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p)
|
||||
>= sizeof(ref)) {
|
||||
error("replace ref name too long: %.*s...", 50, *p);
|
||||
had_error = 1;
|
||||
continue;
|
||||
}
|
||||
if (!resolve_ref(ref, sha1, 1, NULL)) {
|
||||
error("replace ref '%s' not found.", *p);
|
||||
had_error = 1;
|
||||
continue;
|
||||
}
|
||||
if (fn(*p, ref, sha1))
|
||||
had_error = 1;
|
||||
}
|
||||
return had_error;
|
||||
}
|
||||
|
||||
static int delete_replace_ref(const char *name, const char *ref,
|
||||
const unsigned char *sha1)
|
||||
{
|
||||
if (delete_ref(ref, sha1, 0))
|
||||
return 1;
|
||||
printf("Deleted replace ref '%s'\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replace_object(const char *object_ref, const char *replace_ref,
|
||||
int force)
|
||||
{
|
||||
unsigned char object[20], prev[20], repl[20];
|
||||
char ref[PATH_MAX];
|
||||
struct ref_lock *lock;
|
||||
|
||||
if (get_sha1(object_ref, object))
|
||||
die("Failed to resolve '%s' as a valid ref.", object_ref);
|
||||
if (get_sha1(replace_ref, repl))
|
||||
die("Failed to resolve '%s' as a valid ref.", replace_ref);
|
||||
|
||||
if (snprintf(ref, sizeof(ref),
|
||||
"refs/replace/%s",
|
||||
sha1_to_hex(object)) > sizeof(ref) - 1)
|
||||
die("replace ref name too long: %.*s...", 50, ref);
|
||||
if (check_ref_format(ref))
|
||||
die("'%s' is not a valid ref name.", ref);
|
||||
|
||||
if (!resolve_ref(ref, prev, 1, NULL))
|
||||
hashclr(prev);
|
||||
else if (!force)
|
||||
die("replace ref '%s' already exists", ref);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, prev, 0);
|
||||
if (!lock)
|
||||
die("%s: cannot lock the ref", ref);
|
||||
if (write_ref_sha1(lock, repl, NULL) < 0)
|
||||
die("%s: cannot update the ref", ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_replace(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int list = 0, delete = 0, force = 0;
|
||||
struct option options[] = {
|
||||
OPT_BOOLEAN('l', NULL, &list, "list replace refs"),
|
||||
OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"),
|
||||
OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
|
||||
|
||||
if (list && delete)
|
||||
usage_msg_opt("-l and -d cannot be used together",
|
||||
git_replace_usage, options);
|
||||
|
||||
if (force && (list || delete))
|
||||
usage_msg_opt("-f cannot be used with -d or -l",
|
||||
git_replace_usage, options);
|
||||
|
||||
/* Delete refs */
|
||||
if (delete) {
|
||||
if (argc < 1)
|
||||
usage_msg_opt("-d needs at least one argument",
|
||||
git_replace_usage, options);
|
||||
return for_each_replace_name(argv, delete_replace_ref);
|
||||
}
|
||||
|
||||
/* Replace object */
|
||||
if (!list && argc) {
|
||||
if (argc != 2)
|
||||
usage_msg_opt("bad number of arguments",
|
||||
git_replace_usage, options);
|
||||
return replace_object(argv[0], argv[1], force);
|
||||
}
|
||||
|
||||
/* List refs, even if "list" is not set */
|
||||
if (argc > 1)
|
||||
usage_msg_opt("only one pattern can be given with -l",
|
||||
git_replace_usage, options);
|
||||
if (force)
|
||||
usage_msg_opt("-f needs some arguments",
|
||||
git_replace_usage, options);
|
||||
|
||||
return list_replace_refs(argv[0]);
|
||||
}
|
||||
@@ -495,6 +495,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
|
||||
int i;
|
||||
unsigned char sha1[20];
|
||||
|
||||
read_replace_refs = 0;
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
quiet = !isatty(2);
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
|
||||
#define MAX_CHAIN 50
|
||||
|
||||
static void show_pack_info(struct packed_git *p)
|
||||
#define VERIFY_PACK_VERBOSE 01
|
||||
#define VERIFY_PACK_STAT_ONLY 02
|
||||
|
||||
static void show_pack_info(struct packed_git *p, unsigned int flags)
|
||||
{
|
||||
uint32_t nr_objects, i;
|
||||
int cnt;
|
||||
int stat_only = flags & VERIFY_PACK_STAT_ONLY;
|
||||
unsigned long chain_histogram[MAX_CHAIN+1], baseobjects;
|
||||
|
||||
nr_objects = p->num_objects;
|
||||
@@ -32,16 +36,19 @@ static void show_pack_info(struct packed_git *p)
|
||||
type = packed_object_info_detail(p, offset, &size, &store_size,
|
||||
&delta_chain_length,
|
||||
base_sha1);
|
||||
printf("%s ", sha1_to_hex(sha1));
|
||||
if (!stat_only)
|
||||
printf("%s ", sha1_to_hex(sha1));
|
||||
if (!delta_chain_length) {
|
||||
printf("%-6s %lu %lu %"PRIuMAX"\n",
|
||||
type, size, store_size, (uintmax_t)offset);
|
||||
if (!stat_only)
|
||||
printf("%-6s %lu %lu %"PRIuMAX"\n",
|
||||
type, size, store_size, (uintmax_t)offset);
|
||||
baseobjects++;
|
||||
}
|
||||
else {
|
||||
printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
|
||||
type, size, store_size, (uintmax_t)offset,
|
||||
delta_chain_length, sha1_to_hex(base_sha1));
|
||||
if (!stat_only)
|
||||
printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
|
||||
type, size, store_size, (uintmax_t)offset,
|
||||
delta_chain_length, sha1_to_hex(base_sha1));
|
||||
if (delta_chain_length <= MAX_CHAIN)
|
||||
chain_histogram[delta_chain_length]++;
|
||||
else
|
||||
@@ -66,10 +73,12 @@ static void show_pack_info(struct packed_git *p)
|
||||
chain_histogram[0] > 1 ? "s" : "");
|
||||
}
|
||||
|
||||
static int verify_one_pack(const char *path, int verbose)
|
||||
static int verify_one_pack(const char *path, unsigned int flags)
|
||||
{
|
||||
char arg[PATH_MAX];
|
||||
int len;
|
||||
int verbose = flags & VERIFY_PACK_VERBOSE;
|
||||
int stat_only = flags & VERIFY_PACK_STAT_ONLY;
|
||||
struct packed_git *pack;
|
||||
int err;
|
||||
|
||||
@@ -105,14 +114,19 @@ static int verify_one_pack(const char *path, int verbose)
|
||||
return error("packfile %s not found.", arg);
|
||||
|
||||
install_packed_git(pack);
|
||||
err = verify_pack(pack);
|
||||
|
||||
if (verbose) {
|
||||
if (!stat_only)
|
||||
err = verify_pack(pack);
|
||||
else
|
||||
err = open_pack_index(pack);
|
||||
|
||||
if (verbose || stat_only) {
|
||||
if (err)
|
||||
printf("%s: bad\n", pack->pack_name);
|
||||
else {
|
||||
show_pack_info(pack);
|
||||
printf("%s: ok\n", pack->pack_name);
|
||||
show_pack_info(pack, flags);
|
||||
if (!stat_only)
|
||||
printf("%s: ok\n", pack->pack_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,17 +134,20 @@ static int verify_one_pack(const char *path, int verbose)
|
||||
}
|
||||
|
||||
static const char * const verify_pack_usage[] = {
|
||||
"git verify-pack [-v|--verbose] <pack>...",
|
||||
"git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_verify_pack(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int err = 0;
|
||||
int verbose = 0;
|
||||
unsigned int flags = 0;
|
||||
int i;
|
||||
const struct option verify_pack_options[] = {
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT_BIT('v', "verbose", &flags, "verbose",
|
||||
VERIFY_PACK_VERBOSE),
|
||||
OPT_BIT('s', "stat-only", &flags, "show statistics only",
|
||||
VERIFY_PACK_STAT_ONLY),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -140,7 +157,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
|
||||
if (argc < 1)
|
||||
usage_with_options(verify_pack_usage, verify_pack_options);
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (verify_one_pack(argv[i], verbose))
|
||||
if (verify_one_pack(argv[i], flags))
|
||||
err = 1;
|
||||
discard_revindex();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ extern const char git_more_info_string[];
|
||||
extern void list_common_cmds_help(void);
|
||||
extern const char *help_unknown_cmd(const char *cmd);
|
||||
extern void prune_packed_objects(int);
|
||||
extern int read_line_with_nul(char *buf, int size, FILE *file);
|
||||
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
|
||||
struct strbuf *out);
|
||||
extern int commit_tree(const char *msg, unsigned char *tree,
|
||||
@@ -112,5 +111,6 @@ extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_replace(int argc, const char **argv, const char *prefix);
|
||||
|
||||
#endif
|
||||
|
||||
8
cache.h
8
cache.h
@@ -512,6 +512,7 @@ extern int log_all_ref_updates;
|
||||
extern int warn_ambiguous_refs;
|
||||
extern int shared_repository;
|
||||
extern const char *apply_default_whitespace;
|
||||
extern const char *apply_default_ignorewhitespace;
|
||||
extern int zlib_compression_level;
|
||||
extern int core_compression_level;
|
||||
extern int core_compression_seen;
|
||||
@@ -519,6 +520,7 @@ extern size_t packed_git_window_size;
|
||||
extern size_t packed_git_limit;
|
||||
extern size_t delta_base_cache_limit;
|
||||
extern int auto_crlf;
|
||||
extern int read_replace_refs;
|
||||
extern int fsync_object_files;
|
||||
extern int core_preload_index;
|
||||
|
||||
@@ -655,7 +657,11 @@ char *strip_path_suffix(const char *path, const char *suffix);
|
||||
|
||||
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
|
||||
extern int sha1_object_info(const unsigned char *, unsigned long *);
|
||||
extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
|
||||
extern void *read_sha1_file_repl(const unsigned char *sha1, enum object_type *type, unsigned long *size, const unsigned char **replacement);
|
||||
static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
|
||||
{
|
||||
return read_sha1_file_repl(sha1, type, size, NULL);
|
||||
}
|
||||
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
|
||||
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
|
||||
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
|
||||
|
||||
2
commit.h
2
commit.h
@@ -123,6 +123,8 @@ struct commit_graft *read_graft_line(char *buf, int len);
|
||||
int register_commit_graft(struct commit_graft *, int);
|
||||
struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
|
||||
|
||||
const unsigned char *lookup_replace_object(const unsigned char *sha1);
|
||||
|
||||
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
|
||||
extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
|
||||
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
/*
|
||||
* The size parameter specifies the available space, i.e. includes
|
||||
* the trailing NUL byte; but Windows's vsnprintf expects the
|
||||
* number of characters to write without the trailing NUL.
|
||||
* number of characters to write, and does not necessarily write the
|
||||
* trailing NUL.
|
||||
*/
|
||||
#ifndef SNPRINTF_SIZE_CORR
|
||||
#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4
|
||||
|
||||
@@ -674,6 +674,7 @@ _git_am ()
|
||||
--*)
|
||||
__gitcomp "
|
||||
--3way --committer-date-is-author-date --ignore-date
|
||||
--ignore-whitespace --ignore-space-change
|
||||
--interactive --keep --no-utf8 --signoff --utf8
|
||||
--whitespace=
|
||||
"
|
||||
@@ -695,6 +696,7 @@ _git_apply ()
|
||||
--stat --numstat --summary --check --index
|
||||
--cached --index-info --reverse --reject --unidiff-zero
|
||||
--apply --no-add --exclude=
|
||||
--ignore-whitespace --ignore-space-change
|
||||
--whitespace= --inaccurate-eof --verbose
|
||||
"
|
||||
return
|
||||
@@ -1537,6 +1539,7 @@ _git_config ()
|
||||
__gitcomp "
|
||||
add.ignore-errors
|
||||
alias.
|
||||
apply.ignorewhitespace
|
||||
apply.whitespace
|
||||
branch.autosetupmerge
|
||||
branch.autosetuprebase
|
||||
|
||||
@@ -201,7 +201,7 @@ def isModeExec(mode):
|
||||
def isModeExecChanged(src_mode, dst_mode):
|
||||
return isModeExec(src_mode) != isModeExec(dst_mode)
|
||||
|
||||
def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
|
||||
def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
|
||||
cmd = p4_build_cmd("-G %s" % (cmd))
|
||||
if verbose:
|
||||
sys.stderr.write("Opening pipe: %s\n" % cmd)
|
||||
@@ -224,7 +224,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
|
||||
try:
|
||||
while True:
|
||||
entry = marshal.load(p4.stdout)
|
||||
result.append(entry)
|
||||
if cb is not None:
|
||||
cb(entry)
|
||||
else:
|
||||
result.append(entry)
|
||||
except EOFError:
|
||||
pass
|
||||
exitCode = p4.wait()
|
||||
@@ -950,10 +953,84 @@ class P4Sync(Command):
|
||||
|
||||
return branches
|
||||
|
||||
## Should move this out, doesn't use SELF.
|
||||
def readP4Files(self, files):
|
||||
# output one file from the P4 stream
|
||||
# - helper for streamP4Files
|
||||
|
||||
def streamOneP4File(self, file, contents):
|
||||
if file["type"] == "apple":
|
||||
print "\nfile %s is a strange apple file that forks. Ignoring" % \
|
||||
file['depotFile']
|
||||
return
|
||||
|
||||
relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
|
||||
if verbose:
|
||||
sys.stderr.write("%s\n" % relPath)
|
||||
|
||||
mode = "644"
|
||||
if isP4Exec(file["type"]):
|
||||
mode = "755"
|
||||
elif file["type"] == "symlink":
|
||||
mode = "120000"
|
||||
# p4 print on a symlink contains "target\n", so strip it off
|
||||
last = contents.pop()
|
||||
last = last[:-1]
|
||||
contents.append(last)
|
||||
|
||||
if self.isWindows and file["type"].endswith("text"):
|
||||
mangled = []
|
||||
for data in contents:
|
||||
data = data.replace("\r\n", "\n")
|
||||
mangled.append(data)
|
||||
contents = mangled
|
||||
|
||||
if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
|
||||
contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
|
||||
elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
|
||||
contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents)
|
||||
|
||||
self.gitStream.write("M %s inline %s\n" % (mode, relPath))
|
||||
|
||||
# total length...
|
||||
length = 0
|
||||
for d in contents:
|
||||
length = length + len(d)
|
||||
|
||||
self.gitStream.write("data %d\n" % length)
|
||||
for d in contents:
|
||||
self.gitStream.write(d)
|
||||
self.gitStream.write("\n")
|
||||
|
||||
def streamOneP4Deletion(self, file):
|
||||
relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
|
||||
if verbose:
|
||||
sys.stderr.write("delete %s\n" % relPath)
|
||||
self.gitStream.write("D %s\n" % relPath)
|
||||
|
||||
# handle another chunk of streaming data
|
||||
def streamP4FilesCb(self, marshalled):
|
||||
|
||||
if marshalled.has_key('depotFile') and self.stream_have_file_info:
|
||||
# start of a new file - output the old one first
|
||||
self.streamOneP4File(self.stream_file, self.stream_contents)
|
||||
self.stream_file = {}
|
||||
self.stream_contents = []
|
||||
self.stream_have_file_info = False
|
||||
|
||||
# pick up the new file information... for the
|
||||
# 'data' field we need to append to our array
|
||||
for k in marshalled.keys():
|
||||
if k == 'data':
|
||||
self.stream_contents.append(marshalled['data'])
|
||||
else:
|
||||
self.stream_file[k] = marshalled[k]
|
||||
|
||||
self.stream_have_file_info = True
|
||||
|
||||
# Stream directly from "p4 files" into "git fast-import"
|
||||
def streamP4Files(self, files):
|
||||
filesForCommit = []
|
||||
filesToRead = []
|
||||
filesToDelete = []
|
||||
|
||||
for f in files:
|
||||
includeFile = True
|
||||
@@ -967,50 +1044,35 @@ class P4Sync(Command):
|
||||
filesForCommit.append(f)
|
||||
if f['action'] not in ('delete', 'purge'):
|
||||
filesToRead.append(f)
|
||||
else:
|
||||
filesToDelete.append(f)
|
||||
|
||||
# deleted files...
|
||||
for f in filesToDelete:
|
||||
self.streamOneP4Deletion(f)
|
||||
|
||||
filedata = []
|
||||
if len(filesToRead) > 0:
|
||||
filedata = p4CmdList('-x - print',
|
||||
stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
|
||||
self.stream_file = {}
|
||||
self.stream_contents = []
|
||||
self.stream_have_file_info = False
|
||||
|
||||
# curry self argument
|
||||
def streamP4FilesCbSelf(entry):
|
||||
self.streamP4FilesCb(entry)
|
||||
|
||||
p4CmdList("-x - print",
|
||||
'\n'.join(['%s#%s' % (f['path'], f['rev'])
|
||||
for f in filesToRead]),
|
||||
stdin_mode='w+')
|
||||
cb=streamP4FilesCbSelf)
|
||||
|
||||
if "p4ExitCode" in filedata[0]:
|
||||
die("Problems executing p4. Error: [%d]."
|
||||
% (filedata[0]['p4ExitCode']));
|
||||
|
||||
j = 0;
|
||||
contents = {}
|
||||
while j < len(filedata):
|
||||
stat = filedata[j]
|
||||
j += 1
|
||||
text = ''
|
||||
while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
|
||||
text += filedata[j]['data']
|
||||
del filedata[j]['data']
|
||||
j += 1
|
||||
|
||||
if not stat.has_key('depotFile'):
|
||||
sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
|
||||
continue
|
||||
|
||||
if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
|
||||
text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
|
||||
elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
|
||||
text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
|
||||
|
||||
contents[stat['depotFile']] = text
|
||||
|
||||
for f in filesForCommit:
|
||||
path = f['path']
|
||||
if contents.has_key(path):
|
||||
f['data'] = contents[path]
|
||||
|
||||
return filesForCommit
|
||||
# do the last chunk
|
||||
if self.stream_file.has_key('depotFile'):
|
||||
self.streamOneP4File(self.stream_file, self.stream_contents)
|
||||
|
||||
def commit(self, details, files, branch, branchPrefixes, parent = ""):
|
||||
epoch = details["time"]
|
||||
author = details["user"]
|
||||
self.branchPrefixes = branchPrefixes
|
||||
|
||||
if self.verbose:
|
||||
print "commit into %s" % branch
|
||||
@@ -1023,7 +1085,6 @@ class P4Sync(Command):
|
||||
new_files.append (f)
|
||||
else:
|
||||
sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
|
||||
files = self.readP4Files(new_files)
|
||||
|
||||
self.gitStream.write("commit %s\n" % branch)
|
||||
# gitStream.write("mark :%s\n" % details["change"])
|
||||
@@ -1051,33 +1112,7 @@ class P4Sync(Command):
|
||||
print "parent %s" % parent
|
||||
self.gitStream.write("from %s\n" % parent)
|
||||
|
||||
for file in files:
|
||||
if file["type"] == "apple":
|
||||
print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
|
||||
continue
|
||||
|
||||
relPath = self.stripRepoPath(file['path'], branchPrefixes)
|
||||
if file["action"] in ("delete", "purge"):
|
||||
self.gitStream.write("D %s\n" % relPath)
|
||||
else:
|
||||
data = file['data']
|
||||
|
||||
mode = "644"
|
||||
if isP4Exec(file["type"]):
|
||||
mode = "755"
|
||||
elif file["type"] == "symlink":
|
||||
mode = "120000"
|
||||
# p4 print on a symlink contains "target\n", so strip it off
|
||||
data = data[:-1]
|
||||
|
||||
if self.isWindows and file["type"].endswith("text"):
|
||||
data = data.replace("\r\n", "\n")
|
||||
|
||||
self.gitStream.write("M %s inline %s\n" % (mode, relPath))
|
||||
self.gitStream.write("data %s\n" % len(data))
|
||||
self.gitStream.write(data)
|
||||
self.gitStream.write("\n")
|
||||
|
||||
self.streamP4Files(new_files)
|
||||
self.gitStream.write("\n")
|
||||
|
||||
change = int(details["change"])
|
||||
|
||||
@@ -26,6 +26,7 @@ const char *git_commit_encoding;
|
||||
const char *git_log_output_encoding;
|
||||
int shared_repository = PERM_UMASK;
|
||||
const char *apply_default_whitespace;
|
||||
const char *apply_default_ignorewhitespace;
|
||||
int zlib_compression_level = Z_BEST_SPEED;
|
||||
int core_compression_level;
|
||||
int core_compression_seen;
|
||||
@@ -38,6 +39,7 @@ int pager_use_color = 1;
|
||||
const char *editor_program;
|
||||
const char *excludes_file;
|
||||
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
|
||||
int read_replace_refs = 1;
|
||||
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
|
||||
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
|
||||
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
|
||||
|
||||
@@ -841,6 +841,10 @@ sub coalesce_overlapping_hunks {
|
||||
my ($last_o_ctx, $last_was_dirty);
|
||||
|
||||
for (grep { $_->{USE} } @in) {
|
||||
if ($_->{TYPE} ne 'hunk') {
|
||||
push @out, $_;
|
||||
next;
|
||||
}
|
||||
my $text = $_->{TEXT};
|
||||
my ($o_ofs) = parse_hunk_header($text->[0]);
|
||||
if (defined $last_o_ctx &&
|
||||
|
||||
12
git-am.sh
12
git-am.sh
@@ -16,6 +16,8 @@ s,signoff add a Signed-off-by line to the commit message
|
||||
u,utf8 recode into utf8 (default)
|
||||
k,keep pass -k flag to git-mailinfo
|
||||
whitespace= pass it through git-apply
|
||||
ignore-space-change pass it through git-apply
|
||||
ignore-whitespace pass it through git-apply
|
||||
directory= pass it through git-apply
|
||||
C= pass it through git-apply
|
||||
p= pass it through git-apply
|
||||
@@ -211,7 +213,13 @@ check_patch_format () {
|
||||
split_patches () {
|
||||
case "$patch_format" in
|
||||
mbox)
|
||||
git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||
|
||||
case "$rebasing" in
|
||||
'')
|
||||
keep_cr= ;;
|
||||
?*)
|
||||
keep_cr=--keep-cr ;;
|
||||
esac
|
||||
git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
|
||||
clean_abort
|
||||
;;
|
||||
stgit-series)
|
||||
@@ -321,7 +329,7 @@ do
|
||||
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
|
||||
--patch-format)
|
||||
shift ; patch_format="$1" ;;
|
||||
--reject)
|
||||
--reject|--ignore-whitespace|--ignore-space-change)
|
||||
git_apply_opt="$git_apply_opt $1" ;;
|
||||
--committer-date-is-author-date)
|
||||
committer_date_is_author_date=t ;;
|
||||
|
||||
@@ -57,10 +57,8 @@
|
||||
# endif
|
||||
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
|
||||
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
|
||||
#ifndef __sun__
|
||||
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
|
||||
#endif
|
||||
#endif
|
||||
#define _ALL_SOURCE 1
|
||||
#define _GNU_SOURCE 1
|
||||
#define _BSD_SOURCE 1
|
||||
|
||||
@@ -97,12 +97,12 @@ set_ident () {
|
||||
echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
|
||||
}
|
||||
|
||||
USAGE="[--env-filter <command>] [--tree-filter <command>] \
|
||||
[--index-filter <command>] [--parent-filter <command>] \
|
||||
[--msg-filter <command>] [--commit-filter <command>] \
|
||||
[--tag-name-filter <command>] [--subdirectory-filter <directory>] \
|
||||
[--original <namespace>] [-d <directory>] [-f | --force] \
|
||||
[<rev-list options>...]"
|
||||
USAGE="[--env-filter <command>] [--tree-filter <command>]
|
||||
[--index-filter <command>] [--parent-filter <command>]
|
||||
[--msg-filter <command>] [--commit-filter <command>]
|
||||
[--tag-name-filter <command>] [--subdirectory-filter <directory>]
|
||||
[--original <namespace>] [-d <directory>] [-f | --force]
|
||||
[<rev-list options>...]"
|
||||
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
|
||||
@@ -333,6 +333,9 @@ do
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--ignore-whitespace)
|
||||
git_am_opt="$git_am_opt $1"
|
||||
;;
|
||||
--committer-date-is-author-date|--ignore-date)
|
||||
git_am_opt="$git_am_opt $1"
|
||||
force_rebase=t
|
||||
|
||||
11
git-svn.perl
11
git-svn.perl
@@ -21,6 +21,15 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
|
||||
$Git::SVN::Ra::_log_window_size = 100;
|
||||
$Git::SVN::_minimize_url = 'unset';
|
||||
|
||||
if (! exists $ENV{SVN_SSH}) {
|
||||
if (exists $ENV{GIT_SSH}) {
|
||||
$ENV{SVN_SSH} = $ENV{GIT_SSH};
|
||||
if ($^O eq 'msys') {
|
||||
$ENV{SVN_SSH} =~ s/\\/\\\\/g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Git::SVN::Log::TZ = $ENV{TZ};
|
||||
$ENV{TZ} = 'UTC';
|
||||
$| = 1; # unbuffer STDOUT
|
||||
@@ -1222,6 +1231,7 @@ sub complete_url_ls_init {
|
||||
}
|
||||
command_oneline('config', $k, $gs->{url}) unless $orig_url;
|
||||
my $remote_path = "$gs->{path}/$repo_path";
|
||||
$remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
|
||||
$remote_path =~ s#/+#/#g;
|
||||
$remote_path =~ s#^/##g;
|
||||
$remote_path .= "/*" if $remote_path !~ /\*/;
|
||||
@@ -1892,6 +1902,7 @@ sub init_remote_config {
|
||||
command_noisy('config',
|
||||
"svn-remote.$self->{repo_id}.url", $url);
|
||||
$self->{path} =~ s{^/}{};
|
||||
$self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
|
||||
command_noisy('config', '--add',
|
||||
"svn-remote.$self->{repo_id}.fetch",
|
||||
"$self->{path}:".$self->refname);
|
||||
|
||||
1
git.c
1
git.c
@@ -339,6 +339,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "receive-pack", cmd_receive_pack },
|
||||
{ "reflog", cmd_reflog, RUN_SETUP },
|
||||
{ "remote", cmd_remote, RUN_SETUP },
|
||||
{ "replace", cmd_replace, RUN_SETUP },
|
||||
{ "repo-config", cmd_config },
|
||||
{ "rerere", cmd_rerere, RUN_SETUP },
|
||||
{ "reset", cmd_reset, RUN_SETUP },
|
||||
|
||||
@@ -123,6 +123,15 @@ GITWEB_CONFIG file:
|
||||
$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
|
||||
$feature{'snapshot'}{'override'} = 1;
|
||||
|
||||
If you allow overriding for the snapshot feature, you can specify which
|
||||
snapshot formats are globally disabled. You can also add any command line
|
||||
options you want (such as setting the compression level). For instance,
|
||||
you can disable Zip compressed snapshots and set GZip to run at level 6 by
|
||||
adding the following lines to your $GITWEB_CONFIG:
|
||||
|
||||
$known_snapshot_formats{'zip'}{'disabled'} = 1;
|
||||
$known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
|
||||
|
||||
|
||||
Gitweb repositories
|
||||
-------------------
|
||||
|
||||
@@ -160,7 +160,8 @@ our %known_snapshot_formats = (
|
||||
# 'suffix' => filename suffix,
|
||||
# 'format' => --format for git-archive,
|
||||
# 'compressor' => [compressor command and arguments]
|
||||
# (array reference, optional)}
|
||||
# (array reference, optional)
|
||||
# 'disabled' => boolean (optional)}
|
||||
#
|
||||
'tgz' => {
|
||||
'display' => 'tar.gz',
|
||||
@@ -176,6 +177,14 @@ our %known_snapshot_formats = (
|
||||
'format' => 'tar',
|
||||
'compressor' => ['bzip2']},
|
||||
|
||||
'txz' => {
|
||||
'display' => 'tar.xz',
|
||||
'type' => 'application/x-xz',
|
||||
'suffix' => '.tar.xz',
|
||||
'format' => 'tar',
|
||||
'compressor' => ['xz'],
|
||||
'disabled' => 1},
|
||||
|
||||
'zip' => {
|
||||
'display' => 'zip',
|
||||
'type' => 'application/x-zip',
|
||||
@@ -188,6 +197,7 @@ our %known_snapshot_formats = (
|
||||
our %known_snapshot_format_aliases = (
|
||||
'gzip' => 'tgz',
|
||||
'bzip2' => 'tbz2',
|
||||
'xz' => 'txz',
|
||||
|
||||
# backward compatibility: legacy gitweb config support
|
||||
'x-gzip' => undef, 'gz' => undef,
|
||||
@@ -494,7 +504,8 @@ sub filter_snapshot_fmts {
|
||||
exists $known_snapshot_format_aliases{$_} ?
|
||||
$known_snapshot_format_aliases{$_} : $_} @fmts;
|
||||
@fmts = grep {
|
||||
exists $known_snapshot_formats{$_} } @fmts;
|
||||
exists $known_snapshot_formats{$_} &&
|
||||
!$known_snapshot_formats{$_}{'disabled'}} @fmts;
|
||||
}
|
||||
|
||||
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
|
||||
@@ -1513,10 +1524,10 @@ sub format_subject_html {
|
||||
$long =~ s/[[:cntrl:]]/?/g;
|
||||
return $cgi->a({-href => $href, -class => "list subject",
|
||||
-title => to_utf8($long)},
|
||||
esc_html($short) . $extra);
|
||||
esc_html($short)) . $extra;
|
||||
} else {
|
||||
return $cgi->a({-href => $href, -class => "list subject"},
|
||||
esc_html($long) . $extra);
|
||||
esc_html($long)) . $extra;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5181,6 +5192,8 @@ sub git_snapshot {
|
||||
die_error(400, "Unknown snapshot format");
|
||||
} elsif (!grep($_ eq $format, @snapshot_fmts)) {
|
||||
die_error(403, "Unsupported snapshot format");
|
||||
} elsif ($known_snapshot_formats{$format}{'disabled'}) {
|
||||
die_error(403, "Snapshot format not allowed");
|
||||
}
|
||||
|
||||
if (!defined $hash) {
|
||||
|
||||
14
graph.c
14
graph.c
@@ -225,7 +225,12 @@ struct git_graph *graph_init(struct rev_info *opt)
|
||||
graph->num_columns = 0;
|
||||
graph->num_new_columns = 0;
|
||||
graph->mapping_size = 0;
|
||||
graph->default_column_color = 0;
|
||||
/*
|
||||
* Start the column color at the maximum value, since we'll
|
||||
* always increment it for the first commit we output.
|
||||
* This way we start at 0 for the first commit.
|
||||
*/
|
||||
graph->default_column_color = COLUMN_COLORS_MAX - 1;
|
||||
|
||||
/*
|
||||
* Allocate a reasonably large default number of columns
|
||||
@@ -499,11 +504,14 @@ static void graph_update_columns(struct git_graph *graph)
|
||||
parent;
|
||||
parent = next_interesting_parent(graph, parent)) {
|
||||
/*
|
||||
* If this is a merge increment the current
|
||||
* If this is a merge, or the start of a new
|
||||
* childless column, increment the current
|
||||
* color.
|
||||
*/
|
||||
if (graph->num_parents > 1)
|
||||
if (graph->num_parents > 1 ||
|
||||
!is_commit_in_columns) {
|
||||
graph_increment_column_color(graph);
|
||||
}
|
||||
graph_insert_into_new_columns(graph,
|
||||
parent->item,
|
||||
&mapping_idx);
|
||||
|
||||
4
http.c
4
http.c
@@ -719,7 +719,9 @@ void append_remote_object_url(struct strbuf *buf, const char *url,
|
||||
const char *hex,
|
||||
int only_two_digit_prefix)
|
||||
{
|
||||
strbuf_addf(buf, "%s/objects/%.*s/", url, 2, hex);
|
||||
end_url_with_slash(buf, url);
|
||||
|
||||
strbuf_addf(buf, "objects/%.*s/", 2, hex);
|
||||
if (!only_two_digit_prefix)
|
||||
strbuf_addf(buf, "%s", hex+2);
|
||||
}
|
||||
|
||||
7
mktag.c
7
mktag.c
@@ -19,16 +19,17 @@
|
||||
/*
|
||||
* We refuse to tag something we can't verify. Just because.
|
||||
*/
|
||||
static int verify_object(unsigned char *sha1, const char *expected_type)
|
||||
static int verify_object(const unsigned char *sha1, const char *expected_type)
|
||||
{
|
||||
int ret = -1;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
void *buffer = read_sha1_file(sha1, &type, &size);
|
||||
const unsigned char *repl;
|
||||
void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
|
||||
|
||||
if (buffer) {
|
||||
if (type == type_from_string(expected_type))
|
||||
ret = check_sha1_signature(sha1, buffer, size, expected_type);
|
||||
ret = check_sha1_signature(repl, buffer, size, expected_type);
|
||||
free(buffer);
|
||||
}
|
||||
return ret;
|
||||
|
||||
9
object.c
9
object.c
@@ -188,17 +188,18 @@ struct object *parse_object(const unsigned char *sha1)
|
||||
unsigned long size;
|
||||
enum object_type type;
|
||||
int eaten;
|
||||
void *buffer = read_sha1_file(sha1, &type, &size);
|
||||
const unsigned char *repl;
|
||||
void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
|
||||
|
||||
if (buffer) {
|
||||
struct object *obj;
|
||||
if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
|
||||
if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
|
||||
free(buffer);
|
||||
error("sha1 mismatch %s\n", sha1_to_hex(sha1));
|
||||
error("sha1 mismatch %s\n", sha1_to_hex(repl));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
|
||||
obj = parse_object_buffer(repl, type, size, buffer, &eaten);
|
||||
if (!eaten)
|
||||
free(buffer);
|
||||
return obj;
|
||||
|
||||
@@ -549,6 +549,14 @@ void usage_with_options(const char * const *usagestr,
|
||||
exit(129);
|
||||
}
|
||||
|
||||
void usage_msg_opt(const char *msg,
|
||||
const char * const *usagestr,
|
||||
const struct option *options)
|
||||
{
|
||||
fprintf(stderr, "%s\n\n", msg);
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
|
||||
int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
{
|
||||
|
||||
@@ -145,6 +145,10 @@ extern int parse_options(int argc, const char **argv, const char *prefix,
|
||||
extern NORETURN void usage_with_options(const char * const *usagestr,
|
||||
const struct option *options);
|
||||
|
||||
extern NORETURN void usage_msg_opt(const char *msg,
|
||||
const char * const *usagestr,
|
||||
const struct option *options);
|
||||
|
||||
/*----- incremental advanced APIs -----*/
|
||||
|
||||
enum {
|
||||
|
||||
5
refs.c
5
refs.c
@@ -668,6 +668,11 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data)
|
||||
return for_each_ref_in("refs/remotes/", fn, cb_data);
|
||||
}
|
||||
|
||||
int for_each_replace_ref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
|
||||
}
|
||||
|
||||
int for_each_rawref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/", fn, 0,
|
||||
|
||||
1
refs.h
1
refs.h
@@ -24,6 +24,7 @@ extern int for_each_ref_in(const char *, each_ref_fn, void *);
|
||||
extern int for_each_tag_ref(each_ref_fn, void *);
|
||||
extern int for_each_branch_ref(each_ref_fn, void *);
|
||||
extern int for_each_remote_ref(each_ref_fn, void *);
|
||||
extern int for_each_replace_ref(each_ref_fn, void *);
|
||||
|
||||
/* can be used to learn about broken ref and symref */
|
||||
extern int for_each_rawref(each_ref_fn, void *);
|
||||
|
||||
114
replace_object.c
Normal file
114
replace_object.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "cache.h"
|
||||
#include "sha1-lookup.h"
|
||||
#include "refs.h"
|
||||
|
||||
static struct replace_object {
|
||||
unsigned char sha1[2][20];
|
||||
} **replace_object;
|
||||
|
||||
static int replace_object_alloc, replace_object_nr;
|
||||
|
||||
static const unsigned char *replace_sha1_access(size_t index, void *table)
|
||||
{
|
||||
struct replace_object **replace = table;
|
||||
return replace[index]->sha1[0];
|
||||
}
|
||||
|
||||
static int replace_object_pos(const unsigned char *sha1)
|
||||
{
|
||||
return sha1_pos(sha1, replace_object, replace_object_nr,
|
||||
replace_sha1_access);
|
||||
}
|
||||
|
||||
static int register_replace_object(struct replace_object *replace,
|
||||
int ignore_dups)
|
||||
{
|
||||
int pos = replace_object_pos(replace->sha1[0]);
|
||||
|
||||
if (0 <= pos) {
|
||||
if (ignore_dups)
|
||||
free(replace);
|
||||
else {
|
||||
free(replace_object[pos]);
|
||||
replace_object[pos] = replace;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
pos = -pos - 1;
|
||||
if (replace_object_alloc <= ++replace_object_nr) {
|
||||
replace_object_alloc = alloc_nr(replace_object_alloc);
|
||||
replace_object = xrealloc(replace_object,
|
||||
sizeof(*replace_object) *
|
||||
replace_object_alloc);
|
||||
}
|
||||
if (pos < replace_object_nr)
|
||||
memmove(replace_object + pos + 1,
|
||||
replace_object + pos,
|
||||
(replace_object_nr - pos - 1) *
|
||||
sizeof(*replace_object));
|
||||
replace_object[pos] = replace;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_replace_ref(const char *refname,
|
||||
const unsigned char *sha1,
|
||||
int flag, void *cb_data)
|
||||
{
|
||||
/* Get sha1 from refname */
|
||||
const char *slash = strrchr(refname, '/');
|
||||
const char *hash = slash ? slash + 1 : refname;
|
||||
struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
|
||||
|
||||
if (strlen(hash) != 40 || get_sha1_hex(hash, repl_obj->sha1[0])) {
|
||||
free(repl_obj);
|
||||
warning("bad replace ref name: %s", refname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy sha1 from the read ref */
|
||||
hashcpy(repl_obj->sha1[1], sha1);
|
||||
|
||||
/* Register new object */
|
||||
if (register_replace_object(repl_obj, 1))
|
||||
die("duplicate replace ref: %s", refname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prepare_replace_object(void)
|
||||
{
|
||||
static int replace_object_prepared;
|
||||
|
||||
if (replace_object_prepared)
|
||||
return;
|
||||
|
||||
for_each_replace_ref(register_replace_ref, NULL);
|
||||
replace_object_prepared = 1;
|
||||
}
|
||||
|
||||
/* We allow "recursive" replacement. Only within reason, though */
|
||||
#define MAXREPLACEDEPTH 5
|
||||
|
||||
const unsigned char *lookup_replace_object(const unsigned char *sha1)
|
||||
{
|
||||
int pos, depth = MAXREPLACEDEPTH;
|
||||
const unsigned char *cur = sha1;
|
||||
|
||||
if (!read_replace_refs)
|
||||
return sha1;
|
||||
|
||||
prepare_replace_object();
|
||||
|
||||
/* Try to recursively replace the object */
|
||||
do {
|
||||
if (--depth < 0)
|
||||
die("replace depth too high for object %s",
|
||||
sha1_to_hex(sha1));
|
||||
|
||||
pos = replace_object_pos(cur);
|
||||
if (0 <= pos)
|
||||
cur = replace_object[pos]->sha1[1];
|
||||
} while (0 <= pos);
|
||||
|
||||
return cur;
|
||||
}
|
||||
23
sha1_file.c
23
sha1_file.c
@@ -2144,13 +2144,26 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
|
||||
return read_packed_sha1(sha1, type, size);
|
||||
}
|
||||
|
||||
void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
|
||||
unsigned long *size)
|
||||
void *read_sha1_file_repl(const unsigned char *sha1,
|
||||
enum object_type *type,
|
||||
unsigned long *size,
|
||||
const unsigned char **replacement)
|
||||
{
|
||||
void *data = read_object(sha1, type, size);
|
||||
const unsigned char *repl = lookup_replace_object(sha1);
|
||||
void *data = read_object(repl, type, size);
|
||||
|
||||
/* die if we replaced an object with one that does not exist */
|
||||
if (!data && repl != sha1)
|
||||
die("replacement %s not found for %s",
|
||||
sha1_to_hex(repl), sha1_to_hex(sha1));
|
||||
|
||||
/* legacy behavior is to die on corrupted objects */
|
||||
if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1)))
|
||||
die("object %s is corrupted", sha1_to_hex(sha1));
|
||||
if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
|
||||
die("object %s is corrupted", sha1_to_hex(repl));
|
||||
|
||||
if (replacement)
|
||||
*replacement = repl;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
15
strbuf.c
15
strbuf.c
@@ -322,7 +322,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
|
||||
int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
|
||||
{
|
||||
int ch;
|
||||
|
||||
@@ -332,10 +332,10 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
|
||||
|
||||
strbuf_reset(sb);
|
||||
while ((ch = fgetc(fp)) != EOF) {
|
||||
if (ch == term)
|
||||
break;
|
||||
strbuf_grow(sb, 1);
|
||||
sb->buf[sb->len++] = ch;
|
||||
if (ch == term)
|
||||
break;
|
||||
}
|
||||
if (ch == EOF && sb->len == 0)
|
||||
return EOF;
|
||||
@@ -344,6 +344,15 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
|
||||
{
|
||||
if (strbuf_getwholeline(sb, fp, term))
|
||||
return EOF;
|
||||
if (sb->buf[sb->len-1] == term)
|
||||
strbuf_setlen(sb, sb->len-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
|
||||
{
|
||||
int fd, len;
|
||||
|
||||
1
strbuf.h
1
strbuf.h
@@ -126,6 +126,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
|
||||
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
|
||||
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
|
||||
|
||||
extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
|
||||
extern int strbuf_getline(struct strbuf *, FILE *, int);
|
||||
|
||||
extern void stripspace(struct strbuf *buf, int skip_comments);
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
# Copyright (c) 2005 Amos Waterland
|
||||
#
|
||||
|
||||
test_description='git rebase should not destroy author information
|
||||
test_description='git rebase assorted tests
|
||||
|
||||
This test runs git rebase and checks that the author information is not lost.
|
||||
This test runs git rebase and checks that the author information is not lost
|
||||
among other things.
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
@@ -133,4 +134,25 @@ test_expect_success 'rebase -q is quiet' '
|
||||
test ! -s output.out
|
||||
'
|
||||
|
||||
q_to_cr () {
|
||||
tr Q '\015'
|
||||
}
|
||||
|
||||
test_expect_success 'Rebase a commit that sprinkles CRs in' '
|
||||
(
|
||||
echo "One"
|
||||
echo "TwoQ"
|
||||
echo "Three"
|
||||
echo "FQur"
|
||||
echo "Five"
|
||||
) | q_to_cr >CR &&
|
||||
git add CR &&
|
||||
test_tick &&
|
||||
git commit -a -m "A file with a line with CR" &&
|
||||
git tag file-with-cr &&
|
||||
git checkout HEAD^0 &&
|
||||
git rebase --onto HEAD^^ HEAD^ &&
|
||||
git diff --exit-code file-with-cr:CR HEAD:CR
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -163,6 +163,17 @@ test_expect_success FILEMODE 'stage mode but not hunk' '
|
||||
git diff file | grep "+content"
|
||||
'
|
||||
|
||||
|
||||
test_expect_success FILEMODE 'stage mode and hunk' '
|
||||
git reset --hard &&
|
||||
echo content >>file &&
|
||||
chmod +x file &&
|
||||
printf "y\\ny\\n" | git add -p &&
|
||||
git diff --cached file | grep "new mode" &&
|
||||
git diff --cached file | grep "+content" &&
|
||||
test -z "$(git diff file)"
|
||||
'
|
||||
|
||||
# end of tests disabled when filemode is not usable
|
||||
|
||||
test_expect_success 'setup again' '
|
||||
|
||||
185
t/t4107-apply-ignore-whitespace.sh
Executable file
185
t/t4107-apply-ignore-whitespace.sh
Executable file
@@ -0,0 +1,185 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009 Giuseppe Bilotta
|
||||
#
|
||||
|
||||
test_description='git-apply --ignore-whitespace.
|
||||
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
# This primes main.c file that indents without using HT at all.
|
||||
# Various patches with HT and other spaces are attempted in the test.
|
||||
|
||||
cat > patch1.patch <<\EOF
|
||||
diff --git a/main.c b/main.c
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/main.c
|
||||
@@ -0,0 +1,22 @@
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+void print_int(int num);
|
||||
+int func(int num);
|
||||
+
|
||||
+int main() {
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < 10; i++) {
|
||||
+ print_int(func(i)); /* stuff */
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int func(int num) {
|
||||
+ return num * num;
|
||||
+}
|
||||
+
|
||||
+void print_int(int num) {
|
||||
+ printf("%d", num);
|
||||
+}
|
||||
EOF
|
||||
|
||||
# Since whitespace is very significant and we want to prevent whitespace
|
||||
# mangling when creating this test from a patch, we protect 'fixable'
|
||||
# whitespace by replacing spaces with Z and replacing them at patch
|
||||
# creation time, hence the sed trick.
|
||||
|
||||
# This patch will fail unless whitespace differences are being ignored
|
||||
|
||||
sed -e 's/Z/ /g' > patch2.patch <<\EOF
|
||||
diff --git a/main.c b/main.c
|
||||
--- a/main.c
|
||||
+++ b/main.c
|
||||
@@ -10,6 +10,8 @@
|
||||
Z print_int(func(i)); /* stuff */
|
||||
Z }
|
||||
Z
|
||||
+ printf("\n");
|
||||
+
|
||||
Z return 0;
|
||||
Z}
|
||||
Z
|
||||
EOF
|
||||
|
||||
# This patch will fail even if whitespace differences are being ignored,
|
||||
# because of the missing string at EOL. TODO: this testcase should be
|
||||
# improved by creating a line that has the same hash with and without
|
||||
# the final string.
|
||||
|
||||
sed -e 's/Z/ /g' > patch3.patch <<\EOF
|
||||
diff --git a/main.c b/main.c
|
||||
--- a/main.c
|
||||
+++ b/main.c
|
||||
@@ -10,3 +10,4 @@
|
||||
Z for (i = 0; i < 10; i++) {
|
||||
Z print_int(func(i));Z
|
||||
+ /* stuff */
|
||||
Z }
|
||||
EOF
|
||||
|
||||
# This patch will fail even if whitespace differences are being ignored,
|
||||
# because of the missing EOL at EOF.
|
||||
|
||||
sed -e 's/Z/ /g' > patch4.patch <<\EOF
|
||||
diff --git a/main.c b/main.c
|
||||
--- a/main.c
|
||||
+++ b/main.c
|
||||
@@ -21,1 +21,1 @@
|
||||
- };Z
|
||||
\ No newline at end of file
|
||||
+ };
|
||||
EOF
|
||||
|
||||
# This patch will fail unless whitespace differences are being ignored.
|
||||
|
||||
sed -e 's/Z/ /g' > patch5.patch <<\EOF
|
||||
diff --git a/main.c b/main.c
|
||||
--- a/main.c
|
||||
+++ b/main.c
|
||||
@@ -2,2 +2,3 @@
|
||||
Z void print_int(int num);
|
||||
+ /* a comment */
|
||||
Z int func(int num);
|
||||
EOF
|
||||
|
||||
# And this is how the final output should be. Patches introduce
|
||||
# HTs but the original SP indents are mostly kept.
|
||||
|
||||
sed -e 's/T/ /g' > main.c.final <<\EOF
|
||||
#include <stdio.h>
|
||||
|
||||
void print_int(int num);
|
||||
T/* a comment */
|
||||
int func(int num);
|
||||
|
||||
int main() {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
print_int(func(i)); /* stuff */
|
||||
}
|
||||
|
||||
Tprintf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int func(int num) {
|
||||
return num * num;
|
||||
}
|
||||
|
||||
void print_int(int num) {
|
||||
printf("%d", num);
|
||||
}
|
||||
EOF
|
||||
|
||||
test_expect_success 'file creation' '
|
||||
git-apply patch1.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patch2 fails (retab)' '
|
||||
test_must_fail git-apply patch2.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patch2 applies with --ignore-whitespace' '
|
||||
git-apply --ignore-whitespace patch2.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patch2 reverse applies with --ignore-space-change' '
|
||||
git-apply -R --ignore-space-change patch2.patch
|
||||
'
|
||||
|
||||
git config apply.ignorewhitespace change
|
||||
|
||||
test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
|
||||
git-apply patch2.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patch3 fails (missing string at EOL)' '
|
||||
test_must_fail git-apply patch3.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patch4 fails (missing EOL at EOF)' '
|
||||
test_must_fail git-apply patch4.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patch5 applies (leading whitespace)' '
|
||||
git-apply patch5.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patches do not mangle whitespace' '
|
||||
test_cmp main.c main.c.final
|
||||
'
|
||||
|
||||
test_expect_success 're-create file (with --ignore-whitespace)' '
|
||||
rm -f main.c &&
|
||||
git-apply patch1.patch
|
||||
'
|
||||
|
||||
test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
|
||||
test_must_fail git-apply --no-ignore-whitespace patch5.patch
|
||||
'
|
||||
|
||||
test_done
|
||||
200
t/t6050-replace.sh
Executable file
200
t/t6050-replace.sh
Executable file
@@ -0,0 +1,200 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2008 Christian Couder
|
||||
#
|
||||
test_description='Tests replace refs functionality'
|
||||
|
||||
exec </dev/null
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
add_and_commit_file()
|
||||
{
|
||||
_file="$1"
|
||||
_msg="$2"
|
||||
|
||||
git add $_file || return $?
|
||||
test_tick || return $?
|
||||
git commit --quiet -m "$_file: $_msg"
|
||||
}
|
||||
|
||||
HASH1=
|
||||
HASH2=
|
||||
HASH3=
|
||||
HASH4=
|
||||
HASH5=
|
||||
HASH6=
|
||||
HASH7=
|
||||
|
||||
test_expect_success 'set up buggy branch' '
|
||||
echo "line 1" >> hello &&
|
||||
echo "line 2" >> hello &&
|
||||
echo "line 3" >> hello &&
|
||||
echo "line 4" >> hello &&
|
||||
add_and_commit_file hello "4 lines" &&
|
||||
HASH1=$(git rev-parse --verify HEAD) &&
|
||||
echo "line BUG" >> hello &&
|
||||
echo "line 6" >> hello &&
|
||||
echo "line 7" >> hello &&
|
||||
echo "line 8" >> hello &&
|
||||
add_and_commit_file hello "4 more lines with a BUG" &&
|
||||
HASH2=$(git rev-parse --verify HEAD) &&
|
||||
echo "line 9" >> hello &&
|
||||
echo "line 10" >> hello &&
|
||||
add_and_commit_file hello "2 more lines" &&
|
||||
HASH3=$(git rev-parse --verify HEAD) &&
|
||||
echo "line 11" >> hello &&
|
||||
add_and_commit_file hello "1 more line" &&
|
||||
HASH4=$(git rev-parse --verify HEAD) &&
|
||||
sed -e "s/BUG/5/" hello > hello.new &&
|
||||
mv hello.new hello &&
|
||||
add_and_commit_file hello "BUG fixed" &&
|
||||
HASH5=$(git rev-parse --verify HEAD) &&
|
||||
echo "line 12" >> hello &&
|
||||
echo "line 13" >> hello &&
|
||||
add_and_commit_file hello "2 more lines" &&
|
||||
HASH6=$(git rev-parse --verify HEAD)
|
||||
echo "line 14" >> hello &&
|
||||
echo "line 15" >> hello &&
|
||||
echo "line 16" >> hello &&
|
||||
add_and_commit_file hello "again 3 more lines" &&
|
||||
HASH7=$(git rev-parse --verify HEAD)
|
||||
'
|
||||
|
||||
test_expect_success 'replace the author' '
|
||||
git cat-file commit $HASH2 | grep "author A U Thor" &&
|
||||
R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
|
||||
git cat-file commit $R | grep "author O Thor" &&
|
||||
git update-ref refs/replace/$HASH2 $R &&
|
||||
git show HEAD~5 | grep "O Thor" &&
|
||||
git show $HASH2 | grep "O Thor"
|
||||
'
|
||||
|
||||
cat >tag.sig <<EOF
|
||||
object $HASH2
|
||||
type commit
|
||||
tag mytag
|
||||
tagger T A Gger <> 0 +0000
|
||||
|
||||
EOF
|
||||
|
||||
test_expect_success 'tag replaced commit' '
|
||||
git mktag <tag.sig >.git/refs/tags/mytag 2>message
|
||||
'
|
||||
|
||||
test_expect_success '"git fsck" works' '
|
||||
git fsck master > fsck_master.out &&
|
||||
grep "dangling commit $R" fsck_master.out &&
|
||||
grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
|
||||
test -z "$(git fsck)"
|
||||
'
|
||||
|
||||
test_expect_success 'repack, clone and fetch work' '
|
||||
git repack -a -d &&
|
||||
git clone --no-hardlinks . clone_dir &&
|
||||
cd clone_dir &&
|
||||
git show HEAD~5 | grep "A U Thor" &&
|
||||
git show $HASH2 | grep "A U Thor" &&
|
||||
git cat-file commit $R &&
|
||||
git repack -a -d &&
|
||||
test_must_fail git cat-file commit $R &&
|
||||
git fetch ../ "refs/replace/*:refs/replace/*" &&
|
||||
git show HEAD~5 | grep "O Thor" &&
|
||||
git show $HASH2 | grep "O Thor" &&
|
||||
git cat-file commit $R &&
|
||||
cd ..
|
||||
'
|
||||
|
||||
test_expect_success '"git replace" listing and deleting' '
|
||||
test "$HASH2" = "$(git replace -l)" &&
|
||||
test "$HASH2" = "$(git replace)" &&
|
||||
aa=${HASH2%??????????????????????????????????????} &&
|
||||
test "$HASH2" = "$(git replace -l "$aa*")" &&
|
||||
test_must_fail git replace -d $R &&
|
||||
test_must_fail git replace -d &&
|
||||
test_must_fail git replace -l -d $HASH2 &&
|
||||
git replace -d $HASH2 &&
|
||||
git show $HASH2 | grep "A U Thor" &&
|
||||
test -z "$(git replace -l)"
|
||||
'
|
||||
|
||||
test_expect_success '"git replace" replacing' '
|
||||
git replace $HASH2 $R &&
|
||||
git show $HASH2 | grep "O Thor" &&
|
||||
test_must_fail git replace $HASH2 $R &&
|
||||
git replace -f $HASH2 $R &&
|
||||
test_must_fail git replace -f &&
|
||||
test "$HASH2" = "$(git replace)"
|
||||
'
|
||||
|
||||
# This creates a side branch where the bug in H2
|
||||
# does not appear because P2 is created by applying
|
||||
# H2 and squashing H5 into it.
|
||||
# P3, P4 and P6 are created by cherry-picking H3, H4
|
||||
# and H6 respectively.
|
||||
#
|
||||
# At this point, we should have the following:
|
||||
#
|
||||
# P2--P3--P4--P6
|
||||
# /
|
||||
# H1-H2-H3-H4-H5-H6-H7
|
||||
#
|
||||
# Then we replace H6 with P6.
|
||||
#
|
||||
test_expect_success 'create parallel branch without the bug' '
|
||||
git replace -d $HASH2 &&
|
||||
git show $HASH2 | grep "A U Thor" &&
|
||||
git checkout $HASH1 &&
|
||||
git cherry-pick $HASH2 &&
|
||||
git show $HASH5 | git apply &&
|
||||
git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
|
||||
PARA2=$(git rev-parse --verify HEAD) &&
|
||||
git cherry-pick $HASH3 &&
|
||||
PARA3=$(git rev-parse --verify HEAD) &&
|
||||
git cherry-pick $HASH4 &&
|
||||
PARA4=$(git rev-parse --verify HEAD) &&
|
||||
git cherry-pick $HASH6 &&
|
||||
PARA6=$(git rev-parse --verify HEAD) &&
|
||||
git replace $HASH6 $PARA6 &&
|
||||
git checkout master &&
|
||||
cur=$(git rev-parse --verify HEAD) &&
|
||||
test "$cur" = "$HASH7" &&
|
||||
git log --pretty=oneline | grep $PARA2 &&
|
||||
git remote add cloned ./clone_dir
|
||||
'
|
||||
|
||||
test_expect_success 'push to cloned repo' '
|
||||
git push cloned $HASH6^:refs/heads/parallel &&
|
||||
cd clone_dir &&
|
||||
git checkout parallel &&
|
||||
git log --pretty=oneline | grep $PARA2 &&
|
||||
cd ..
|
||||
'
|
||||
|
||||
test_expect_success 'push branch with replacement' '
|
||||
git cat-file commit $PARA3 | grep "author A U Thor" &&
|
||||
S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
|
||||
git cat-file commit $S | grep "author O Thor" &&
|
||||
git replace $PARA3 $S &&
|
||||
git show $HASH6~2 | grep "O Thor" &&
|
||||
git show $PARA3 | grep "O Thor" &&
|
||||
git push cloned $HASH6^:refs/heads/parallel2 &&
|
||||
cd clone_dir &&
|
||||
git checkout parallel2 &&
|
||||
git log --pretty=oneline | grep $PARA3 &&
|
||||
git show $PARA3 | grep "A U Thor" &&
|
||||
cd ..
|
||||
'
|
||||
|
||||
test_expect_success 'fetch branch with replacement' '
|
||||
git branch tofetch $HASH6 &&
|
||||
cd clone_dir &&
|
||||
git fetch origin refs/heads/tofetch:refs/heads/parallel3
|
||||
git log --pretty=oneline parallel3 | grep $PARA3
|
||||
git show $PARA3 | grep "A U Thor"
|
||||
cd ..
|
||||
'
|
||||
|
||||
#
|
||||
#
|
||||
test_done
|
||||
@@ -10,6 +10,10 @@ test_expect_success 'setup svnrepo' '
|
||||
mkdir project project/trunk project/branches project/tags &&
|
||||
echo foo > project/trunk/foo &&
|
||||
svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
|
||||
svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \
|
||||
"$svnrepo/pr ject/branches/b" &&
|
||||
svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
|
||||
"$svnrepo/pr ject/tags/v1" &&
|
||||
rm -rf project &&
|
||||
start_httpd
|
||||
'
|
||||
@@ -21,6 +25,54 @@ test_expect_success 'test clone with percent escapes' '
|
||||
cd ..
|
||||
'
|
||||
|
||||
# SVN works either way, so should we...
|
||||
|
||||
test_expect_success 'svn checkout with percent escapes' '
|
||||
svn_cmd checkout "$svnrepo/pr%20ject" svn.percent &&
|
||||
svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk
|
||||
'
|
||||
|
||||
test_expect_success 'svn checkout with space' '
|
||||
svn_cmd checkout "$svnrepo/pr ject" svn.space &&
|
||||
svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk
|
||||
'
|
||||
|
||||
test_expect_success 'test clone trunk with percent escapes and minimize-url' '
|
||||
git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize &&
|
||||
(
|
||||
cd minimize &&
|
||||
git rev-parse refs/${remotes_git_svn}
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'test clone trunk with percent escapes' '
|
||||
git svn clone "$svnrepo/pr%20ject/trunk" trunk &&
|
||||
(
|
||||
cd trunk &&
|
||||
git rev-parse refs/${remotes_git_svn}
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'test clone --stdlayout with percent escapes' '
|
||||
git svn clone --stdlayout "$svnrepo/pr%20ject" percent &&
|
||||
(
|
||||
cd percent &&
|
||||
git rev-parse refs/remotes/trunk^0 &&
|
||||
git rev-parse refs/remotes/b^0 &&
|
||||
git rev-parse refs/remotes/tags/v1^0
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'test clone -s with unescaped space' '
|
||||
git svn clone -s "$svnrepo/pr ject" space &&
|
||||
(
|
||||
cd space &&
|
||||
git rev-parse refs/remotes/trunk^0 &&
|
||||
git rev-parse refs/remotes/b^0 &&
|
||||
git rev-parse refs/remotes/tags/v1^0
|
||||
)
|
||||
'
|
||||
|
||||
stop_httpd
|
||||
|
||||
test_done
|
||||
|
||||
@@ -651,6 +651,7 @@ int main(int argc, char **argv)
|
||||
int strict = 0;
|
||||
|
||||
git_extract_argv0_path(argv[0]);
|
||||
read_replace_refs = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
char *arg = argv[i];
|
||||
|
||||
Reference in New Issue
Block a user