diff --git a/.mailmap b/.mailmap index 3a624eabc3..17e89af118 100644 --- a/.mailmap +++ b/.mailmap @@ -23,6 +23,7 @@ Lars Doelle Lars Doelle Lukas Sandström Martin Langhoff +Michele Ballabio Nguyễn Thái Ngọc Duy Ramsay Allan Jones René Scharfe diff --git a/Documentation/RelNotes-1.5.1.3.txt b/Documentation/RelNotes-1.5.1.3.txt new file mode 100644 index 0000000000..2ddeabd029 --- /dev/null +++ b/Documentation/RelNotes-1.5.1.3.txt @@ -0,0 +1,46 @@ +GIT v1.5.1.3 Release Notes +========================== + +Fixes since v1.5.1.2 +-------------------- + +* Bugfixes + + - git-add tried to optimize by finding common leading + directories across its arguments but botched, causing very + confused behaviour. + + - unofficial rpm.spec file shipped with git was letting + ETC_GITCONFIG set to /usr/etc/gitconfig. Tweak the official + Makefile to make it harder for distro people to make the + same mistake, by setting the variable to /etc/gitconfig if + prefix is set to /usr. + + - git-svn inconsistently stripped away username from the URL + only when svnsync_props was in use. + + - git-svn got confused when handling symlinks on Mac OS. + + - git-send-email was not quoting recipient names that have + period '.' in them. Also it did not allow overriding + envelope sender, which made it impossible to send patches to + certain subscriber-only lists. + + - built-in write_tree() routine had a sequence that renamed a + file that is still open, which some systems did not like. + + - when memory is very tight, sliding mmap code to read + packfiles incorrectly closed the fd that was still being + used to read the pack. + + - import-tars contributed front-end for fastimport was passing + wrong directory modes without checking. + + - git-fastimport trusted its input too much and allowed to + create corrupt tree objects with entries without a name. + + - git-fetch needlessly barfed when too long reflog action + description was given by the caller. + +Also contains various documentation updates. + diff --git a/Documentation/RelNotes-1.5.2.txt b/Documentation/RelNotes-1.5.2.txt index abecac6de9..712ebb0b78 100644 --- a/Documentation/RelNotes-1.5.2.txt +++ b/Documentation/RelNotes-1.5.2.txt @@ -26,8 +26,14 @@ Updates since v1.5.1 considered a binary or text (the former would be treated by 'git diff' not to produce textual output; the latter can go through the line endings conversion process in repositories - with core.autocrlf set), and specify a custom 3-way merge - driver. + with core.autocrlf set), expand and unexpand '$ident$' keyword + with blob object name, specify a custom 3-way merge driver, + and specify a custom diff driver. You can also apply + arbitrary filter to contents on check-in/check-out codepath + but this feature is an extremely sharp-edged razor and needs + to be handled with caution (do not use it unless you + understand the earlier mailing list discussion on keyward + expansion). * The packfile format now optionally suports 64-bit index. @@ -53,8 +59,21 @@ Updates since v1.5.1 commit -a" (i.e. update the index to match the working tree); it obviously does not make a commit. + - "git clean" honors a new configuration, "clean.requireforce". When + set to true, this makes "git clean" a no-op, preventing you + from losing files by typing "git clean" when you meant to + say "make clean". You can still say "git clean -f" to + override this. + + - "git log" family of commands learned --date={local,relative,default} + option. --date=relative is synonym to the --relative-date. + --date=local gives the timestamp in local timezone. + * Updated behavior of existing commands. + - When $GIT_COMMITTER_EMAIL or $GIT_AUTHOR_EMAIL is not set + but $EMAIL is set, the latter is used as a substitute. + - "git diff --stat" shows size of preimage and postimage blobs for binary contents. Earlier it only said "Bin". @@ -82,11 +101,17 @@ Updates since v1.5.1 - "gitview" (in contrib/ section) learned to better support "git-annotate". + - "git diff $commit1:$path2 $commit2:$path2" can now report + mode changes between the two blobs. + - Local "git fetch" from a repository whose object store is one of the alternates (e.g. fetching from the origin in a repository created with "git clone -l -s") avoids downloading objects unnecessary. + - "git blame" uses .mailmap to canonicalize the author name + just like "git shortlog" does. + * Builds - git-p4import has never been installed; now there is an @@ -102,11 +127,15 @@ Updates since v1.5.1 * Performance Tweaks - - optimized "git-rev-list --bisect" (hence "git-bisect"). + - Optimized "git-rev-list --bisect" (hence "git-bisect"). - - optimized "git-add $path" in a large directory, most of + - Optimized "git-add $path" in a large directory, most of whose contents are ignored. + - The recursive merge strategy updated a worktree file that + was changed identically in two branches, when one of them + renamed it. We do not do that when there is no rename, so + match that behaviour. Fixes since v1.5.1 ------------------ @@ -133,12 +162,15 @@ this release, unless otherwise noted. will not be backported to 1.5.1.x series, as it is rather an intrusive change. + - git-fetch had trouble with a remote with insanely large number + of refs. + * Documentation updates * Performance Tweaks -- exec >/var/tmp/1 -O=v1.5.1.2-242-g2d76548 +O=v1.5.2-rc0-106-g07c785d echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 2386f496ee..6a4da2ddd9 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -1,5 +1,7 @@ Checklist (and a short version for the impatient): + Commits: + - make commits of logical units - check for unnecessary whitespace with "git diff --check" before committing @@ -12,8 +14,14 @@ Checklist (and a short version for the impatient): commit message (or just use the option "-s" when committing) to confirm that you agree to the Developer's Certificate of Origin - - do not PGP sign your patch + + Patch: + - use "git format-patch -M" to create the patch + - send your patch to . 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 leave the formatting of the patch alone. diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index 331f161c77..a46bf6ce70 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -9,8 +9,28 @@ --show-stats:: Include additional statistics at the end of blame output. --L n,m:: - Annotate only the specified line range (lines count from 1). +-L ,:: + Annotate only the given line range. and can take + one of these forms: + + - number ++ +If or is a number, it specifies an +absolute line number (lines count from 1). ++ + +- /regex/ ++ +This form will use the first line matching the given +POSIX regex. If is a regex, it will search +starting at the line given by . ++ + +- +offset or -offset ++ +This is only valid for and will specify a number +of lines before or after the line given by . ++ -l:: Show long rev (Default: off). diff --git a/Documentation/config.txt b/Documentation/config.txt index e0aff5369f..c257cdf525 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -610,8 +610,8 @@ tar.umask:: user.email:: Your email address to be recorded in any newly created commits. - Can be overridden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL' - environment variables. See gitlink:git-commit-tree[1]. + Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and + 'EMAIL' environment variables. See gitlink:git-commit-tree[1]. user.name:: Your full name to be recorded in any newly created commits. diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 97cdb90cb4..6b9b9ad7d1 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -319,10 +319,9 @@ argument to `git-commit-tree`. `git-commit-tree` normally takes several arguments -- it wants to know what the 'parent' of a commit was, but since this is the first commit ever in this new repository, and it has no parents, we only need to pass in -the object name of the tree. However, `git-commit-tree` -also wants to get a commit message -on its standard input, and it will write out the resulting object name for the -commit to its standard output. +the object name of the tree. However, `git-commit-tree` also wants to get a +commit message on its standard input, and it will write out the resulting +object name for the commit to its standard output. And this is where we create the `.git/refs/heads/master` file which is pointed at by `HEAD`. This file is supposed to contain @@ -1304,7 +1303,7 @@ So, we can use somebody else's work from a remote repository, but how can *you* prepare a repository to let other people pull from it? -Your do your real work in your working tree that has your +You do your real work in your working tree that has your primary repository hanging under it as its `.git` subdirectory. You *could* make that repository accessible remotely and ask people to pull from it, but in practice that is not the way diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 5b4d184a73..bdc7332c7b 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -1,13 +1,20 @@ +-q, \--quiet:: + Pass --quiet to git-fetch-pack and silence any other internally + used programs. + +-v, \--verbose:: + Be verbose. + -a, \--append:: Append ref names and object names of fetched refs to the existing contents of `.git/FETCH_HEAD`. Without this option old data in `.git/FETCH_HEAD` will be overwritten. \--upload-pack :: - When given, and the repository to fetch from is handled - by 'git-fetch-pack', '--exec=' is passed to - the command to specify non-default path for the command - run on the other end. + When given, and the repository to fetch from is handled + by 'git-fetch-pack', '--exec=' is passed to + the command to specify non-default path for the command + run on the other end. -f, \--force:: When `git-fetch` is used with `:` @@ -16,7 +23,7 @@ fetches is a descendant of ``. This option overrides that check. -\--no-tags:: +-n, \--no-tags:: By default, `git-fetch` fetches tags that point at objects that are downloaded from the remote repository and stores them locally. This option disables this diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt index 8f9439a6dd..44678b0c36 100644 --- a/Documentation/git-blame.txt +++ b/Documentation/git-blame.txt @@ -8,7 +8,7 @@ git-blame - Show what revision and author last modified each line of a file SYNOPSIS -------- [verse] -'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] +'git-blame' [-c] [-b] [--root] [-s] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] [-S ] [-M] [-C] [-C] [--since=] [ | --contents ] [--] @@ -60,6 +60,9 @@ include::blame-options.txt[] -n, --show-number:: Show line number in the original commit (Default: off). +-s:: + Suppress author name and timestamp from the output. + THE PORCELAIN FORMAT -------------------- diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt index cf25507f8f..504a3aa1b4 100644 --- a/Documentation/git-commit-tree.txt +++ b/Documentation/git-commit-tree.txt @@ -60,6 +60,8 @@ either `.git/config` file, or using the following environment variables. GIT_AUTHOR_DATE GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL + GIT_COMMITTER_DATE + EMAIL (nb "<", ">" and "\n"s are stripped) diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index b78c4c64f1..2e1e29ef5a 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index SYNOPSIS -------- -'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|-n|--no-index] [] [...] +'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|--no-index] [] [...] DESCRIPTION ----------- @@ -36,7 +36,7 @@ omit diff output for unmerged entries and just show "Unmerged". diff, similar to the way 'diff-tree' shows a merge commit with these flags. -\-n,\--no-index:: +--no-index:: Compare the two given files / directories. -q:: diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt index a70eb3994a..e560b30c57 100644 --- a/Documentation/git-fmt-merge-msg.txt +++ b/Documentation/git-fmt-merge-msg.txt @@ -8,7 +8,8 @@ git-fmt-merge-msg - Produce a merge commit message SYNOPSIS -------- -'git-fmt-merge-msg' <$GIT_DIR/FETCH_HEAD +git-fmt-merge-msg [--summary | --no-summary] <$GIT_DIR/FETCH_HEAD +git-fmt-merge-msg [--summary | --no-summray] -F DESCRIPTION ----------- @@ -19,6 +20,28 @@ passed as the '' argument of `git-merge`. This script is intended mostly for internal use by scripts automatically invoking `git-merge`. +OPTIONS +------- + +--summary:: + In addition to branch names, populate the log message with + one-line descriptions from the actual commits that are being + merged. + +--no-summary:: + Do not list one-line descriptions from the actual commits being + merged. + +--file , -F :: + Take the list of merged objects from instead of + stdin. + +CONFIGURATION +------------- + +merge.summary:: + Whether to include summaries of merged commits in newly + merge commit messages. False by default. SEE ALSO -------- diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 0140c8e358..c5a5dad1ce 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -12,12 +12,13 @@ SYNOPSIS 'git-grep' [--cached] [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp] [-v | --invert-match] [-h|-H] [--full-name] - [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings] - [-n] [-l | --files-with-matches] [-L | --files-without-match] + [-E | --extended-regexp] [-G | --basic-regexp] + [-F | --fixed-strings] [-n] + [-l | --files-with-matches] [-L | --files-without-match] [-c | --count] [--all-match] [-A ] [-B ] [-C ] - [-f ] [-e] [--and|--or|--not|(|)|-e ...] - [...] + [-f ] [-e] + [--and|--or|--not|(|)|-e ...] [...] [--] [...] DESCRIPTION @@ -39,6 +40,9 @@ OPTIONS Ignore case differences between the patterns and the files. +-I:: + Don't match the pattern in binary files. + -w | --word-regexp:: Match the pattern only at word boundary (either begin at the beginning of a line, or preceded by a non-word character; end at @@ -64,6 +68,10 @@ OPTIONS Use POSIX extended/basic regexp for patterns. Default is to use basic regexp. +-F | --fixed-strings:: + Use fixed strings for patterns (don't interpret pattern + as a regex). + -n:: Prefix the line number to matching lines. @@ -81,6 +89,9 @@ OPTIONS line containing `--` between contiguous groups of matches. +-:: + A shortcut for specifying -C. + -f :: Read patterns from , one per line. diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt index 7dc2df3044..4deabc376c 100644 --- a/Documentation/git-http-fetch.txt +++ b/Documentation/git-http-fetch.txt @@ -39,6 +39,10 @@ commit-id:: ['\t'] +--recover:: + Verify that everything reachable from target is fetched. Used after + an earlier fetch is interrupted. + Author ------ Written by Linus Torvalds diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt index 4b4a46169c..a15cf5b2a3 100644 --- a/Documentation/git-http-push.txt +++ b/Documentation/git-http-push.txt @@ -8,7 +8,7 @@ git-http-push - Push objects over HTTP/DAV to another repository SYNOPSIS -------- -'git-http-push' [--complete] [--force] [--verbose] [...] +'git-http-push' [--all] [--force] [--verbose] [...] DESCRIPTION ----------- @@ -18,7 +18,7 @@ remote branch. OPTIONS ------- ---complete:: +--all:: Do not assume that the remote repository is complete in its current state, and verify all objects in the entire local ref's history exist in the remote repository. @@ -34,6 +34,15 @@ OPTIONS Report the list of objects being walked locally and the list of objects successfully sent to the remote repository. +-d, -D:: + Remove from remote repository. The specified branch + cannot be the remote HEAD. If -d is specified the following + other conditions must also be met: + + - Remote HEAD must resolve to an object that exists locally + - Specified branch resolves to an object that exists locally + - Specified branch is an ancestor of the remote HEAD + ...:: The remote refs to update. diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt index 22048d82bd..dd9e2387fc 100644 --- a/Documentation/git-local-fetch.txt +++ b/Documentation/git-local-fetch.txt @@ -24,6 +24,16 @@ OPTIONS 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 :: Writes the commit-id into the filename under $GIT_DIR/refs/ on @@ -35,6 +45,10 @@ OPTIONS ['\t'] +--recover:: + Verify that everything reachable from target is fetched. Used after + an earlier fetch is interrupted. + Author ------ Written by Junio C Hamano diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 77e068b15f..1b12b4f2a4 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -25,6 +25,7 @@ SYNOPSIS [ \--cherry-pick ] [ \--encoding[=] ] [ \--(author|committer|grep)= ] + [ \--date={local|relative|default} ] [ [\--objects | \--objects-edge] [ \--unpacked ] ] [ \--pretty | \--header ] [ \--bisect ] @@ -90,9 +91,20 @@ include::pretty-formats.txt[] --relative-date:: - Show dates relative to the current time, e.g. "2 hours ago". + Synonym for `--date=relative`. + +--date={relative,local,default}:: + Only takes effect for dates shown in human-readable format, such as when using "--pretty". ++ +`--date=relative` shows dates relative to the current time, +e.g. "2 hours ago". ++ +`--date=local` shows timestamps in user's local timezone. ++ +`--date=default` shows timestamps in the original timezone +(either committer's or author's). --header:: diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 682313e95d..7ae39fd5a2 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -54,7 +54,7 @@ The --cc option must be repeated for each user you want on the cc list. --in-reply-to:: Specify the contents of the first In-Reply-To header. - Subsequent emails will refer to the previous email + Subsequent emails will refer to the previous email instead of this if --chain-reply-to is set (the default) Only necessary if --compose is also set. If --compose is not set, this will be prompted for. @@ -68,8 +68,9 @@ The --cc option must be repeated for each user you want on the cc list. all that is output. --smtp-server:: - If set, specifies the outgoing SMTP server to use. A full - pathname of a sendmail-like program can be specified instead; + If set, specifies the outgoing SMTP server to use (e.g. + `smtp.example.com` or a raw IP address). Alternatively it can + specify a full pathname of a sendmail-like program instead; the program must support the `-i` option. Default value can be specified by the 'sendemail.smtpserver' configuration option; the built-in default is `/usr/sbin/sendmail` or @@ -85,6 +86,15 @@ The --cc option must be repeated for each user you want on the cc list. Do not add the From: address to the cc: list, if it shows up in a From: line. +--dry-run:: + Do everything except actually send the emails. + +--envelope-sender:: + Specify the envelope sender used to send the emails. + This is useful if your default address is not the address that is + subscribed to a list. If you use the sendmail binary, you must have + suitable privileges for the -f parameter. + --to:: Specify the primary recipient of the emails generated. Generally, this will be the upstream maintainer of the diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 1c8c55ef6e..15cc6f77c1 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s] -git-shortlog [-n|--number] [-s|--summary] [...] +git-shortlog [-n|--numbered] [-s|--summary] [...] DESCRIPTION ----------- @@ -22,14 +22,14 @@ Additionally, "[PATCH]" will be stripped from the commit description. OPTIONS ------- --h:: +-h, \--help:: Print a short usage message and exit. --n:: +-n, \--numbered:: Sort output according to the number of commits per author instead of author alphabetic order. --s:: +-s, \--summary:: Suppress commit description and provide a commit count summary only. FILES diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index a0d34e0058..62d7ef8be4 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -159,6 +159,12 @@ New features: Any other arguments are passed directly to `git log' -- +'find-rev':: + When given an SVN revision number of the form 'rN', returns the + corresponding git commit hash (this can optionally be followed by a + tree-ish to specify which branch should be searched). When given a + tree-ish, returns the corresponding SVN revision number. + 'set-tree':: You should consider using 'dcommit' instead of this command. Commit specified commit or tree objects to SVN. This relies on diff --git a/Documentation/git.txt b/Documentation/git.txt index ca1f78f790..b0550b8b1c 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -29,6 +29,10 @@ in a coherent way to git enlightenment ;-). The COMMAND is either a name of a Git command (see below) or an alias as defined in the configuration file (see gitlink:git-config[1]). +Formatted and hyperlinked version of the latest git +documentation can be viewed at +`http://www.kernel.org/pub/software/scm/git/docs/`. + ifdef::stalenotes[] [NOTE] ============ @@ -344,6 +348,8 @@ git Commits 'GIT_AUTHOR_DATE':: 'GIT_COMMITTER_NAME':: 'GIT_COMMITTER_EMAIL':: +'GIT_COMMITTER_DATE':: +'EMAIL':: see gitlink:git-commit-tree[1] git Diffs diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index d2edb9b14a..87723105d1 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -49,10 +49,12 @@ Set to a value:: Unspecified:: No glob pattern matches the path, and nothing says if - the path has or does not have the attribute. + the path has or does not have the attribute, the + attribute for the path is said to be Unspecified. When more than one glob pattern matches the path, a later line -overrides an earlier line. +overrides an earlier line. This overriding is done per +attribute. When deciding what attributes are assigned to a path, git consults `$GIT_DIR/info/attributes` file (which has the highest @@ -76,12 +78,17 @@ are attributes-aware. Checking-out and checking-in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The attribute `crlf` affects how the contents stored in the +These attributes affect how the contents stored in the repository are copied to the working tree files when commands -such as `git checkout` and `git merge` run. It also affects how +such as `git checkout` and `git merge` run. They also affect how git stores the contents you prepare in the working tree in the repository upon `git add` and `git commit`. +`crlf` +^^^^^^ + +This attribute controls the line-ending convention. + Set:: Setting the `crlf` attribute on a path is meant to mark @@ -127,6 +134,67 @@ converted to LF upon checkin, but there is no conversion done upon checkout. +`ident` +^^^^^^^ + +When the attribute `ident` is set to a path, git replaces +`$ident$` in the blob object with `$ident:`, followed by +40-character hexadecimal blob object name, followed by a dollar +sign `$` upon checkout. Any byte sequence that begins with +`$ident:` and ends with `$` in the worktree file is replaced +with `$ident$` upon check-in. + + +Interaction between checkin/checkout attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the check-in codepath, the worktree file is first converted +with `ident` (if specified), and then with `crlf` (again, if +specified and applicable). + +In the check-out codepath, the blob content is first converted +with `crlf`, and then `ident`. + + +`filter` +^^^^^^^^ + +A `filter` attribute can be set to a string value. This names +filter driver specified in the configuration. + +A filter driver consists of `clean` command and `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. + +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 +"turning something unusable into usable". In other words, it is +"hanging yourself because we gave you a long rope" if your +project uses filtering mechanism in such a way that it makes +your project unusable unless the checkout is done with a +specific filter in effect. + + +Interaction between checkin/checkout attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the check-in codepath, the worktree file is first converted +with `filter` driver (if specified and corresponding driver +defined), then the result is processed with `ident` (if +specified), and then finally with `crlf` (again, if specified +and applicable). + +In the check-out codepath, the blob content is first converted +with `crlf`, and then `ident` and fed to `filter`. + + Generating diff text ~~~~~~~~~~~~~~~~~~~~ @@ -215,7 +283,7 @@ String:: merge driver. The built-in 3-way merge driver can be explicitly specified by asking for "text" driver; the built-in "take the current branch" driver can be - requested by "binary". + requested with "binary". Defining a custom merge driver diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 41ee8b4ea2..0a6ea68dec 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.1.2.GIT +DEF_VER=v1.5.2-rc1.GIT LF=' ' diff --git a/Makefile b/Makefile index 51cda55aee..7772e7327f 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,12 @@ prefix = $(HOME) bindir = $(prefix)/bin gitexecdir = $(bindir) template_dir = $(prefix)/share/git-core/templates/ -ETC_GITCONFIG = $(prefix)/etc/gitconfig +ifeq ($(prefix),/usr) +sysconfdir = /etc +else +sysconfdir = $(prefix)/etc +endif +ETC_GITCONFIG = $(sysconfdir)/gitconfig # DESTDIR= # default configuration for gitweb @@ -160,7 +165,7 @@ GITWEB_FAVICON = git-favicon.png GITWEB_SITE_HEADER = GITWEB_SITE_FOOTER = -export prefix bindir gitexecdir template_dir +export prefix bindir gitexecdir template_dir sysconfdir CC = gcc AR = ar @@ -283,7 +288,7 @@ LIB_H = \ 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 \ spawn-pipe.h \ - utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h + utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h mailmap.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -306,7 +311,7 @@ LIB_OBJS = \ write_or_die.o trace.o list-objects.o grep.o match-trees.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 + convert.o attr.o decorate.o progress.o mailmap.o BUILTIN_OBJS = \ builtin-add.o \ @@ -953,6 +958,10 @@ endif ### Testing rules +TEST_PROGRAMS = test-chmtime$X test-genrandom$X + +all: $(TEST_PROGRAMS) + # GNU make supports exporting all variables by "export" without parameters. # However, the environment gets quite big, and some programs have problems # with that. @@ -960,7 +969,7 @@ endif export NO_SYMLINKS export NO_SVN_TESTS -test: all test-chmtime$X test-genrandom$X +test: all $(MAKE) -C t/ all test-date$X: test-date.c date.o ctype.o diff --git a/builtin-blame.c b/builtin-blame.c index 8919b028e6..3442d282aa 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -16,16 +16,20 @@ #include "quote.h" #include "xdiff-interface.h" #include "cache-tree.h" +#include "path-list.h" +#include "mailmap.h" static char blame_usage[] = -"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S ] [-M] [-C] [-C] [--contents ] [--incremental] [commit] [--] file\n" +"git-blame [-c] [-b] [-l] [--root] [-x] [-t] [-f] [-n] [-s] [-p] [-L n,m] [-S ] [-M] [-C] [-C] [--contents ] [--incremental] [commit] [--] file\n" " -c Use the same output mode as git-annotate (Default: off)\n" " -b Show blank SHA-1 for boundary commits (Default: off)\n" " -l Show long commit SHA1 (Default: off)\n" " --root Do not treat root commits as boundaries (Default: off)\n" " -t Show raw timestamp (Default: off)\n" +" -x Do not use .mailmap file\n" " -f, --show-name Show original filename (Default: auto)\n" " -n, --show-number Show original linenumber (Default: off)\n" +" -s Suppress author name and timestamp (Default: off)\n" " -p, --porcelain Show in a format designed for machine consumption\n" " -L n,m Process only line range n,m, counting from 1\n" " -M, -C Find line movements within and across files\n" @@ -42,6 +46,8 @@ static int show_root; static int blank_boundary; static int incremental; static int cmd_is_annotate; +static int no_mailmap; +static struct path_list mailmap; #ifndef DEBUG #define DEBUG 0 @@ -1264,8 +1270,8 @@ static void get_ac_line(const char *inbuf, const char *what, int bufsz, char *person, const char **mail, unsigned long *time, const char **tz) { - int len; - char *tmp, *endp; + int len, tzlen, maillen; + char *tmp, *endp, *timepos; tmp = strstr(inbuf, what); if (!tmp) @@ -1291,17 +1297,42 @@ static void get_ac_line(const char *inbuf, const char *what, while (*tmp != ' ') tmp--; *tz = tmp+1; + tzlen = (person+len)-(tmp+1); *tmp = 0; while (*tmp != ' ') tmp--; *time = strtoul(tmp, NULL, 10); + timepos = tmp; *tmp = 0; while (*tmp != ' ') tmp--; *mail = tmp + 1; *tmp = 0; + maillen = timepos - tmp; + + if (!mailmap.nr) + return; + + /* + * mailmap expansion may make the name longer. + * make room by pushing stuff down. + */ + tmp = person + bufsz - (tzlen + 1); + memmove(tmp, *tz, tzlen); + tmp[tzlen] = 0; + *tz = tmp; + + tmp = tmp - (maillen + 1); + memmove(tmp, *mail, maillen); + tmp[maillen] = 0; + *mail = tmp; + + /* + * Now, convert e-mail using mailmap + */ + map_email(&mailmap, tmp + 1, person, tmp-person-1); } static void get_commit_info(struct commit *commit, @@ -1483,6 +1514,7 @@ static const char *format_time(unsigned long time, const char *tz_str, #define OUTPUT_SHOW_NAME 020 #define OUTPUT_SHOW_NUMBER 040 #define OUTPUT_SHOW_SCORE 0100 +#define OUTPUT_NO_AUTHOR 0200 static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent) { @@ -1577,10 +1609,15 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) if (opt & OUTPUT_SHOW_NUMBER) printf(" %*d", max_orig_digits, ent->s_lno + 1 + cnt); - printf(" (%-*.*s %10s %*d) ", - longest_author, longest_author, ci.author, - format_time(ci.author_time, ci.author_tz, - show_raw_time), + + if (!(opt & OUTPUT_NO_AUTHOR)) + printf(" (%-*.*s %10s", + longest_author, longest_author, + ci.author, + format_time(ci.author_time, + ci.author_tz, + show_raw_time)); + printf(" %*d) ", max_digits, ent->lno + 1 + cnt); } do { @@ -2092,6 +2129,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) output_option |= OUTPUT_RAW_TIMESTAMP; else if (!strcmp("-l", arg)) output_option |= OUTPUT_LONG_OBJECT_NAME; + else if (!strcmp("-s", arg)) + output_option |= OUTPUT_NO_AUTHOR; else if (!strcmp("-S", arg) && ++i < argc) revs_file = argv[i]; else if (!prefixcmp(arg, "-M")) { @@ -2134,6 +2173,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) else if (!strcmp("-p", arg) || !strcmp("--porcelain", arg)) output_option |= OUTPUT_PORCELAIN; + else if (!strcmp("-x", arg) || + !strcmp("--no-mailmap", arg)) + no_mailmap = 1; else if (!strcmp("--", arg)) { seen_dashdash = 1; i++; @@ -2333,6 +2375,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) die("reading graft file %s failed: %s", revs_file, strerror(errno)); + if (!no_mailmap) + read_mailmap(&mailmap, ".mailmap", NULL); + assign_blame(&sb, &revs, opt); if (incremental) diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index 4a8d8d8b67..ccbcbe30da 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -16,9 +16,8 @@ */ static void init_buffer(char **bufp, unsigned int *sizep) { - char *buf = xmalloc(BLOCKING); + *bufp = xmalloc(BLOCKING); *sizep = 0; - *bufp = buf; } static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...) diff --git a/builtin-diff-files.c b/builtin-diff-files.c index 6ba5077a2b..6cb30c8e12 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -10,7 +10,7 @@ #include "builtin.h" static const char diff_files_usage[] = -"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|-n|--no-index] [] [...]" +"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|--no-index] [] [...]" COMMON_DIFF_OPTIONS_HELP; int cmd_diff_files(int argc, const char **argv, const char *prefix) diff --git a/builtin-diff.c b/builtin-diff.c index 2ae60097b8..7f367b6b9d 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -13,13 +13,10 @@ #include "log-tree.h" #include "builtin.h" -/* NEEDSWORK: struct object has place for name but we _do_ - * know mode when we extracted the blob out of a tree, which - * we currently lose. - */ struct blobinfo { unsigned char sha1[20]; const char *name; + unsigned mode; }; static const char builtin_diff_usage[] = @@ -35,7 +32,7 @@ static void stuff_change(struct diff_options *opt, struct diff_filespec *one, *two; if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) && - !hashcmp(old_sha1, new_sha1)) + !hashcmp(old_sha1, new_sha1) && (old_mode == new_mode)) return; if (opt->reverse_diff) { @@ -70,8 +67,12 @@ static int builtin_diff_b_f(struct rev_info *revs, die("'%s': %s", path, strerror(errno)); if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) die("'%s': not a regular file or symlink", path); + + if (blob[0].mode == S_IFINVALID) + blob[0].mode = canon_mode(st.st_mode); + stuff_change(&revs->diffopt, - canon_mode(st.st_mode), canon_mode(st.st_mode), + blob[0].mode, canon_mode(st.st_mode), blob[0].sha1, null_sha1, path, path); diffcore_std(&revs->diffopt); @@ -88,8 +89,14 @@ static int builtin_diff_blobs(struct rev_info *revs, if (argc > 1) usage(builtin_diff_usage); + if (blob[0].mode == S_IFINVALID) + blob[0].mode = mode; + + if (blob[1].mode == S_IFINVALID) + blob[1].mode = mode; + stuff_change(&revs->diffopt, - mode, mode, + blob[0].mode, blob[1].mode, blob[0].sha1, blob[1].sha1, blob[0].name, blob[1].name); diffcore_std(&revs->diffopt); @@ -272,6 +279,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) die("more than two blobs given: '%s'", name); hashcpy(blob[blobs].sha1, obj->sha1); blob[blobs].name = name; + blob[blobs].mode = list->mode; blobs++; continue; diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 3145c01f7e..2065466f27 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -35,16 +35,13 @@ static int update_ref(const char *action, unsigned char *sha1, unsigned char *oldval) { - int len; char msg[1024]; char *rla = getenv("GIT_REFLOG_ACTION"); static struct ref_lock *lock; if (!rla) rla = "(reflog update)"; - len = snprintf(msg, sizeof(msg), "%s: %s", rla, action); - if (sizeof(msg) <= len) - die("insanely long action"); + snprintf(msg, sizeof(msg), "%s: %s", rla, action); lock = lock_any_ref_for_update(refname, oldval); if (!lock) return 1; diff --git a/builtin-fsck.c b/builtin-fsck.c index fcb8ed5af1..44ce629a49 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -219,6 +219,7 @@ static int fsck_tree(struct tree *item) { int retval; int has_full_path = 0; + int has_empty_name = 0; int has_zero_pad = 0; int has_bad_modes = 0; int has_dup_entries = 0; @@ -242,6 +243,8 @@ static int fsck_tree(struct tree *item) if (strchr(name, '/')) has_full_path = 1; + if (!*name) + has_empty_name = 1; has_zero_pad |= *(char *)desc.buffer == '0'; update_tree_entry(&desc); @@ -291,6 +294,9 @@ static int fsck_tree(struct tree *item) if (has_full_path) { objwarning(&item->object, "contains full pathnames"); } + if (has_empty_name) { + objwarning(&item->object, "contains empty pathname"); + } if (has_zero_pad) { objwarning(&item->object, "contains zero-padded file modes"); } diff --git a/builtin-rev-list.c b/builtin-rev-list.c index c0329dcecd..ebf53f5944 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -95,7 +95,7 @@ static void show_commit(struct commit *commit) static char pretty_header[16384]; pretty_print_commit(revs.commit_format, commit, ~0, pretty_header, sizeof(pretty_header), - revs.abbrev, NULL, NULL, revs.relative_date); + revs.abbrev, NULL, NULL, revs.date_mode); printf("%s%c", pretty_header, hdr_termination); } fflush(stdout); diff --git a/builtin-shortlog.c b/builtin-shortlog.c index 3f93498bb7..8d3f742d43 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -5,6 +5,7 @@ #include "path-list.h" #include "revision.h" #include "utf8.h" +#include "mailmap.h" static const char shortlog_usage[] = "git-shortlog [-n] [-s] [... ]"; @@ -26,83 +27,6 @@ static int compare_by_number(const void *a1, const void *a2) static struct path_list mailmap = {NULL, 0, 0, 0}; -static int read_mailmap(const char *filename) -{ - char buffer[1024]; - FILE *f = fopen(filename, "r"); - - if (f == NULL) - return 1; - while (fgets(buffer, sizeof(buffer), f) != NULL) { - char *end_of_name, *left_bracket, *right_bracket; - char *name, *email; - int i; - if (buffer[0] == '#') { - static const char abbrev[] = "# repo-abbrev:"; - int abblen = sizeof(abbrev) - 1; - int len = strlen(buffer); - - if (len && buffer[len - 1] == '\n') - buffer[--len] = 0; - if (!strncmp(buffer, abbrev, abblen)) { - char *cp; - - if (common_repo_prefix) - free(common_repo_prefix); - common_repo_prefix = xmalloc(len); - - for (cp = buffer + abblen; isspace(*cp); cp++) - ; /* nothing */ - strcpy(common_repo_prefix, cp); - } - continue; - } - if ((left_bracket = strchr(buffer, '<')) == NULL) - continue; - if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL) - continue; - if (right_bracket == left_bracket + 1) - continue; - for (end_of_name = left_bracket; end_of_name != buffer - && isspace(end_of_name[-1]); end_of_name--) - /* keep on looking */ - if (end_of_name == buffer) - continue; - name = xmalloc(end_of_name - buffer + 1); - strlcpy(name, buffer, end_of_name - buffer + 1); - email = xmalloc(right_bracket - left_bracket); - for (i = 0; i < right_bracket - left_bracket - 1; i++) - email[i] = tolower(left_bracket[i + 1]); - email[right_bracket - left_bracket - 1] = '\0'; - path_list_insert(email, &mailmap)->util = name; - } - fclose(f); - return 0; -} - -static int map_email(char *email, char *name, int maxlen) -{ - char *p; - struct path_list_item *item; - - /* autocomplete common developers */ - p = strchr(email, '>'); - if (!p) - return 0; - - *p = '\0'; - /* downcase the email address */ - for (p = email; *p; p++) - *p = tolower(*p); - item = path_list_lookup(email, &mailmap); - if (item != NULL) { - const char *realname = (const char *)item->util; - strncpy(name, realname, maxlen); - return 1; - } - return 0; -} - static void insert_author_oneline(struct path_list *list, const char *author, int authorlen, const char *oneline, int onelinelen) @@ -184,7 +108,7 @@ static void read_from_stdin(struct path_list *list) (bob = strchr(buffer + 7, '<')) != NULL) { char buffer2[1024], offset = 0; - if (map_email(bob + 1, buffer, sizeof(buffer))) + if (map_email(&mailmap, bob + 1, buffer, sizeof(buffer))) bob = buffer + strlen(buffer); else { offset = 8; @@ -238,7 +162,7 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list) die("Invalid commit buffer: %s", sha1_to_hex(commit->object.sha1)); - if (map_email(bracket + 1, scratch, + if (map_email(&mailmap, bracket + 1, scratch, sizeof(scratch))) { author = scratch; authorlen = strlen(scratch); @@ -359,8 +283,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) if (argc > 1) die ("unrecognized argument: %s", argv[1]); - if (!access(".mailmap", R_OK)) - read_mailmap(".mailmap"); + read_mailmap(&mailmap, ".mailmap", &common_repo_prefix); if (rev.pending.nr == 0) { if (isatty(0)) diff --git a/builtin-write-tree.c b/builtin-write-tree.c index c88bbd1b9b..391de53972 100644 --- a/builtin-write-tree.c +++ b/builtin-write-tree.c @@ -36,8 +36,10 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix) die("git-write-tree: error building trees"); if (0 <= newfd) { if (!write_cache(newfd, active_cache, active_nr) - && !close(newfd)) + && !close(newfd)) { commit_lock_file(lock_file); + newfd = -1; + } } /* Not being able to write is fine -- we are only interested * in updating the cache-tree part, and if the next caller @@ -55,6 +57,8 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix) else hashcpy(sha1, active_cache_tree->sha1); + if (0 <= newfd) + close(newfd); rollback_lock_file(lock_file); return 0; diff --git a/cache.h b/cache.h index 8bb6fd882b..7e950f87b9 100644 --- a/cache.h +++ b/cache.h @@ -24,6 +24,9 @@ #define DTYPE(de) DT_UNKNOWN #endif +/* unknown mode (impossible combination S_IFIFO|S_IFCHR) */ +#define S_IFINVALID 0030000 + /* * A "directory link" is a link to another git directory. * @@ -143,9 +146,37 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7) -extern struct cache_entry **active_cache; -extern unsigned int active_nr, active_alloc, active_cache_changed; -extern struct cache_tree *active_cache_tree; +struct index_state { + struct cache_entry **cache; + unsigned int cache_nr, cache_alloc, cache_changed; + struct cache_tree *cache_tree; + time_t timestamp; + void *mmap; + size_t mmap_size; +}; + +extern struct index_state the_index; + +#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS +#define active_cache (the_index.cache) +#define active_nr (the_index.cache_nr) +#define active_alloc (the_index.cache_alloc) +#define active_cache_changed (the_index.cache_changed) +#define active_cache_tree (the_index.cache_tree) + +#define read_cache() read_index(&the_index) +#define read_cache_from(path) read_index_from(&the_index, (path)) +#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd)) +#define discard_cache() discard_index(&the_index) +#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen)) +#define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option)) +#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos)) +#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) +#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) +#define refresh_cache(flags) refresh_index(&the_index, flags) +#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really)) +#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really)) +#endif enum object_type { OBJ_BAD = -1, @@ -195,23 +226,23 @@ extern void verify_non_filename(const char *prefix, const char *name); #define alloc_nr(x) (((x)+16)*3/2) /* Initialize and use the cache information */ -extern int read_cache(void); -extern int read_cache_from(const char *path); -extern int write_cache(int newfd, struct cache_entry **cache, int entries); -extern int discard_cache(void); +extern int read_index(struct index_state *); +extern int read_index_from(struct index_state *, const char *path); +extern int write_index(struct index_state *, int newfd); +extern int discard_index(struct index_state *); extern int verify_path(const char *path); -extern int cache_name_pos(const char *name, int namelen); +extern int index_name_pos(struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ #define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */ -extern int add_cache_entry(struct cache_entry *ce, int option); +extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option); extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); -extern int remove_cache_entry_at(int pos); -extern int remove_file_from_cache(const char *path); -extern int add_file_to_cache(const char *path, int verbose); +extern int remove_index_entry_at(struct index_state *, int pos); +extern int remove_file_from_index(struct index_state *, const char *path); +extern int add_file_to_index(struct index_state *, const char *path, int verbose); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); -extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int); -extern int ce_modified(struct cache_entry *ce, struct stat *st, int); +extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int); +extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); extern int read_pipe(int fd, char** return_buf, unsigned long* return_size); @@ -223,7 +254,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ -extern int refresh_cache(unsigned int flags); +extern int refresh_index(struct index_state *, unsigned int flags); struct lock_file { struct lock_file *next; @@ -340,6 +371,7 @@ static inline unsigned int hexval(unsigned int c) #define DEFAULT_ABBREV 7 extern int get_sha1(const char *str, unsigned char *sha1); +extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode); extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern int read_ref(const char *filename, unsigned char *sha1); @@ -358,7 +390,7 @@ extern void *read_object_with_reference(const unsigned char *sha1, unsigned long *size, unsigned char *sha1_ret); -enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT }; +enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT, DATE_LOCAL }; const char *show_date(unsigned long time, int timezone, enum date_mode mode); const char *show_rfc2822_date(unsigned long time, int timezone); int parse_date(const char *date, char *buf, int bufsize); @@ -378,7 +410,7 @@ struct checkout { refresh_cache:1; }; -extern int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath); +extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); extern struct alternate_object_database { struct alternate_object_database *next; diff --git a/commit.c b/commit.c index 10466c4ae0..f1ba972d9a 100644 --- a/commit.c +++ b/commit.c @@ -526,7 +526,7 @@ static int add_rfc2047(char *buf, const char *line, int len, } static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, - const char *line, int relative_date, + const char *line, enum date_mode dmode, const char *encoding) { char *date; @@ -569,7 +569,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, switch (fmt) { case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, relative_date)); + show_date(time, tz, dmode)); break; case CMIT_FMT_EMAIL: ret += sprintf(buf + ret, "Date: %s\n", @@ -577,7 +577,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, - show_date(time, tz, relative_date)); + show_date(time, tz, dmode)); break; default: /* notin' */ @@ -919,7 +919,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, - int relative_date) + enum date_mode dmode) { int hdr = 1, body = 0, seen_title = 0; unsigned long offset = 0; @@ -1023,14 +1023,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, offset += add_user_info("Author", fmt, buf + offset, line + 7, - relative_date, + dmode, encoding); if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) offset += add_user_info("Commit", fmt, buf + offset, line + 10, - relative_date, + dmode, encoding); continue; } diff --git a/commit.h b/commit.h index 59de17eff8..86e8dca0c9 100644 --- a/commit.h +++ b/commit.h @@ -61,7 +61,7 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, int relative_date); +extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 5585a8b2c5..d2363a415d 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -52,6 +52,7 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; last unless $name; + next if $name =~ m{/\z}; $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; @@ -64,7 +65,12 @@ foreach my $tar_file (@ARGV) } print FI "\n"; - my $path = "$prefix$name"; + my $path; + if ($prefix) { + $path = "$prefix/$name"; + } else { + $path = "$name"; + } $files{$path} = [$next_mark++, $mode]; $commit_time = $mtime if $mtime > $commit_time; diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 65160153ee..d1bef9125b 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -302,7 +302,7 @@ generate_update_branch_email() # 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 - fastforward="" + fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) do @@ -327,36 +327,67 @@ generate_update_branch_email() if [ -z "$fastforward" ]; 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) certainly happened, (2) possibly. When (2) hasn't happened, + # we set a flag to indicate that no log printout is required. + echo "" - echo "This update added new revisions after undoing old revisions. That is to" - echo "say, the old revision is not a strict subset of the new revision. This" - echo "situation occurs when you --force push a change and generate a" - echo "repository containing something like this:" - echo "" - echo " * -- * -- B -- O -- O -- O ($oldrev)" - echo " \\" - echo " N -- N -- N ($newrev)" - echo "" - echo "When this happens we assume that you've already had alert emails for all" - echo "of the O revisions, and so we here report only the revisions in the N" - echo "branch from the common base, B." + + # 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 + echo "This update discarded existing revisions and left the branch pointing at" + echo "a previous point in the repository history." + echo "" + echo " * -- * -- N ($newrev)" + echo " \\" + echo " O -- O -- O ($oldrev)" + echo "" + echo "The removed revisions are not necessarilly gone - if another reference" + echo "still refers to them they will stay in the repository." + rewind_only=1 + else + echo "This update added new revisions after undoing existing revisions. That is" + echo "to say, the old revision is not a strict subset of the new revision. This" + echo "situation occurs when you --force push a change and generate a repository" + echo "containing something like this:" + echo "" + echo " * -- * -- B -- O -- O -- O ($oldrev)" + echo " \\" + echo " N -- N -- N ($newrev)" + echo "" + echo "When this happens we assume that you've already had alert emails for all" + echo "of the O revisions, and so we here report only the revisions in the N" + echo "branch from the common base, B." + fi fi echo "" - echo "Those revisions listed above that are new to this repository have" - echo "not appeared on any other notification email; so we list those" - echo "revisions in full, below." + if [ -z "$rewind_only" ]; then + echo "Those revisions listed above that are new to this repository have" + echo "not appeared on any other notification email; so we list those" + echo "revisions in full, below." - echo "" - echo $LOGBEGIN - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $oldrev..$newrev + echo "" + echo $LOGBEGIN + 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 + 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 @@ -556,7 +587,7 @@ if [ -z "$GIT_DIR" ]; then exit 1 fi -projectdesc=$(sed -e '1p' "$GIT_DIR/description") +projectdesc=$(sed -ne '1p' "$GIT_DIR/description") # Check if the description is unchanged from it's default, and shorten it to a # more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null diff --git a/convert.c b/convert.c index ad106ef35f..9ee31b0ee0 100644 --- a/convert.c +++ b/convert.c @@ -1,5 +1,6 @@ #include "cache.h" #include "attr.h" +#include "run-command.h" /* * convert.c - convert a file when checking it out and checking it in. @@ -200,13 +201,350 @@ static char *crlf_to_worktree(const char *path, const char *src, unsigned long * return buffer; } +static int filter_buffer(const char *path, const char *src, + unsigned long size, const char *cmd) +{ + /* + * Spawn cmd and feed the buffer contents through its stdin. + */ + struct child_process child_process; + int pipe_feed[2]; + int write_err, status; + + memset(&child_process, 0, sizeof(child_process)); + + if (pipe(pipe_feed) < 0) { + error("cannot create pipe to run external filter %s", cmd); + return 1; + } + + child_process.pid = fork(); + if (child_process.pid < 0) { + error("cannot fork to run external filter %s", cmd); + close(pipe_feed[0]); + close(pipe_feed[1]); + return 1; + } + if (!child_process.pid) { + dup2(pipe_feed[0], 0); + close(pipe_feed[0]); + close(pipe_feed[1]); + execlp("sh", "sh", "-c", cmd, NULL); + return 1; + } + close(pipe_feed[0]); + + write_err = (write_in_full(pipe_feed[1], src, size) < 0); + if (close(pipe_feed[1])) + write_err = 1; + if (write_err) + error("cannot feed the input to external filter %s", cmd); + + status = finish_command(&child_process); + if (status) + error("external filter %s failed %d", cmd, -status); + return (write_err || status); +} + +static char *apply_filter(const char *path, const char *src, + unsigned long *sizep, const char *cmd) +{ + /* + * Create a pipeline to have the command filter the buffer's + * contents. + * + * (child --> cmd) --> us + */ + const int SLOP = 4096; + int pipe_feed[2]; + int status; + char *dst; + unsigned long dstsize, dstalloc; + struct child_process child_process; + + if (!cmd) + return NULL; + + memset(&child_process, 0, sizeof(child_process)); + + if (pipe(pipe_feed) < 0) { + error("cannot create pipe to run external filter %s", cmd); + return NULL; + } + + fflush(NULL); + child_process.pid = fork(); + if (child_process.pid < 0) { + error("cannot fork to run external filter %s", cmd); + close(pipe_feed[0]); + close(pipe_feed[1]); + return NULL; + } + if (!child_process.pid) { + dup2(pipe_feed[1], 1); + close(pipe_feed[0]); + close(pipe_feed[1]); + exit(filter_buffer(path, src, *sizep, cmd)); + } + close(pipe_feed[1]); + + dstalloc = *sizep; + dst = xmalloc(dstalloc); + dstsize = 0; + + while (1) { + ssize_t numread = xread(pipe_feed[0], dst + dstsize, + dstalloc - dstsize); + + if (numread <= 0) { + if (!numread) + break; + error("read from external filter %s failed", cmd); + free(dst); + dst = NULL; + break; + } + dstsize += numread; + if (dstalloc <= dstsize + SLOP) { + dstalloc = dstsize + SLOP; + dst = xrealloc(dst, dstalloc); + } + } + if (close(pipe_feed[0])) { + error("read from external filter %s failed", cmd); + free(dst); + dst = NULL; + } + + status = finish_command(&child_process); + if (status) { + error("external filter %s failed %d", cmd, -status); + free(dst); + dst = NULL; + } + + if (dst) + *sizep = dstsize; + return dst; +} + +static struct convert_driver { + const char *name; + struct convert_driver *next; + char *smudge; + char *clean; +} *user_convert, **user_convert_tail; + +static int read_convert_config(const char *var, const char *value) +{ + const char *ep, *name; + int namelen; + struct convert_driver *drv; + + /* + * External conversion drivers are configured using + * "filter..variable". + */ + if (prefixcmp(var, "filter.") || (ep = strrchr(var, '.')) == var + 6) + return 0; + name = var + 7; + namelen = ep - name; + for (drv = user_convert; drv; drv = drv->next) + if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) + break; + if (!drv) { + char *namebuf; + drv = xcalloc(1, sizeof(struct convert_driver)); + namebuf = xmalloc(namelen + 1); + memcpy(namebuf, name, namelen); + namebuf[namelen] = 0; + drv->name = namebuf; + drv->next = NULL; + *user_convert_tail = drv; + user_convert_tail = &(drv->next); + } + + ep++; + + /* + * filter..smudge and filter..clean specifies + * the command line: + * + * command-line + * + * The command-line will not be interpolated in any way. + */ + + if (!strcmp("smudge", ep)) { + if (!value) + return error("%s: lacks value", var); + drv->smudge = strdup(value); + return 0; + } + + if (!strcmp("clean", ep)) { + if (!value) + return error("%s: lacks value", var); + drv->clean = strdup(value); + return 0; + } + return 0; +} + static void setup_convert_check(struct git_attr_check *check) { static struct git_attr *attr_crlf; + static struct git_attr *attr_ident; + static struct git_attr *attr_filter; - if (!attr_crlf) + if (!attr_crlf) { attr_crlf = git_attr("crlf", 4); - check->attr = attr_crlf; + attr_ident = git_attr("ident", 5); + attr_filter = git_attr("filter", 6); + user_convert_tail = &user_convert; + git_config(read_convert_config); + } + check[0].attr = attr_crlf; + check[1].attr = attr_ident; + check[2].attr = attr_filter; +} + +static int count_ident(const char *cp, unsigned long size) +{ + /* + * "$ident: 0000000000000000000000000000000000000000 $" <=> "$ident$" + */ + int cnt = 0; + char ch; + + while (size) { + ch = *cp++; + size--; + if (ch != '$') + continue; + if (size < 6) + break; + if (memcmp("ident", cp, 5)) + continue; + ch = cp[5]; + cp += 6; + size -= 6; + if (ch == '$') + cnt++; /* $ident$ */ + if (ch != ':') + continue; + + /* + * "$ident: ... "; scan up to the closing dollar sign and discard. + */ + while (size) { + ch = *cp++; + size--; + if (ch == '$') { + cnt++; + break; + } + } + } + return cnt; +} + +static char *ident_to_git(const char *path, const char *src, unsigned long *sizep, int ident) +{ + int cnt; + unsigned long size; + char *dst, *buf; + + if (!ident) + return NULL; + size = *sizep; + cnt = count_ident(src, size); + if (!cnt) + return NULL; + buf = xmalloc(size); + + for (dst = buf; size; size--) { + char ch = *src++; + *dst++ = ch; + if ((ch == '$') && (6 <= size) && + !memcmp("ident:", src, 6)) { + unsigned long rem = size - 6; + const char *cp = src + 6; + do { + ch = *cp++; + if (ch == '$') + break; + rem--; + } while (rem); + if (!rem) + continue; + memcpy(dst, "ident$", 6); + dst += 6; + size -= (cp - src); + src = cp; + } + } + + *sizep = dst - buf; + return buf; +} + +static char *ident_to_worktree(const char *path, const char *src, unsigned long *sizep, int ident) +{ + int cnt; + unsigned long size; + char *dst, *buf; + unsigned char sha1[20]; + + if (!ident) + return NULL; + + size = *sizep; + cnt = count_ident(src, size); + if (!cnt) + return NULL; + + hash_sha1_file(src, size, "blob", sha1); + buf = xmalloc(size + cnt * 43); + + for (dst = buf; size; size--) { + const char *cp; + char ch = *src++; + *dst++ = ch; + if ((ch != '$') || (size < 6) || memcmp("ident", src, 5)) + continue; + + if (src[5] == ':') { + /* discard up to but not including the closing $ */ + unsigned long rem = size - 6; + cp = src + 6; + do { + ch = *cp++; + if (ch == '$') + break; + rem--; + } while (rem); + if (!rem) + continue; + size -= (cp - src); + } else if (src[5] == '$') + cp = src + 5; + else + continue; + + memcpy(dst, "ident: ", 7); + dst += 7; + memcpy(dst, sha1_to_hex(sha1), 40); + dst += 40; + *dst++ = ' '; + size -= (cp - src); + src = cp; + *dst++ = *src++; + size--; + } + + *sizep = dst - buf; + return buf; } static int git_path_check_crlf(const char *path, struct git_attr_check *check) @@ -224,26 +562,93 @@ static int git_path_check_crlf(const char *path, struct git_attr_check *check) return CRLF_GUESS; } +static struct convert_driver *git_path_check_convert(const char *path, + struct git_attr_check *check) +{ + const char *value = check->value; + struct convert_driver *drv; + + if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value)) + return NULL; + for (drv = user_convert; drv; drv = drv->next) + if (!strcmp(value, drv->name)) + return drv; + return NULL; +} + +static int git_path_check_ident(const char *path, struct git_attr_check *check) +{ + const char *value = check->value; + + return !!ATTR_TRUE(value); +} + char *convert_to_git(const char *path, const char *src, unsigned long *sizep) { - struct git_attr_check check[1]; + struct git_attr_check check[3]; int crlf = CRLF_GUESS; + int ident = 0; + char *filter = NULL; + char *buf, *buf2; setup_convert_check(check); - if (!git_checkattr(path, 1, check)) { - crlf = git_path_check_crlf(path, check); + if (!git_checkattr(path, ARRAY_SIZE(check), check)) { + struct convert_driver *drv; + crlf = git_path_check_crlf(path, check + 0); + ident = git_path_check_ident(path, check + 1); + drv = git_path_check_convert(path, check + 2); + if (drv && drv->clean) + filter = drv->clean; } - return crlf_to_git(path, src, sizep, crlf); + + buf = apply_filter(path, src, sizep, filter); + + buf2 = crlf_to_git(path, buf ? buf : src, sizep, crlf); + if (buf2) { + free(buf); + buf = buf2; + } + + buf2 = ident_to_git(path, buf ? buf : src, sizep, ident); + if (buf2) { + free(buf); + buf = buf2; + } + + return buf; } char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep) { - struct git_attr_check check[1]; + struct git_attr_check check[3]; int crlf = CRLF_GUESS; + int ident = 0; + char *filter = NULL; + char *buf, *buf2; setup_convert_check(check); - if (!git_checkattr(path, 1, check)) { - crlf = git_path_check_crlf(path, check); + if (!git_checkattr(path, ARRAY_SIZE(check), check)) { + struct convert_driver *drv; + crlf = git_path_check_crlf(path, check + 0); + ident = git_path_check_ident(path, check + 1); + drv = git_path_check_convert(path, check + 2); + if (drv && drv->smudge) + filter = drv->smudge; } - return crlf_to_worktree(path, src, sizep, crlf); + + buf = ident_to_worktree(path, src, sizep, ident); + + buf2 = crlf_to_worktree(path, buf ? buf : src, sizep, crlf); + if (buf2) { + free(buf); + buf = buf2; + } + + buf2 = apply_filter(path, buf ? buf : src, sizep, filter); + if (buf2) { + free(buf); + buf = buf2; + } + + return buf; } diff --git a/date.c b/date.c index be4ae18428..93c4eaf26d 100644 --- a/date.c +++ b/date.c @@ -55,6 +55,32 @@ static struct tm *time_to_tm(unsigned long time, int tz) return gmtime(&t); } +/* + * What value of "tz" was in effect back then at "time" in the + * local timezone? + */ +static int local_tzoffset(unsigned long time) +{ + time_t t, t_local; + struct tm tm; + int offset, eastwest; + + t = time; + localtime_r(&t, &tm); + t_local = my_mktime(&tm); + + if (t_local < t) { + eastwest = -1; + offset = t - t_local; + } else { + eastwest = 1; + offset = t_local - t; + } + offset /= 60; /* in minutes */ + offset = (offset % 60) + ((offset / 60) * 100); + return offset * eastwest; +} + const char *show_date(unsigned long time, int tz, enum date_mode mode) { struct tm *tm; @@ -102,6 +128,9 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode) /* Else fall back on absolute format.. */ } + if (mode == DATE_LOCAL) + tz = local_tzoffset(time); + tm = time_to_tm(time, tz); if (!tm) return NULL; @@ -109,12 +138,14 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode) sprintf(timebuf, "%04d-%02d-%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); else - sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d", + sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", weekday_names[tm->tm_wday], month_names[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_year + 1900, tz); + tm->tm_year + 1900, + (mode == DATE_LOCAL) ? 0 : ' ', + tz); return timebuf; } diff --git a/diffcore.h b/diffcore.h index 1ea80671e3..7b9294eab2 100644 --- a/diffcore.h +++ b/diffcore.h @@ -1,8 +1,8 @@ /* * Copyright (C) 2005 Junio C Hamano */ -#ifndef _DIFFCORE_H_ -#define _DIFFCORE_H_ +#ifndef DIFFCORE_H +#define DIFFCORE_H /* This header file is internal between diff.c and its diff transformers * (e.g. diffcore-rename, diffcore-pickaxe). Never include this header diff --git a/entry.c b/entry.c index 84f78025ff..82bf7259a7 100644 --- a/entry.c +++ b/entry.c @@ -1,7 +1,7 @@ #include "cache.h" #include "blob.h" -static void create_directories(const char *path, struct checkout *state) +static void create_directories(const char *path, const struct checkout *state) { int len = strlen(path); char *buf = xmalloc(len + 1); @@ -33,7 +33,7 @@ static void remove_subtree(const char *path) char *name; if (!dir) - die("cannot opendir %s", path); + die("cannot opendir %s (%s)", path, strerror(errno)); strcpy(pathbuf, path); name = pathbuf + strlen(path); *name++ = '/'; @@ -45,15 +45,15 @@ static void remove_subtree(const char *path) continue; strcpy(name, de->d_name); if (lstat(pathbuf, &st)) - die("cannot lstat %s", pathbuf); + die("cannot lstat %s (%s)", pathbuf, strerror(errno)); if (S_ISDIR(st.st_mode)) remove_subtree(pathbuf); else if (unlink(pathbuf)) - die("cannot unlink %s", pathbuf); + die("cannot unlink %s (%s)", pathbuf, strerror(errno)); } closedir(dir); if (rmdir(path)) - die("cannot rmdir %s", path); + die("cannot rmdir %s (%s)", path, strerror(errno)); } static int create_file(const char *path, unsigned int mode) @@ -75,7 +75,7 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned return NULL; } -static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile) +static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { int fd; long wrote; @@ -163,7 +163,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat return 0; } -int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath) +int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) { static char path[PATH_MAX + 1]; struct stat st; diff --git a/exec_cmd.h b/exec_cmd.h index eefb4afe40..58e2eb4096 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -1,5 +1,5 @@ -#ifndef __GIT_EXEC_CMD_H_ -#define __GIT_EXEC_CMD_H_ +#ifndef GIT_EXEC_CMD_H +#define GIT_EXEC_CMD_H extern void git_set_exec_path(const char *exec_path); extern const char* git_exec_path(void); @@ -8,4 +8,4 @@ extern int execl_git_cmd(const char *cmd, ...); extern int spawnv_git_cmd(const char **argv, int pin[2], int pout[2]); -#endif /* __GIT_EXEC_CMD_H_ */ +#endif /* GIT_EXEC_CMD_H */ diff --git a/fast-import.c b/fast-import.c index cdd629d6bc..b4cbcd9011 100644 --- a/fast-import.c +++ b/fast-import.c @@ -673,7 +673,7 @@ static void fixup_header_footer(void) buf = xmalloc(buf_sz); for (;;) { - size_t n = xread(pack_fd, buf, buf_sz); + ssize_t n = xread(pack_fd, buf, buf_sz); if (!n) break; if (n < 0) @@ -904,6 +904,12 @@ static int store_object( if (e->offset) { duplicate_count_by_type[type]++; return 1; + } else if (find_sha1_pack(sha1, packed_git)) { + e->type = type; + e->pack_id = MAX_PACK_ID; + e->offset = 1; /* just not zero! */ + duplicate_count_by_type[type]++; + return 1; } if (last && last->data && last->depth < max_depth) { @@ -1193,6 +1199,8 @@ static int tree_content_set( n = slash1 - p; else n = strlen(p); + if (!n) + die("Empty path component found in input"); for (i = 0; i < t->entry_count; i++) { e = t->entries[i]; @@ -2021,6 +2029,7 @@ static void import_marks(const char *input_file) e = insert_object(sha1); e->type = type; e->pack_id = MAX_PACK_ID; + e->offset = 1; /* just not zero! */ } insert_mark(mark, e); } @@ -2086,6 +2095,7 @@ int main(int argc, const char **argv) if (i != argc) usage(fast_import_usage); + prepare_packed_git(); start_packfile(); for (;;) { read_next_command(); diff --git a/git-applymbox.sh b/git-applymbox.sh index 3efd6a7464..c18e80ff8c 100755 --- a/git-applymbox.sh +++ b/git-applymbox.sh @@ -78,7 +78,7 @@ do git-mailinfo $keep_subject $utf8 \ .dotest/msg .dotest/patch <$i >.dotest/info || exit 1 test -s .dotest/patch || { - echo "Patch is empty. Was is split wrong?" + echo "Patch is empty. Was it split wrong?" exit 1 } git-stripspace < .dotest/msg > .dotest/msg-clean diff --git a/git-compat-util.h b/git-compat-util.h index a02010d633..56a8eb1971 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -171,13 +171,13 @@ extern size_t gitstrlcpy(char *, const char *, size_t); extern uintmax_t gitstrtoumax(const char *, char **, int); #endif -extern void release_pack_memory(size_t); +extern void release_pack_memory(size_t, int); static inline char* xstrdup(const char *str) { char *ret = strdup(str); if (!ret) { - release_pack_memory(strlen(str) + 1); + release_pack_memory(strlen(str) + 1, -1); ret = strdup(str); if (!ret) die("Out of memory, strdup failed"); @@ -191,7 +191,7 @@ static inline void *xmalloc(size_t size) if (!ret && !size) ret = malloc(1); if (!ret) { - release_pack_memory(size); + release_pack_memory(size, -1); ret = malloc(size); if (!ret && !size) ret = malloc(1); @@ -210,7 +210,7 @@ static inline void *xrealloc(void *ptr, size_t size) if (!ret && !size) ret = realloc(ptr, 1); if (!ret) { - release_pack_memory(size); + release_pack_memory(size, -1); ret = realloc(ptr, size); if (!ret && !size) ret = realloc(ptr, 1); @@ -226,7 +226,7 @@ static inline void *xcalloc(size_t nmemb, size_t size) if (!ret && (!nmemb || !size)) ret = calloc(1, 1); if (!ret) { - release_pack_memory(nmemb * size); + release_pack_memory(nmemb * size, -1); ret = calloc(nmemb, size); if (!ret && (!nmemb || !size)) ret = calloc(1, 1); @@ -243,7 +243,7 @@ static inline void *xmmap(void *start, size_t length, if (ret == MAP_FAILED) { if (!length) return NULL; - release_pack_memory(length); + release_pack_memory(length, fd); ret = mmap(start, length, prot, flags, fd, offset); if (ret == MAP_FAILED) die("Out of memory? mmap failed: %s", strerror(errno)); diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 018cc75bd0..a7a6757dd8 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -74,7 +74,7 @@ for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do echo $patch_name (cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3 test -s .dotest/patch || { - echo "Patch is empty. Was is split wrong?" + echo "Patch is empty. Was it split wrong?" exit 1 } diff --git a/git-send-email.perl b/git-send-email.perl index d6b15480dc..a6e3e02619 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -77,6 +77,10 @@ Options: --quiet Make git-send-email less verbose. One line per email should be all that is output. + --dry-run Do everything except actually send the emails. + + --envelope-sender Specify the envelope sender used to send the emails. + EOT exit(1); } @@ -137,6 +141,7 @@ my (@to,@cc,@initial_cc,@bcclist,@xh, my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc, $dry_run) = (1, 0, 0, 0, 0); my $smtp_server; +my $envelope_sender; # Example reply to: #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; @@ -175,6 +180,7 @@ my $rc = GetOptions("from=s" => \$from, "suppress-from" => \$suppress_from, "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc, "dry-run" => \$dry_run, + "envelope-sender=s" => \$envelope_sender, ); unless ($rc) { @@ -268,6 +274,7 @@ sub expand_aliases { } @to = expand_aliases(@to); +@to = (map { sanitize_address_rfc822($_) } @to); @initial_cc = expand_aliases(@initial_cc); @bcclist = expand_aliases(@bcclist); @@ -377,7 +384,7 @@ if (@files) { } # Variables we set as part of the loop over files -our ($message_id, $cc, %mail, $subject, $reply_to, $references, $message); +our ($message_id, %mail, $subject, $reply_to, $references, $message); sub extract_valid_address { my $address = shift; @@ -418,7 +425,6 @@ sub make_message_id -$cc = ""; $time = time - scalar $#files; sub unquote_rfc2047 { @@ -430,26 +436,37 @@ sub unquote_rfc2047 { return "$_"; } +# If an address contains a . in the name portion, the name must be quoted. +sub sanitize_address_rfc822 +{ + my ($recipient) = @_; + my ($recipient_name) = ($recipient =~ /^(.*?)\s+new( $smtp_server ); - $smtp->mail( $from ) or die $smtp->message; + $smtp->mail( $raw_from ) or die $smtp->message; $smtp->to( @recipients ) or die $smtp->message; $smtp->data or die $smtp->message; $smtp->datasend("$header\n$message") or die $smtp->message; @@ -489,13 +511,15 @@ X-Mailer: git-send-email $gitversion $smtp->ok or die "Failed to send $subject\n".$smtp->message; } if ($quiet) { - printf "Sent %s\n", $subject; + printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject); } else { - print "OK. Log says:\nDate: $date\n"; - if ($smtp) { + print (($dry_run ? "Dry-" : "")."OK. Log says:\nDate: $date\n"); + if ($smtp_server !~ m#^/#) { print "Server: $smtp_server\n"; + print "MAIL FROM:<$raw_from>\n"; + print "RCPT TO:".join(',',(map { "<$_>" } @recipients))."\n"; } else { - print "Sendmail: $smtp_server\n"; + print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n"; } print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n"; if ($smtp) { @@ -590,7 +614,6 @@ foreach my $t (@files) { $message = "From: $author_not_sender\n\n$message"; } - $cc = join(", ", unique_email_list(@cc)); send_message(); diff --git a/git-svn.perl b/git-svn.perl index efc4c88a4e..6657e100fb 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -141,6 +141,8 @@ my %cmd = ( 'color' => \$Git::SVN::Log::color, 'pager=s' => \$Git::SVN::Log::pager, } ], + 'find-rev' => [ \&cmd_find_rev, "Translate between SVN revision numbers and tree-ish", + { } ], 'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory", { 'merge|m|M' => \$_merge, 'verbose|v' => \$_verbose, @@ -428,6 +430,27 @@ sub cmd_dcommit { command_noisy(@finish, $gs->refname); } +sub cmd_find_rev { + my $revision_or_hash = shift; + my $result; + if ($revision_or_hash =~ /^r\d+$/) { + my $head = shift; + $head ||= 'HEAD'; + my @refs; + my (undef, undef, undef, $gs) = working_head_info($head, \@refs); + unless ($gs) { + die "Unable to determine upstream SVN information from ", + "$head history\n"; + } + my $desired_revision = substr($revision_or_hash, 1); + $result = $gs->rev_db_get($desired_revision); + } else { + my (undef, $rev, undef) = cmt_metadata($revision_or_hash); + $result = $rev; + } + print "$result\n" if $result; +} + sub cmd_rebase { command_noisy(qw/update-index --refresh/); my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); @@ -771,19 +794,19 @@ sub cmt_metadata { sub working_head_info { my ($head, $refs) = @_; my ($fh, $ctx) = command_output_pipe('rev-list', $head); - while (<$fh>) { - chomp; - my ($url, $rev, $uuid) = cmt_metadata($_); + while (my $hash = <$fh>) { + chomp($hash); + my ($url, $rev, $uuid) = cmt_metadata($hash); if (defined $url && defined $rev) { if (my $gs = Git::SVN->find_by_url($url)) { my $c = $gs->rev_db_get($rev); - if ($c && $c eq $_) { + if ($c && $c eq $hash) { close $fh; # break the pipe return ($url, $rev, $uuid, $gs); } } } - unshift @$refs, $_ if $refs; + unshift @$refs, $hash if $refs; } command_close_pipe($fh, $ctx); (undef, undef, undef, undef); @@ -1064,7 +1087,10 @@ sub init_remote_config { sub find_by_url { # repos_root and, path are optional my ($class, $full_url, $repos_root, $path) = @_; + return undef unless defined $full_url; + remove_username($full_url); + remove_username($repos_root) if defined $repos_root; my $remotes = read_all_remotes(); if (defined $full_url && defined $repos_root && !defined $path) { $path = $full_url; @@ -1072,6 +1098,7 @@ sub find_by_url { # repos_root and, path are optional } foreach my $repo_id (keys %$remotes) { my $u = $remotes->{$repo_id}->{url} or next; + remove_username($u); next if defined $repos_root && $repos_root ne $u; my $fetch = $remotes->{$repo_id}->{fetch} || {}; @@ -1866,11 +1893,14 @@ sub make_log_entry { } elsif ($self->use_svnsync_props) { my $full_url = $self->svnsync->{url}; $full_url .= "/$self->{path}" if length $self->{path}; + remove_username($full_url); my $uuid = $self->svnsync->{uuid}; $log_entry{metadata} = "$full_url\@$rev $uuid"; $email ||= "$author\@$uuid" } else { - $log_entry{metadata} = $self->metadata_url. "\@$rev " . + my $url = $self->metadata_url; + remove_username($url); + $log_entry{metadata} = "$url\@$rev " . $self->ra->get_uuid; $email ||= "$author\@" . $self->ra->get_uuid; } @@ -2439,9 +2469,9 @@ sub close_file { my $got = $md5->hexdigest; die "Checksum mismatch: $path\n", "expected: $exp\n got: $got\n" if ($got ne $exp); - seek($fh, 0, 0) or croak $!; + sysseek($fh, 0, 0) or croak $!; if ($fb->{mode_b} == 120000) { - read($fh, my $buf, 5) == 5 or croak $!; + sysread($fh, my $buf, 5) == 5 or croak $!; $buf eq 'link ' or die "$path has mode 120000", "but is not a link\n"; } diff --git a/http.c b/http.c index 576740feff..ae27e0c940 100644 --- a/http.c +++ b/http.c @@ -300,6 +300,7 @@ void http_cleanup(void) curl_global_cleanup(); curl_slist_free_all(pragma_header); + pragma_header = NULL; } struct active_request_slot *get_active_slot(void) diff --git a/ident.c b/ident.c index 8540819d56..35dc4c6072 100644 --- a/ident.c +++ b/ident.c @@ -201,6 +201,8 @@ const char *fmt_ident(const char *name, const char *email, setup_ident(); if (!name) name = git_default_name; + if (!email) + email = getenv("EMAIL"); if (!email) email = git_default_email; diff --git a/log-tree.c b/log-tree.c index 300b733560..c679324c07 100644 --- a/log-tree.c +++ b/log-tree.c @@ -267,7 +267,7 @@ void show_log(struct rev_info *opt, const char *sep) if (opt->reflog_info) { show_reflog_message(opt->reflog_info, opt->commit_format == CMIT_FMT_ONELINE, - opt->relative_date); + opt->date_mode); if (opt->commit_format == CMIT_FMT_ONELINE) { printf("%s", sep); return; @@ -280,7 +280,7 @@ void show_log(struct rev_info *opt, const char *sep) */ len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, - extra_headers, opt->relative_date); + extra_headers, opt->date_mode); if (opt->add_signoff) len = append_signoff(this_header, sizeof(this_header), len, diff --git a/mailmap.c b/mailmap.c new file mode 100644 index 0000000000..cb567a2832 --- /dev/null +++ b/mailmap.c @@ -0,0 +1,92 @@ +#include "cache.h" +#include "path-list.h" +#include "mailmap.h" + +int read_mailmap(struct path_list *map, const char *filename, char **repo_abbrev) +{ + char buffer[1024]; + FILE *f = fopen(filename, "r"); + + if (f == NULL) + return 1; + while (fgets(buffer, sizeof(buffer), f) != NULL) { + char *end_of_name, *left_bracket, *right_bracket; + char *name, *email; + int i; + if (buffer[0] == '#') { + static const char abbrev[] = "# repo-abbrev:"; + int abblen = sizeof(abbrev) - 1; + int len = strlen(buffer); + + if (!repo_abbrev) + continue; + + if (len && buffer[len - 1] == '\n') + buffer[--len] = 0; + if (!strncmp(buffer, abbrev, abblen)) { + char *cp; + + if (repo_abbrev) + free(*repo_abbrev); + *repo_abbrev = xmalloc(len); + + for (cp = buffer + abblen; isspace(*cp); cp++) + ; /* nothing */ + strcpy(*repo_abbrev, cp); + } + continue; + } + if ((left_bracket = strchr(buffer, '<')) == NULL) + continue; + if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL) + continue; + if (right_bracket == left_bracket + 1) + continue; + for (end_of_name = left_bracket; end_of_name != buffer + && isspace(end_of_name[-1]); end_of_name--) + /* keep on looking */ + if (end_of_name == buffer) + continue; + name = xmalloc(end_of_name - buffer + 1); + strlcpy(name, buffer, end_of_name - buffer + 1); + email = xmalloc(right_bracket - left_bracket); + for (i = 0; i < right_bracket - left_bracket - 1; i++) + email[i] = tolower(left_bracket[i + 1]); + email[right_bracket - left_bracket - 1] = '\0'; + path_list_insert(email, map)->util = name; + } + fclose(f); + return 0; +} + +int map_email(struct path_list *map, const char *email, char *name, int maxlen) +{ + char *p; + struct path_list_item *item; + char buf[1024], *mailbuf; + int i; + + /* autocomplete common developers */ + p = strchr(email, '>'); + if (!p) + return 0; + if (p - email + 1 < sizeof(buf)) + mailbuf = buf; + else + mailbuf = xmalloc(p - email + 1); + + /* downcase the email address */ + for (i = 0; i < p - email; i++) + mailbuf[i] = tolower(email[i]); + mailbuf[i] = 0; + item = path_list_lookup(mailbuf, map); + if (mailbuf != buf) + free(mailbuf); + if (item != NULL) { + const char *realname = (const char *)item->util; + strlcpy(name, realname, maxlen); + return 1; + } + return 0; +} + diff --git a/mailmap.h b/mailmap.h new file mode 100644 index 0000000000..3503fd2727 --- /dev/null +++ b/mailmap.h @@ -0,0 +1,7 @@ +#ifndef MAILMAP_H +#define MAILMAP_H + +int read_mailmap(struct path_list *map, const char *filename, char **repo_abbrev); +int map_email(struct path_list *mailmap, const char *email, char *name, int maxlen); + +#endif diff --git a/merge-recursive.c b/merge-recursive.c index 403a4c8bca..8f72b2c079 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1342,20 +1342,31 @@ static int process_renames(struct path_list *a_renames, mfi = merge_file(o, a, b, a_branch, b_branch); - if (mfi.merge || !mfi.clean) - output(1, "Renamed %s => %s", ren1_src, ren1_dst); - if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); - if (!mfi.clean) { - output(1, "CONFLICT (rename/modify): Merge conflict in %s", - ren1_dst); - clean_merge = 0; + if (mfi.clean && + sha_eq(mfi.sha, ren1->pair->two->sha1) && + mfi.mode == ren1->pair->two->mode) + /* + * This messaged is part of + * t6022 test. If you change + * it update the test too. + */ + output(3, "Skipped %s (merged same as existing)", ren1_dst); + else { + if (mfi.merge || !mfi.clean) + output(1, "Renamed %s => %s", ren1_src, ren1_dst); + if (mfi.merge) + output(2, "Auto-merged %s", ren1_dst); + if (!mfi.clean) { + output(1, "CONFLICT (rename/modify): Merge conflict in %s", + ren1_dst); + clean_merge = 0; - if (!index_only) - update_stages(ren1_dst, - o, a, b, 1); + if (!index_only) + update_stages(ren1_dst, + o, a, b, 1); + } + update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } - update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } } diff --git a/object.c b/object.c index 7bd3fec556..37d1363359 100644 --- a/object.c +++ b/object.c @@ -230,6 +230,11 @@ int object_list_contains(struct object_list *list, struct object *obj) } void add_object_array(struct object *obj, const char *name, struct object_array *array) +{ + add_object_array_with_mode(obj, name, array, S_IFINVALID); +} + +void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode) { unsigned nr = array->nr; unsigned alloc = array->alloc; @@ -243,5 +248,6 @@ void add_object_array(struct object *obj, const char *name, struct object_array } objects[nr].item = obj; objects[nr].name = name; + objects[nr].mode = mode; array->nr = ++nr; } diff --git a/object.h b/object.h index 3e26a0e8b9..94f19eed86 100644 --- a/object.h +++ b/object.h @@ -17,6 +17,7 @@ struct object_array { struct object_array_entry { struct object *item; const char *name; + unsigned mode; } *objects; }; @@ -77,5 +78,6 @@ int object_list_contains(struct object_list *list, struct object *obj); /* Object array handling .. */ void add_object_array(struct object *obj, const char *name, struct object_array *array); +void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode); #endif /* OBJECT_H */ diff --git a/path-list.h b/path-list.h index d6401eaa35..ce5ffabcce 100644 --- a/path-list.h +++ b/path-list.h @@ -1,5 +1,5 @@ -#ifndef _PATH_LIST_H_ -#define _PATH_LIST_H_ +#ifndef PATH_LIST_H +#define PATH_LIST_H struct path_list_item { char *path; @@ -19,4 +19,4 @@ void path_list_clear(struct path_list *list, int free_items); struct path_list_item *path_list_insert(const char *path, struct path_list *list); struct path_list_item *path_list_lookup(const char *path, struct path_list *list); -#endif /* _PATH_LIST_H_ */ +#endif /* PATH_LIST_H */ diff --git a/progress.h b/progress.h index 4ee851acfb..5ae1a89e5a 100644 --- a/progress.h +++ b/progress.h @@ -1,5 +1,5 @@ -#ifndef __progress_h__ -#define __progress_h__ +#ifndef PROGRESS_H +#define PROGRESS_H struct progress { const char *prefix; diff --git a/read-cache.c b/read-cache.c index d2f332a622..d9f46da5cc 100644 --- a/read-cache.c +++ b/read-cache.c @@ -3,6 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "cache-tree.h" #include "refs.h" @@ -19,14 +20,7 @@ #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) ) #define CACHE_EXT_TREE 0x54524545 /* "TREE" */ -struct cache_entry **active_cache; -static time_t index_file_timestamp; -unsigned int active_nr, active_alloc, active_cache_changed; - -struct cache_tree *active_cache_tree; - -static void *cache_mmap; -static size_t cache_mmap_size; +struct index_state the_index; /* * This only updates the "non-critical" parts of the directory @@ -196,7 +190,8 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) return changed; } -int ce_match_stat(struct cache_entry *ce, struct stat *st, int options) +int ie_match_stat(struct index_state *istate, + struct cache_entry *ce, struct stat *st, int options) { unsigned int changed; int ignore_valid = options & 01; @@ -228,8 +223,8 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st, int options) * carefully than others. */ if (!changed && - index_file_timestamp && - index_file_timestamp <= ntohl(ce->ce_mtime.sec)) { + istate->timestamp && + istate->timestamp <= ntohl(ce->ce_mtime.sec)) { if (assume_racy_is_modified) changed |= DATA_CHANGED; else @@ -239,10 +234,11 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st, int options) return changed; } -int ce_modified(struct cache_entry *ce, struct stat *st, int really) +int ie_modified(struct index_state *istate, + struct cache_entry *ce, struct stat *st, int really) { int changed, changed_fs; - changed = ce_match_stat(ce, st, really); + changed = ie_match_stat(istate, ce, st, really); if (!changed) return 0; /* @@ -310,15 +306,15 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla return 0; } -int cache_name_pos(const char *name, int namelen) +int index_name_pos(struct index_state *istate, const char *name, int namelen) { int first, last; first = 0; - last = active_nr; + last = istate->cache_nr; while (last > first) { int next = (last + first) >> 1; - struct cache_entry *ce = active_cache[next]; + struct cache_entry *ce = istate->cache[next]; int cmp = cache_name_compare(name, namelen, ce->name, ntohs(ce->ce_flags)); if (!cmp) return next; @@ -332,27 +328,29 @@ int cache_name_pos(const char *name, int namelen) } /* Remove entry, return true if there are more entries to go.. */ -int remove_cache_entry_at(int pos) +int remove_index_entry_at(struct index_state *istate, int pos) { - active_cache_changed = 1; - active_nr--; - if (pos >= active_nr) + istate->cache_changed = 1; + istate->cache_nr--; + if (pos >= istate->cache_nr) return 0; - memmove(active_cache + pos, active_cache + pos + 1, (active_nr - pos) * sizeof(struct cache_entry *)); + memmove(istate->cache + pos, + istate->cache + pos + 1, + (istate->cache_nr - pos) * sizeof(struct cache_entry *)); return 1; } -int remove_file_from_cache(const char *path) +int remove_file_from_index(struct index_state *istate, const char *path) { - int pos = cache_name_pos(path, strlen(path)); + int pos = index_name_pos(istate, path, strlen(path)); if (pos < 0) pos = -pos-1; - while (pos < active_nr && !strcmp(active_cache[pos]->name, path)) - remove_cache_entry_at(pos); + while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path)) + remove_index_entry_at(istate, pos); return 0; } -int add_file_to_cache(const char *path, int verbose) +int add_file_to_index(struct index_state *istate, const char *path, int verbose) { int size, namelen; struct stat st; @@ -382,19 +380,19 @@ int add_file_to_cache(const char *path, int verbose) * from it, otherwise assume unexecutable regular file. */ struct cache_entry *ent; - int pos = cache_name_pos(path, namelen); + int pos = index_name_pos(istate, path, namelen); - ent = (0 <= pos) ? active_cache[pos] : NULL; + ent = (0 <= pos) ? istate->cache[pos] : NULL; ce->ce_mode = ce_mode_from_stat(ent, st.st_mode); } if (index_path(ce->sha1, path, &st, 1)) die("unable to index file %s", path); - if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) + if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) die("unable to add %s to index",path); if (verbose) printf("add '%s'\n", path); - cache_tree_invalidate_path(active_cache_tree, path); + cache_tree_invalidate_path(istate->cache_tree, path); return 0; } @@ -498,15 +496,16 @@ inside: * Do we have another file that has the beginning components being a * proper superset of the name we're trying to add? */ -static int has_file_name(const struct cache_entry *ce, int pos, int ok_to_replace) +static int has_file_name(struct index_state *istate, + const struct cache_entry *ce, int pos, int ok_to_replace) { int retval = 0; int len = ce_namelen(ce); int stage = ce_stage(ce); const char *name = ce->name; - while (pos < active_nr) { - struct cache_entry *p = active_cache[pos++]; + while (pos < istate->cache_nr) { + struct cache_entry *p = istate->cache[pos++]; if (len >= ce_namelen(p)) break; @@ -521,7 +520,7 @@ static int has_file_name(const struct cache_entry *ce, int pos, int ok_to_replac retval = -1; if (!ok_to_replace) break; - remove_cache_entry_at(--pos); + remove_index_entry_at(istate, --pos); } return retval; } @@ -530,7 +529,8 @@ static int has_file_name(const struct cache_entry *ce, int pos, int ok_to_replac * Do we have another file with a pathname that is a proper * subset of the name we're trying to add? */ -static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace) +static int has_dir_name(struct index_state *istate, + const struct cache_entry *ce, int pos, int ok_to_replace) { int retval = 0; int stage = ce_stage(ce); @@ -548,7 +548,7 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace } len = slash - name; - pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage))); + pos = index_name_pos(istate, name, ntohs(create_ce_flags(len, stage))); if (pos >= 0) { /* * Found one, but not so fast. This could @@ -558,11 +558,11 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace * it is Ok to have a directory at the same * path. */ - if (stage || active_cache[pos]->ce_mode) { + if (stage || istate->cache[pos]->ce_mode) { retval = -1; if (!ok_to_replace) break; - remove_cache_entry_at(pos); + remove_index_entry_at(istate, pos); continue; } } @@ -574,8 +574,8 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace * already matches the sub-directory, then we know * we're ok, and we can exit. */ - while (pos < active_nr) { - struct cache_entry *p = active_cache[pos]; + while (pos < istate->cache_nr) { + struct cache_entry *p = istate->cache[pos]; if ((ce_namelen(p) <= len) || (p->name[len] != '/') || memcmp(p->name, name, len)) @@ -602,7 +602,9 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace * from the cache so the caller should recompute the insert position. * When this happens, we return non-zero. */ -static int check_file_directory_conflict(const struct cache_entry *ce, int pos, int ok_to_replace) +static int check_file_directory_conflict(struct index_state *istate, + const struct cache_entry *ce, + int pos, int ok_to_replace) { int retval; @@ -617,28 +619,28 @@ static int check_file_directory_conflict(const struct cache_entry *ce, int pos, * first, since removing those will not change the position * in the array. */ - retval = has_file_name(ce, pos, ok_to_replace); + retval = has_file_name(istate, ce, pos, ok_to_replace); /* * Then check if the path might have a clashing sub-directory * before it. */ - return retval + has_dir_name(ce, pos, ok_to_replace); + return retval + has_dir_name(istate, ce, pos, ok_to_replace); } -int add_cache_entry(struct cache_entry *ce, int option) +int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option) { int pos; int ok_to_add = option & ADD_CACHE_OK_TO_ADD; int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE; int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK; - pos = cache_name_pos(ce->name, ntohs(ce->ce_flags)); + pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags)); /* existing match? Just replace it. */ if (pos >= 0) { - active_cache_changed = 1; - active_cache[pos] = ce; + istate->cache_changed = 1; + istate->cache[pos] = ce; return 0; } pos = -pos-1; @@ -647,10 +649,10 @@ int add_cache_entry(struct cache_entry *ce, int option) * Inserting a merged entry ("stage 0") into the index * will always replace all non-merged entries.. */ - if (pos < active_nr && ce_stage(ce) == 0) { - while (ce_same_name(active_cache[pos], ce)) { + if (pos < istate->cache_nr && ce_stage(ce) == 0) { + while (ce_same_name(istate->cache[pos], ce)) { ok_to_add = 1; - if (!remove_cache_entry_at(pos)) + if (!remove_index_entry_at(istate, pos)) break; } } @@ -661,25 +663,29 @@ int add_cache_entry(struct cache_entry *ce, int option) return -1; if (!skip_df_check && - check_file_directory_conflict(ce, pos, ok_to_replace)) { + check_file_directory_conflict(istate, ce, pos, ok_to_replace)) { if (!ok_to_replace) - return error("'%s' appears as both a file and as a directory", ce->name); - pos = cache_name_pos(ce->name, ntohs(ce->ce_flags)); + return error("'%s' appears as both a file and as a directory", + ce->name); + pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags)); pos = -pos-1; } /* Make sure the array is big enough .. */ - if (active_nr == active_alloc) { - active_alloc = alloc_nr(active_alloc); - active_cache = xrealloc(active_cache, active_alloc * sizeof(struct cache_entry *)); + if (istate->cache_nr == istate->cache_alloc) { + istate->cache_alloc = alloc_nr(istate->cache_alloc); + istate->cache = xrealloc(istate->cache, + istate->cache_alloc * sizeof(struct cache_entry *)); } /* Add it in.. */ - active_nr++; - if (active_nr > pos) - memmove(active_cache + pos + 1, active_cache + pos, (active_nr - pos - 1) * sizeof(ce)); - active_cache[pos] = ce; - active_cache_changed = 1; + istate->cache_nr++; + if (istate->cache_nr > pos) + memmove(istate->cache + pos + 1, + istate->cache + pos, + (istate->cache_nr - pos - 1) * sizeof(ce)); + istate->cache[pos] = ce; + istate->cache_changed = 1; return 0; } @@ -694,7 +700,8 @@ int add_cache_entry(struct cache_entry *ce, int option) * For example, you'd want to do this after doing a "git-read-tree", * to link up the stat cache details with the proper files. */ -static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really, int *err) +static struct cache_entry *refresh_cache_ent(struct index_state *istate, + struct cache_entry *ce, int really, int *err) { struct stat st; struct cache_entry *updated; @@ -706,7 +713,7 @@ static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really, return NULL; } - changed = ce_match_stat(ce, &st, really); + changed = ie_match_stat(istate, ce, &st, really); if (!changed) { if (really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) @@ -715,7 +722,7 @@ static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really, return ce; } - if (ce_modified(ce, &st, really)) { + if (ie_modified(istate, ce, &st, really)) { if (err) *err = EINVAL; return NULL; @@ -738,7 +745,7 @@ static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really, return updated; } -int refresh_cache(unsigned int flags) +int refresh_index(struct index_state *istate, unsigned int flags) { int i; int has_errors = 0; @@ -747,14 +754,14 @@ int refresh_cache(unsigned int flags) int quiet = (flags & REFRESH_QUIET) != 0; int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; - for (i = 0; i < active_nr; i++) { + for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce, *new; int cache_errno = 0; - ce = active_cache[i]; + ce = istate->cache[i]; if (ce_stage(ce)) { - while ((i < active_nr) && - ! strcmp(active_cache[i]->name, ce->name)) + while ((i < istate->cache_nr) && + ! strcmp(istate->cache[i]->name, ce->name)) i++; i--; if (allow_unmerged) @@ -764,7 +771,7 @@ int refresh_cache(unsigned int flags) continue; } - new = refresh_cache_ent(ce, really, &cache_errno); + new = refresh_cache_ent(istate, ce, really, &cache_errno); if (new == ce) continue; if (!new) { @@ -775,7 +782,7 @@ int refresh_cache(unsigned int flags) * means the index is not valid anymore. */ ce->ce_flags &= ~htons(CE_VALID); - active_cache_changed = 1; + istate->cache_changed = 1; } if (quiet) continue; @@ -783,18 +790,18 @@ int refresh_cache(unsigned int flags) has_errors = 1; continue; } - active_cache_changed = 1; - /* You can NOT just free active_cache[i] here, since it + istate->cache_changed = 1; + /* You can NOT just free istate->cache[i] here, since it * might not be necessarily malloc()ed but can also come * from mmap(). */ - active_cache[i] = new; + istate->cache[i] = new; } return has_errors; } struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really) { - return refresh_cache_ent(ce, really, NULL); + return refresh_cache_ent(&the_index, ce, really, NULL); } static int verify_hdr(struct cache_header *hdr, unsigned long size) @@ -814,11 +821,12 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size) return 0; } -static int read_index_extension(const char *ext, void *data, unsigned long sz) +static int read_index_extension(struct index_state *istate, + const char *ext, void *data, unsigned long sz) { switch (CACHE_EXT(ext)) { case CACHE_EXT_TREE: - active_cache_tree = cache_tree_read(data, sz); + istate->cache_tree = cache_tree_read(data, sz); break; default: if (*ext < 'A' || 'Z' < *ext) @@ -830,13 +838,13 @@ static int read_index_extension(const char *ext, void *data, unsigned long sz) return 0; } -int read_cache(void) +int read_index(struct index_state *istate) { - return read_cache_from(get_index_file()); + return read_index_from(istate, get_index_file()); } /* remember to discard_cache() before reading a different cache! */ -int read_cache_from(const char *path) +int read_index_from(struct index_state *istate, const char *path) { int fd, i; struct stat st; @@ -844,11 +852,11 @@ int read_cache_from(const char *path) struct cache_header *hdr; errno = EBUSY; - if (cache_mmap) - return active_nr; + if (istate->mmap) + return istate->cache_nr; errno = ENOENT; - index_file_timestamp = 0; + istate->timestamp = 0; fd = open(path, O_RDONLY); if (fd < 0) { if (errno == ENOENT) @@ -856,33 +864,35 @@ int read_cache_from(const char *path) die("index file open failed (%s)", strerror(errno)); } - if (!fstat(fd, &st)) { - cache_mmap_size = xsize_t(st.st_size); - errno = EINVAL; - if (cache_mmap_size >= sizeof(struct cache_header) + 20) - cache_mmap = xmmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - else - die("index file smaller than expected"); - } else + if (fstat(fd, &st)) die("cannot stat the open index (%s)", strerror(errno)); + + errno = EINVAL; + istate->mmap_size = xsize_t(st.st_size); + if (istate->mmap_size < sizeof(struct cache_header) + 20) + die("index file smaller than expected"); + + istate->mmap = xmmap(NULL, istate->mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); close(fd); - hdr = cache_mmap; - if (verify_hdr(hdr, cache_mmap_size) < 0) + hdr = istate->mmap; + if (verify_hdr(hdr, istate->mmap_size) < 0) goto unmap; - active_nr = ntohl(hdr->hdr_entries); - active_alloc = alloc_nr(active_nr); - active_cache = xcalloc(active_alloc, sizeof(struct cache_entry *)); + istate->cache_nr = ntohl(hdr->hdr_entries); + istate->cache_alloc = alloc_nr(istate->cache_nr); + istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *)); offset = sizeof(*hdr); - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = (struct cache_entry *) ((char *) cache_mmap + offset); + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce; + + ce = (struct cache_entry *)((char *)(istate->mmap) + offset); offset = offset + ce_size(ce); - active_cache[i] = ce; + istate->cache[i] = ce; } - index_file_timestamp = st.st_mtime; - while (offset <= cache_mmap_size - 20 - 8) { + istate->timestamp = st.st_mtime; + while (offset <= istate->mmap_size - 20 - 8) { /* After an array of active_nr index entries, * there can be arbitrary number of extended * sections, each of which is prefixed with @@ -890,35 +900,37 @@ int read_cache_from(const char *path) * in 4-byte network byte order. */ unsigned long extsize; - memcpy(&extsize, (char *) cache_mmap + offset + 4, 4); + memcpy(&extsize, (char *)(istate->mmap) + offset + 4, 4); extsize = ntohl(extsize); - if (read_index_extension(((const char *) cache_mmap) + offset, - (char *) cache_mmap + offset + 8, + if (read_index_extension(istate, + ((const char *) (istate->mmap)) + offset, + (char *) (istate->mmap) + offset + 8, extsize) < 0) goto unmap; offset += 8; offset += extsize; } - return active_nr; + return istate->cache_nr; unmap: - munmap(cache_mmap, cache_mmap_size); + munmap(istate->mmap, istate->mmap_size); errno = EINVAL; die("index file corrupt"); } -int discard_cache(void) +int discard_index(struct index_state *istate) { int ret; - active_nr = active_cache_changed = 0; - index_file_timestamp = 0; - cache_tree_free(&active_cache_tree); - if (cache_mmap == NULL) + istate->cache_nr = 0; + istate->cache_changed = 0; + istate->timestamp = 0; + cache_tree_free(&(istate->cache_tree)); + if (istate->mmap == NULL) return 0; - ret = munmap(cache_mmap, cache_mmap_size); - cache_mmap = NULL; - cache_mmap_size = 0; + ret = munmap(istate->mmap, istate->mmap_size); + istate->mmap = NULL; + istate->mmap_size = 0; /* no need to throw away allocated active_cache */ return ret; @@ -1037,11 +1049,13 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce) } } -int write_cache(int newfd, struct cache_entry **cache, int entries) +int write_index(struct index_state *istate, int newfd) { SHA_CTX c; struct cache_header hdr; int i, removed; + struct cache_entry **cache = istate->cache; + int entries = istate->cache_nr; for (i = removed = 0; i < entries; i++) if (!cache[i]->ce_mode) @@ -1059,17 +1073,17 @@ int write_cache(int newfd, struct cache_entry **cache, int entries) struct cache_entry *ce = cache[i]; if (!ce->ce_mode) continue; - if (index_file_timestamp && - index_file_timestamp <= ntohl(ce->ce_mtime.sec)) + if (istate->timestamp && + istate->timestamp <= ntohl(ce->ce_mtime.sec)) ce_smudge_racily_clean_entry(ce); if (ce_write(&c, newfd, ce, ce_size(ce)) < 0) return -1; } /* Write extension data here */ - if (active_cache_tree) { + if (istate->cache_tree) { unsigned long sz; - void *data = cache_tree_write(active_cache_tree, &sz); + void *data = cache_tree_write(istate->cache_tree, &sz); if (data && !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) && !ce_write(&c, newfd, data, sz)) diff --git a/revision.c b/revision.c index ce70f48ce0..e60a26c6bb 100644 --- a/revision.c +++ b/revision.c @@ -115,10 +115,15 @@ void mark_parents_uninteresting(struct commit *commit) } void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) +{ + add_pending_object_with_mode(revs, obj, name, S_IFINVALID); +} + +void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode) { if (revs->no_walk && (obj->flags & UNINTERESTING)) die("object ranges do not make sense when not walking revisions"); - add_object_array(obj, name, &revs->pending); + add_object_array_with_mode(obj, name, &revs->pending, mode); if (revs->reflog_info && obj->type == OBJ_COMMIT) add_reflog_for_walk(revs->reflog_info, (struct commit *)obj, name); @@ -723,6 +728,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, int cant_be_filename) { + unsigned mode; char *dotdot; struct object *object; unsigned char sha1[20]; @@ -796,12 +802,12 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, local_flags = UNINTERESTING; arg++; } - if (get_sha1(arg, sha1)) + if (get_sha1_with_mode(arg, sha1, &mode)) return -1; if (!cant_be_filename) verify_non_filename(revs->prefix, arg); object = get_reference(revs, arg, sha1, flags ^ local_flags); - add_pending_object(revs, object, arg); + add_pending_object_with_mode(revs, object, arg, mode); return 0; } @@ -1105,7 +1111,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch continue; } if (!strcmp(arg, "--relative-date")) { - revs->relative_date = 1; + revs->date_mode = DATE_RELATIVE; + continue; + } + if (!strncmp(arg, "--date=", 7)) { + if (!strcmp(arg + 7, "relative")) + revs->date_mode = DATE_RELATIVE; + else if (!strcmp(arg + 7, "local")) + revs->date_mode = DATE_LOCAL; + else if (!strcmp(arg + 7, "default")) + revs->date_mode = DATE_NORMAL; + else + die("unknown date format %s", arg); continue; } @@ -1177,10 +1194,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (def && !revs->pending.nr) { unsigned char sha1[20]; struct object *object; - if (get_sha1(def, sha1)) + unsigned mode; + if (get_sha1_with_mode(def, sha1, &mode)) die("bad default revision '%s'", def); object = get_reference(revs, def, sha1, 0); - add_pending_object(revs, object, def); + add_pending_object_with_mode(revs, object, def, mode); } if (revs->topo_order) diff --git a/revision.h b/revision.h index 8a02618428..cdf94ad695 100644 --- a/revision.h +++ b/revision.h @@ -63,8 +63,8 @@ struct rev_info { /* Format info */ unsigned int shown_one:1, - abbrev_commit:1, - relative_date:1; + abbrev_commit:1; + enum date_mode date_mode; const char **ignore_packed; /* pretend objects in these are unpacked */ int num_ignore_packed; @@ -131,5 +131,6 @@ extern void add_object(struct object *obj, const char *name); extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name); +extern void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode); #endif diff --git a/sha1_file.c b/sha1_file.c index d895ed0ffe..f97a901a0e 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -557,7 +557,7 @@ static void scan_windows(struct packed_git *p, } } -static int unuse_one_window(struct packed_git *current) +static int unuse_one_window(struct packed_git *current, int keep_fd) { struct packed_git *p, *lru_p = NULL; struct pack_window *lru_w = NULL, *lru_l = NULL; @@ -573,7 +573,7 @@ static int unuse_one_window(struct packed_git *current) lru_l->next = lru_w->next; else { lru_p->windows = lru_w->next; - if (!lru_p->windows && lru_p != current) { + if (!lru_p->windows && lru_p->pack_fd != keep_fd) { close(lru_p->pack_fd); lru_p->pack_fd = -1; } @@ -585,10 +585,10 @@ static int unuse_one_window(struct packed_git *current) return 0; } -void release_pack_memory(size_t need) +void release_pack_memory(size_t need, int fd) { size_t cur = pack_mapped; - while (need >= (cur - pack_mapped) && unuse_one_window(NULL)) + while (need >= (cur - pack_mapped) && unuse_one_window(NULL, fd)) ; /* nothing */ } @@ -723,7 +723,7 @@ unsigned char* use_pack(struct packed_git *p, win->len = (size_t)len; pack_mapped += win->len; while (packed_git_limit < pack_mapped - && unuse_one_window(p)) + && unuse_one_window(p, p->pack_fd)) ; /* nothing */ win->base = xmmap(NULL, win->len, PROT_READ, MAP_PRIVATE, diff --git a/sha1_name.c b/sha1_name.c index b0b12bbe9d..55f25a2d3b 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -643,11 +643,17 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) */ int get_sha1(const char *name, unsigned char *sha1) { - int ret, bracket_depth; unsigned unused; + return get_sha1_with_mode(name, sha1, &unused); +} + +int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode) +{ + int ret, bracket_depth; int namelen = strlen(name); const char *cp; + *mode = S_IFINVALID; prepare_alt_odb(); ret = get_sha1_1(name, namelen, sha1); if (!ret) @@ -685,6 +691,7 @@ int get_sha1(const char *name, unsigned char *sha1) break; if (ce_stage(ce) == stage) { hashcpy(sha1, ce->sha1); + *mode = ntohl(ce->ce_mode); return 0; } pos++; @@ -703,7 +710,7 @@ int get_sha1(const char *name, unsigned char *sha1) unsigned char tree_sha1[20]; if (!get_sha1_1(name, cp-name, tree_sha1)) return get_tree_entry(tree_sha1, cp+1, sha1, - &unused); + mode); } return ret; } diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh new file mode 100755 index 0000000000..bab9ecc34e --- /dev/null +++ b/t/t0021-conversion.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +test_description='blob conversion via gitattributes' + +. ./test-lib.sh + +cat <<\EOF >rot13.sh +tr '[a-zA-Z]' '[n-za-mN-ZA-M]' +EOF +chmod +x rot13.sh + +test_expect_success setup ' + git config filter.rot13.smudge ./rot13.sh && + git config filter.rot13.clean ./rot13.sh && + + { + echo "*.t filter=rot13" + echo "*.i ident" + } >.gitattributes && + + { + echo a b c d e f g h i j k l m + echo n o p q r s t u v w x y z + echo '\''$ident$'\'' + } >test && + cat test >test.t && + cat test >test.o && + cat test >test.i && + git add test test.t test.i && + rm -f test test.t test.i && + git checkout -- test test.t test.i +' + +script='s/^\$ident: \([0-9a-f]*\) \$/\1/p' + +test_expect_success check ' + + cmp test.o test && + cmp test.o test.t && + + # ident should be stripped in the repository + git diff --raw --exit-code :test :test.i && + id=$(git rev-parse --verify :test) && + embedded=$(sed -ne "$script" test.i) && + test "z$id" = "z$embedded" +' + +test_done diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh index b608e202c1..e3f7ae8120 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6022-merge-rename.sh @@ -47,6 +47,8 @@ git branch white && git branch red && git branch blue && git branch yellow && +git branch change && +git branch change+rename && sed -e "/^g /s/.*/g : master changes a line/" A+ && mv A+ A && @@ -77,6 +79,17 @@ rm -f A M && git update-index --add --remove A C M N && git commit -m "blue renames A->C, M->N" && +git checkout change && +sed -e "/^g /s/.*/g : changed line/" A+ && +mv A+ A && +git commit -q -a -m "changed" && + +git checkout change+rename && +sed -e "/^g /s/.*/g : changed line/" B && +rm A && +git update-index --add B && +git commit -q -a -m "changed and renamed" && + git checkout master' test_expect_success 'pull renaming branch into unrenaming one' \ @@ -318,4 +331,14 @@ test_expect_success 'interference with untracked working tree file' ' git reset --hard anchor ' +test_expect_success 'merge of identical changes in a renamed file' ' + rm -f A M N + git reset --hard && + git checkout change+rename && + GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" && + git reset --hard HEAD^ && + git checkout change && + GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B" +' + test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 0a2623a7f8..04a2c67089 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -16,6 +16,7 @@ unset AUTHOR_EMAIL unset AUTHOR_NAME unset COMMIT_AUTHOR_EMAIL unset COMMIT_AUTHOR_NAME +unset EMAIL unset GIT_ALTERNATE_OBJECT_DIRECTORIES unset GIT_AUTHOR_DATE GIT_AUTHOR_EMAIL=author@example.com @@ -36,6 +37,10 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME export EDITOR VISUAL +# Protect ourselves from common misconfiguration to export +# CDPATH into the environment +unset CDPATH + case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in 1|2|true) echo "* warning: Some tests will not work if GIT_TRACE" \