Merge branch 'ph/parseopt'
authorJunio C Hamano <gitster@pobox.com>
Sun, 9 Mar 2008 05:29:59 +0000 (21:29 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 9 Mar 2008 05:29:59 +0000 (21:29 -0800)
* ph/parseopt:
parse-options: new option type to treat an option-like parameter as an argument.
parse-opt: bring PARSE_OPT_HIDDEN and NONEG to git-rev-parse --parseopt

133 files changed:
Documentation/CodingGuidelines
Documentation/RelNotes-1.5.4.4.txt
Documentation/RelNotes-1.5.5.txt
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-cvsimport.txt
Documentation/git-describe.txt
Documentation/git-fetch-pack.txt
Documentation/git-grep.txt
Documentation/git-index-pack.txt
Documentation/git-merge.txt
Documentation/git-mergetool.txt
Documentation/git-pack-objects.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-verify-pack.txt
Documentation/git-whatchanged.txt
Documentation/git.txt
Documentation/pretty-options.txt
Documentation/rev-list-options.txt
Documentation/technical/api-run-command.txt
Makefile
builtin-add.c
builtin-checkout.c
builtin-clean.c
builtin-describe.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fsck.c
builtin-http-fetch.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-reflog.c
builtin-reset.c
builtin-rev-list.c
builtin-revert.c
builtin-send-pack.c
builtin-shortlog.c
builtin-unpack-objects.c
builtin-verify-pack.c
cache.h
commit.c
commit.h
compat/snprintf.c [new file with mode: 0644]
config.mak.in
configure.ac
contrib/completion/git-completion.bash
contrib/emacs/git.el
contrib/fast-import/git-p4
contrib/hooks/post-receive-email
contrib/stats/packinfo.pl
daemon.c
diff-lib.c
diff.c
fast-import.c
fetch-pack.h
fsck.c [new file with mode: 0644]
fsck.h [new file with mode: 0644]
git-am.sh
git-compat-util.h
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-gui/Makefile
git-merge.sh
git-mergetool.sh
git-rebase.sh
git-remote.perl
git-send-email.perl
git-stash.sh
git-submodule.sh
git-svn.perl
gitweb/gitweb.perl
http-push.c
http-walker.c
http.c
http.h
ident.c
index-pack.c
object-refs.c [deleted file]
object.h
pack-check.c
pack-revindex.c [new file with mode: 0644]
pack-revindex.h [new file with mode: 0644]
path.c
quote.c
quote.h
receive-pack.c
refs.c
refs.h
revision.c
run-command.c
setup.c
sha1_file.c
sha1_name.c
t/lib-httpd.sh [new file with mode: 0644]
t/lib-httpd/apache.conf [new file with mode: 0644]
t/lib-httpd/ssl.cnf [new file with mode: 0644]
t/t0000-basic.sh
t/t1410-reflog.sh
t/t3101-ls-tree-dirname.sh
t/t3407-rebase-abort.sh
t/t3501-revert-cherry-pick.sh
t/t3903-stash.sh
t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
t/t4014-format-patch.sh
t/t4027-diff-submodule.sh [new file with mode: 0755]
t/t4150-am-subdir.sh [new file with mode: 0755]
t/t5305-include-tag.sh [new file with mode: 0755]
t/t5503-tagfollow.sh [new file with mode: 0755]
t/t5540-http-push.sh [new file with mode: 0755]
t/t6120-describe.sh
t/t7003-filter-branch.sh
t/t7010-setup.sh
t/t7300-clean.sh
t/t7600-merge.sh
t/t7610-mergetool.sh [new file with mode: 0644]
t/t9300-fast-import.sh
t/test-lib.sh
tag.c
transport.c
transport.h
tree.c
upload-pack.c
walker.c
walker.h
wt-status.c
index 3b042db624980dc962bbd67af1f3defa82ea64b1..994eb9159a2b0e8a10f4f9510165d420004203bf 100644 (file)
@@ -53,6 +53,18 @@ For shell scripts specifically (not exhaustive):
  - We do not write the noiseword "function" in front of shell
    functions.
 
+ - As to use of grep, stick to a subset of BRE (namely, no \{m,n\},
+   [::], [==], nor [..]) for portability.
+
+   - We do not use \{m,n\};
+
+   - We do not use -E;
+
+   - We do not use ? nor + (which are \{0,1\} and \{1,\}
+     respectively in BRE) but that goes without saying as these
+     are ERE elements not BRE (note that \? and \+ are not even part
+     of BRE -- making them accessible from BRE is a GNU extension).
+
 For C programs:
 
  - We use tabs to indent, and interpret tabs as taking up to
index 5bfdb35376858be5358be21832ff56f4eaa3e8b9..89fa6d03bc038d6210e94d7fe9fbbffdbc84d883 100644 (file)
@@ -4,6 +4,10 @@ GIT v1.5.4.4 Release Notes
 Fixes since v1.5.4.3
 --------------------
 
+ * Building and installing with an overtight umask such as 077 made
+   installed templates unreadable by others, while the rest of the install
+   are done in a way that is friendly to umask 022.
+
  * "git cvsexportcommit -w $cvsdir" misbehaved when GIT_DIR is set to a
    relative directory.
 
@@ -17,10 +21,46 @@ Fixes since v1.5.4.3
 
  * "git send-email" in 1.5.4.3 issued a bogus empty In-Reply-To: header.
 
-Also included are a handful documentation updates.
+ * "git bisect" showed mysterious "won't bisect on seeked tree" error message.
+   This was leftover from Cogito days to prevent "bisect" starting from a
+   cg-seeked state.  We still keep the Cogito safety, but running "git bisect
+   start" when another bisect was in effect will clean up and start over.
+
+ * "git push" with an explicit PATH to receive-pack did not quite work if
+   receive-pack was not on usual PATH.  We earlier fixed the same issue
+   with "git fetch" and upload-pack, but somehow forgot to do so in the
+   other direction.
+
+ * git-gui's info dialog was not displayed correctly when the user tries
+   to commit nothing (i.e. without staging anything).
+
+ * "git revert" did not properly fail when attempting to run with a
+   dirty index.
+
+ * "git merge --no-commit --no-ff <other>" incorrectly made commits.
+
+ * "git merge --squash --no-ff <other>", which is a nonsense combination
+   of options, was not rejected.
+
+ * "git ls-remote" and "git remote show" against an empty repository
+   failed, instead of just giving an empty result (regression).
 
----
-exec >/var/tmp/1
-echo O=$(git describe maint)
-O=v1.5.4.3
-git shortlog --no-merges $O..maint
+ * "git fast-import" did not handle a renamed path whose name needs to be
+   quoted, due to a bug in unquote_c_style() function.
+
+ * "git cvsexportcommit" was confused when multiple files with the same
+   basename needed to be pushed out in the same commit.
+
+ * "git daemon" did not send early errors to syslog.
+
+ * "git log --merge" did not work well with --left-right option.
+
+ * "git svn" promprted for client cert password every time it accessed the
+   server.
+
+ * The reset command in "git fast-import" data stream was documented to
+   end with an optional LF, but it actually required one.
+
+ * "git svn dcommit/rebase" did not honor --rewrite-root option.
+
+Also included are a handful documentation updates.
index 849b6b96047d305122c24cd4728467ffde551238..874dad9a4f04b2ce11c9a7449c60b322aa041a37 100644 (file)
@@ -4,6 +4,10 @@ GIT v1.5.5 Release Notes
 Updates since v1.5.4
 --------------------
 
+(subsystems)
+
+ * Comes with git-gui 0.9.3
+
 (performance)
 
  * On platforms with suboptimal qsort(3) implementation, there
@@ -57,6 +61,8 @@ Updates since v1.5.4
 
  * "git add -i" behaves better even before you make an initial commit.
 
+ * "git am" refused to run from a subdirectory without a good reason.
+
  * After "git apply --whitespace=fix" fixes whitespace errors in a patch,
    a line before the fix can appear as a context or preimage line in a
    later patch, causing the patch not to apply.  The command now knows to
@@ -81,6 +87,8 @@ Updates since v1.5.4
    inspect what is going to be committed and prepare the commit
    log message template to be edited.
 
+ * "git cvsimport" can now take more than one -M options.
+
  * "git describe" learned to limit the tags to be used for
    naming with --match option.
 
@@ -89,6 +97,12 @@ Updates since v1.5.4
 
  * "git describe --exact-match" describes only commits that are tagged.
 
+ * "git describe --long" describes a tagged commit as $tag-0-$sha1,
+   instead of just showing the exact tagname.
+
+ * "git describe" warns when using a tag whose name and path contradict
+   with each other.
+
  * "git diff" learned "--relative" option to limit and output paths
    relative to the current directory when working in a subdirectory.
 
@@ -98,6 +112,8 @@ Updates since v1.5.4
  * "git format-patch" learned --cover-letter option to generate a cover
    letter template.
 
+ * "git gc" learned --quiet option.
+
  * "git grep" now knows "--name-only" is a synonym for the "-l" option.
 
  * "git help <alias>" now reports "'git <alias>' is alias to <what>",
@@ -144,8 +160,14 @@ Fixes since v1.5.4
 All of the fixes in v1.5.4 maintenance series are included in
 this release, unless otherwise noted.
 
+ * "git-http-push" did not allow deletion of remote ref with the usual
+   "push <remote> :<branch>" syntax.
+
+ * "git-rebase --abort" did not go back to the right location if
+   "git-reset" was run during the "git-rebase" session.
+
 ---
 exec >/var/tmp/1
-O=v1.5.4.3-339-g7cf7f54
+O=v1.5.4.3-428-g6b48990
 echo O=`git describe refs/heads/master`
 git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
index 4027726f2ee66ebad69412a5c8c6d1aef7f7463f..c5e094a9c4a31568e4efb2c70af7e7808ae63528 100644 (file)
@@ -556,6 +556,11 @@ format.suffix::
        `.patch`. Use this variable to change that suffix (make sure to
        include the dot if you want it).
 
+format.pretty::
+       The default pretty format for log/show/whatchanged command,
+       See linkgit:git-log[1], linkgit:git-show[1],
+       linkgit:git-whatchanged[1].
+
 gc.aggressiveWindow::
        The window size parameter used in the delta compression
        algorithm used by 'git gc --aggressive'.  This defaults
@@ -749,8 +754,10 @@ merge.summary::
 
 merge.tool::
        Controls which merge resolution program is used by
-       linkgit:git-mergetool[1].  Valid values are: "kdiff3", "tkdiff",
-       "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
+       linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
+       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+       "opendiff".  Any other value is treated is custom merge tool
+       and there must be a corresponing mergetool.<tool>.cmd option.
 
 merge.verbosity::
        Controls the amount of output shown by the recursive merge
@@ -777,6 +784,31 @@ mergetool.<tool>.path::
        Override the path for the given tool.  This is useful in case
        your tool is not in the PATH.
 
+mergetool.<tool>.cmd::
+       Specify the command to invoke the specified merge tool.  The
+       specified command is evaluated in shell with the following
+       variables available: 'BASE' is the name of a temporary file
+       containing the common base of the files to be merged, if available;
+       'LOCAL' is the name of a temporary file containing the contents of
+       the file on the current branch; 'REMOTE' is the name of a temporary
+       file containing the contents of the file from the branch being
+       merged; 'MERGED' contains the name of the file to which the merge
+       tool should write the results of a successful merge.
+
+mergetool.<tool>.trustExitCode::
+       For a custom merge command, specify whether the exit code of
+       the merge command can be used to determine whether the merge was
+       successful.  If this is not set to true then the merge target file
+       timestamp is checked and the merge assumed to have been successful
+       if the file has been updated, otherwise the user is prompted to
+       indicate the success of the merge.
+
+mergetool.keepBackup::
+       After performing a merge, the original file with conflict markers
+       can be saved as a file with a `.orig` extension.  If this variable
+       is set to `false` then this file is not preserved.  Defaults to
+       `true` (i.e. keep the backup files).
+
 pack.window::
        The size of the window used by linkgit:git-pack-objects[1] when no
        window size is given on the command line. Defaults to 10.
@@ -864,15 +896,15 @@ remote.<name>.skipDefaultUpdate::
 
 remote.<name>.receivepack::
        The default program to execute on the remote side when pushing.  See
-       option \--exec of linkgit:git-push[1].
+       option \--receive-pack of linkgit:git-push[1].
 
 remote.<name>.uploadpack::
        The default program to execute on the remote side when fetching.  See
-       option \--exec of linkgit:git-fetch-pack[1].
+       option \--upload-pack of linkgit:git-fetch-pack[1].
 
 remote.<name>.tagopt::
-       Setting this value to --no-tags disables automatic tag following when fetching
-       from remote <name>
+       Setting this value to \--no-tags disables automatic tag following when
+       fetching from remote <name>
 
 remotes.<group>::
        The list of remotes which are fetched by "git remote update
index e640fc75cd3c463370f62b623a4060b014d9371a..2387a8d6c2472fa87136cd18c0a1145e4dc23bd2 100644 (file)
@@ -9,7 +9,7 @@ git-am - Apply a series of patches from a mailbox
 SYNOPSIS
 --------
 [verse]
-'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
+'git-am' [--signoff] [--keep] [--utf8 | --no-utf8]
          [--3way] [--interactive] [--binary]
          [--whitespace=<option>] [-C<n>] [-p<n>]
          <mbox>|<Maildir>...
@@ -32,10 +32,6 @@ OPTIONS
        Add `Signed-off-by:` line to the commit message, using
        the committer identity of yourself.
 
--d=<dir>, --dotest=<dir>::
-       Instead of `.dotest` directory, use <dir> as a working
-       area to store extracted patches.
-
 -k, --keep::
        Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
 
index 6f91b9ea2a17df8fc8e3c00fdf0d5c74a6c17950..58eefd42e504cf34178719080fd4f75a0b9ea621 100644 (file)
@@ -102,13 +102,17 @@ If you need to pass multiple options, separate them with a comma.
 
 -m::
        Attempt to detect merges based on the commit message. This option
-       will enable default regexes that try to capture the name source
+       will enable default regexes that try to capture the source
        branch name from the commit message.
 
 -M <regex>::
        Attempt to detect merges based on the commit message with a custom
        regex. It can be used with '-m' to enable the default regexes
        as well. You must escape forward slashes.
++
+The regex must capture the source branch name in $1.
++
+This option can be used several times to provide several detection regexes.
 
 -S <regex>::
        Skip paths matching the regex.
index fbb40a29165a47e627358b5b9f8afb50e84958cd..d9aa2f29809eb07385468edd2587daf55339389a 100644 (file)
@@ -56,6 +56,15 @@ OPTIONS
        being employed to standard error.  The tag name will still
        be printed to standard out.
 
+--long::
+       Always output the long format (the tag, the number of commits
+       and the abbreviated commit name) even when it matches a tag.
+       This is useful when you want to see parts of the commit object name
+       in "describe" output, even when the commit in question happens to be
+       a tagged version.  Instead of just emitting the tag name, it will
+       describe such a commit as v1.2-0-deadbeef (0th commit since tag v1.2
+       that points at object deadbeef....).
+
 --match <pattern>::
        Only consider tags matching the given pattern (can be used to avoid
        leaking private tags made from the repository).
index 2b8ffe5324c427d3b80f5b21d4a9d2ef8bfea4fd..57598eb0566c8eabc3537fb67b7841c65bcf5c2c 100644 (file)
@@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository
 
 SYNOPSIS
 --------
-'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
+'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
 
 DESCRIPTION
 -----------
@@ -45,6 +45,12 @@ OPTIONS
        Spend extra cycles to minimize the number of objects to be sent.
        Use it on slower connection.
 
+\--include-tag::
+       If the remote side supports it, annotated tags objects will
+       be downloaded on the same connection as the other objects if
+       the object the tag references is downloaded.  The caller must
+       otherwise determine the tags this option made available.
+
 \--upload-pack=<git-upload-pack>::
        Use this to specify the path to 'git-upload-pack' on the
        remote side, if is not found on your $PATH.
index 71a73354f8cfa960f641d9563ee8af405b4f881f..a97f0557f4aa6cf1d795470d7bbe94ce35abc56a 100644 (file)
@@ -78,7 +78,7 @@ OPTIONS
 -l | --files-with-matches | --name-only | -L | --files-without-match::
        Instead of showing every matched line, show only the
        names of files that contain (or do not contain) matches.
-       For better compatability with git-diff, --name-only is a
+       For better compatibility with git-diff, --name-only is a
        synonym for --files-with-matches.
 
 -c | --count::
index 72b5d001161f8a05e9c132b15409d7a504b0ced3..a7825b6144b67d486b5b81be0217b57db5197e65 100644 (file)
@@ -75,6 +75,9 @@ OPTIONS
        to force the version for the generated pack index, and to force
        64-bit index entries on objects located above the given offset.
 
+--strict::
+       Die, if the pack contains broken objects or links.
+
 
 Note
 ----
index 0c9ad7f2bbdf4872882d127804e7c474729c0818..c136b1069230af55b62ba95a1b40bdcc019b7b42 100644 (file)
@@ -68,7 +68,8 @@ HOW MERGE WORKS
 ---------------
 
 A merge is always between the current `HEAD` and one or more
-remote branch heads, and the index file must exactly match the
+commits (usually, branch head or tag), and the index file must
+exactly match the
 tree of `HEAD` commit (i.e. the contents of the last commit) when
 it happens.  In other words, `git-diff --cached HEAD` must
 report no changes.
index 50f106ec5b49b6d46f4be6fa012148b24d6e8801..8ed44947ef877d2f960b1506a39ab0c166cce514 100644 (file)
@@ -12,12 +12,12 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Use 'git mergetool' to run one of several merge utilities to resolve
+Use `git mergetool` to run one of several merge utilities to resolve
 merge conflicts.  It is typically run after linkgit:git-merge[1].
 
 If one or more <file> parameters are given, the merge tool program will
 be run to resolve differences on each file.  If no <file> names are
-specified, 'git mergetool' will run the merge tool program on every file
+specified, `git mergetool` will run the merge tool program on every file
 with merge conflicts.
 
 OPTIONS
@@ -27,16 +27,38 @@ OPTIONS
        Valid merge tools are:
        kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
 +
-If a merge resolution program is not specified, 'git mergetool'
-will use the configuration variable merge.tool.  If the
-configuration variable merge.tool is not set, 'git mergetool'
+If a merge resolution program is not specified, `git mergetool`
+will use the configuration variable `merge.tool`.  If the
+configuration variable `merge.tool` is not set, `git mergetool`
 will pick a suitable default.
 +
 You can explicitly provide a full path to the tool by setting the
-configuration variable mergetool.<tool>.path. For example, you
+configuration variable `mergetool.<tool>.path`. For example, you
 can configure the absolute path to kdiff3 by setting
-mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool
-is available in PATH.
+`mergetool.kdiff3.path`. Otherwise, `git mergetool` assumes the
+tool is available in PATH.
++
+Instead of running one of the known merge tool programs
+`git mergetool` can be customized to run an alternative program
+by specifying the command line to invoke in a configration
+variable `mergetool.<tool>.cmd`.
++
+When `git mergetool` is invoked with this tool (either through the
+`-t` or `--tool` option or the `merge.tool` configuration
+variable) the configured command line will be invoked with `$BASE`
+set to the name of a temporary file containing the common base for
+the merge, if available; `$LOCAL` set to the name of a temporary
+file containing the contents of the file on the current branch;
+`$REMOTE` set to the name of a temporary file containing the
+contents of the file to be merged, and `$MERGED` set to the name
+of the file to which the merge tool should write the result of the
+merge resolution.
++
+If the custom merge tool correctly indicates the success of a
+merge resolution with its exit code then the configuration
+variable `mergetool.<tool>.trustExitCode` can be set to `true`.
+Otherwise, `git mergetool` will prompt the user to indicate the
+success of the resolution after the custom tool has exited.
 
 Author
 ------
index 5c1bd3b0813a95ee0c1831a2e10f5512a9330793..eed0a94c6e4d6f520923a2ee86f1fcfbee228185 100644 (file)
@@ -73,6 +73,11 @@ base-name::
        as if all refs under `$GIT_DIR/refs` are specified to be
        included.
 
+--include-tag::
+       Include unasked-for annotated tags if the object they
+       reference was included in the resulting packfile.  This
+       can be useful to send new tags to native git clients.
+
 --window=[N], --depth=[N]::
        These two options affect how the objects contained in
        the pack are stored using delta compression.  The
index c11c6453ea5d6cc8d7a727e66dafc6e628ea5b2e..4b10304740f5035095c19be05859ae0b9205e9a0 100644 (file)
@@ -9,6 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
+       [-s <strategy> | --strategy=<strategy>]
        [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
        [--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
index f9bba36c2309d5b4a9587d0f4a406cf57fcd8348..047e3ce14dfac6f1848fa8f1772202c26efa1055 100644 (file)
@@ -19,6 +19,8 @@ depending on the subcommand:
 git reflog expire [--dry-run] [--stale-fix] [--verbose]
        [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
 
+git reflog delete ref@\{specifier\}...
+
 git reflog [show] [log-options] [<ref>]
 
 Reflog is a mechanism to record when the tip of branches are
@@ -43,6 +45,9 @@ two moves ago", `master@\{one.week.ago\}` means "where master used to
 point to one week ago", and so on. See linkgit:git-rev-parse[1] for
 more details.
 
+To delete single entries from the reflog, use the subcommand "delete"
+and specify the _exact_ entry (e.g. ``git reflog delete master@\{2\}'').
+
 
 OPTIONS
 -------
@@ -75,6 +80,15 @@ them.
 --all::
        Instead of listing <refs> explicitly, prune all refs.
 
+--updateref::
+       Update the ref with the sha1 of the top reflog entry (i.e.
+       <ref>@\{0\}) after expiring or deleting.
+
+--rewrite::
+       While expiring or deleting, adjust each reflog entry to ensure
+       that the `old` sha1 field points to the `new` sha1 field of the
+       previous entry.
+
 --verbose::
        Print extra information on screen.
 
index 48e6f5a3f7a85e886639381dfa70434ecdeccfc2..8dc35d493e81bc0c5515ec279dbe95c464c57469 100644 (file)
@@ -8,7 +8,7 @@ git-stash - Stash the changes in a dirty working directory away
 SYNOPSIS
 --------
 [verse]
-'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
+'git-stash' (list | show [<stash>] | apply [<stash>] | clear | drop [<stash>] | pop [<stash>])
 'git-stash' [save [<message>]]
 
 DESCRIPTION
@@ -85,6 +85,17 @@ clear::
        Remove all the stashed states. Note that those states will then
        be subject to pruning, and may be difficult or impossible to recover.
 
+drop [<stash>]::
+
+       Remove a single stashed state from the stash list. When no `<stash>`
+       is given, it removes the latest one. i.e. `stash@\{0}`
+
+pop [<stash>]::
+
+       Remove a single stashed state from the stash list and apply on top
+       of the current working tree state. When no `<stash>` is given,
+       `stash@\{0}` is assumed. See also `apply`.
+
 
 DISCUSSION
 ----------
index e818e6e7895a94af0b05f04d2c8ecdae64583efd..b4d01605a521279e298d9764ea90f25a8ee367d2 100644 (file)
@@ -18,8 +18,9 @@ COMMANDS
 --------
 add::
        Add the given repository as a submodule at the given path
-       to the changeset to be committed next.  In particular, the
-       repository is cloned at the specified path, added to the
+       to the changeset to be committed next.  If path is a valid
+       repository within the project, it is added as is. Otherwise,
+       repository is cloned at the specified path. path is added to the
        changeset and registered in .gitmodules.   If no path is
        specified, the path is deduced from the repository specification.
        If the repository url begins with ./ or ../, it is stored as
index db019a2b8d1bb283789726bf47ee0f7b9e4a523e..ba2a157299566b4dcc88f8ecb6f0eaed755f95bb 100644 (file)
@@ -32,11 +32,11 @@ OUTPUT FORMAT
 -------------
 When specifying the -v option the format used is:
 
-       SHA1 type size offset-in-packfile
+       SHA1 type size size-in-pack-file offset-in-packfile
 
 for objects that are not deltified in the pack, and
 
-       SHA1 type size offset-in-packfile depth base-SHA1
+       SHA1 type size size-in-packfile offset-in-packfile depth base-SHA1
 
 for objects that are deltified.
 
index 54947b676969585c4641bf7a4f538623e059dd15..a6e7bd4c8b99ad2d6a19acf4a412b81580ef3027 100644 (file)
@@ -38,11 +38,6 @@ OPTIONS
        Show git internal diff output, but for the whole tree,
        not just the top level.
 
---pretty=<format>::
-       Controls the output format for the commit logs.
-       <format> can be one of 'raw', 'medium', 'short', 'full',
-       and 'oneline'.
-
 -m::
        By default, differences for merge commits are not shown.
        With this flag, show differences to that commit from all
@@ -51,6 +46,10 @@ OPTIONS
 However, it is not very useful in general, although it
 *is* useful on a file-by-file basis.
 
+include::pretty-options.txt[]
+
+include::pretty-formats.txt[]
+
 Examples
 --------
 git-whatchanged -p v2.6.12.. include/scsi drivers/scsi::
index 741ae0e4c8ec9a3aaaef19430b0f76e4baedb546..3ed24d449a1f277c40dbb49654a196e38d1fa3e4 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.5.4.3/git.html[documentation for release 1.5.4.3]
+* link:v1.5.4.4/git.html[documentation for release 1.5.4.4]
 
 * release notes for
+  link:RelNotes-1.5.4.4.txt[1.5.4.4],
   link:RelNotes-1.5.4.3.txt[1.5.4.3],
   link:RelNotes-1.5.4.2.txt[1.5.4.2],
   link:RelNotes-1.5.4.1.txt[1.5.4.1],
index 973d8dd733f954abf93aac7c68efef1154e9f315..6d66c74cc11e6622892061f8328d04dfe38f87bf 100644 (file)
@@ -4,6 +4,9 @@
        where '<format>' can be one of 'oneline', 'short', 'medium',
        'full', 'fuller', 'email', 'raw' and 'format:<string>'.
        When omitted, the format defaults to 'medium'.
++
+Note: you can specify the default pretty format in the repository
+configuration (see linkgit:git-config[1]).
 
 --abbrev-commit::
        Instead of showing the full 40-byte hexadecimal commit object
index 259072c07883cf15db4162737a63a6506b7443b4..2648a550850bf07128e4a0a14c82860d6bad17b8 100644 (file)
@@ -130,9 +130,11 @@ limiting may be applied.
 
        Show commits older than a specific date.
 
+ifdef::git-rev-list[]
 --max-age='timestamp', --min-age='timestamp'::
 
        Limit the commits output to specified time range.
+endif::git-rev-list[]
 
 --author='pattern', --committer='pattern'::
 
index dfbf9ac5d06e32a7961afdd08910df6eccb912c7..c364a22c8f98f6fbb71d4059f7458ed0fa78ed43 100644 (file)
@@ -49,7 +49,7 @@ Functions
 
 `finish_async`::
 
-       Wait for the completeion of an asynchronous function that was
+       Wait for the completion of an asynchronous function that was
        started with start_async().
 
 
@@ -111,9 +111,10 @@ stderr as follows:
        .no_stdin, .no_stdout, .no_stderr: The respective channel is
                redirected to /dev/null.
 
-       .stdout_to_stderr: stdout of the child is redirected to the
-               parent's stderr (i.e. *not* to what .err or
-               .no_stderr specify).
+       .stdout_to_stderr: stdout of the child is redirected to its
+               stderr. This happens after stderr is itself redirected.
+               So stdout will follow stderr to wherever it is
+               redirected.
 
 To modify the environment of the sub-process, specify an array of
 string pointers (NULL terminated) in .env:
@@ -121,8 +122,8 @@ string pointers (NULL terminated) in .env:
 . If the string is of the form "VAR=value", i.e. it contains '='
   the variable is added to the child process's environment.
 
-. If the string does not contain '=', it names an environement
-  variable that will be removed from the child process's envionment.
+. If the string does not contain '=', it names an environment
+  variable that will be removed from the child process's environment.
 
 To specify a new initial working directory for the sub-process,
 specify it in the .dir member.
index c2f84a76f62171fe32ffe1770a10721f516f8169..7fbb81578264e47c83857e864e58b32c4f81a5fa 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,10 @@ all::
 
 # Define V=1 to have a more verbose compile.
 #
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+#
 # Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
 # when attempting to read from an fopen'ed directory.
 #
@@ -304,7 +308,8 @@ 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 \
        utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
-       mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
+       mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h fsck.h \
+       pack-revindex.h
 
 DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -319,7 +324,7 @@ LIB_OBJS = \
        patch-ids.o \
        object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
        sideband.o reachable.o reflog-walk.o \
-       quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
+       quote.o read-cache.o refs.o run-command.o dir.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
        revision.o pager.o tree-walk.o xdiff-interface.o \
@@ -328,7 +333,7 @@ LIB_OBJS = \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
        convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
        transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
-       alias.o
+       alias.o fsck.o pack-revindex.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
@@ -477,6 +482,7 @@ ifeq ($(uname_S),FreeBSD)
        NO_MEMMEM = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
+       DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@ -628,6 +634,10 @@ endif
 ifdef NO_C99_FORMAT
        BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
+ifdef SNPRINTF_RETURNS_BOGUS
+       COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
+       COMPAT_OBJS += compat/snprintf.o
+endif
 ifdef FREAD_READS_DIRECTORIES
        COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
        COMPAT_OBJS += compat/fopen.o
@@ -746,6 +756,9 @@ ifdef THREADED_DELTA_SEARCH
        EXTLIBS += -lpthread
        LIB_OBJS += thread-utils.o
 endif
+ifdef DIR_HAS_BSD_GROUP_SEMANTICS
+       COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
+endif
 
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
index 820110e085f20d8615c74167cf3ae4cfd1fe6912..4a91e3eb118850882fbc22e8d8f37e8bbfaa7617 100644 (file)
@@ -228,18 +228,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                goto finish;
        }
 
-       if (*argv) {
-               /* Was there an invalid path? */
-               if (pathspec) {
-                       int num;
-                       for (num = 0; pathspec[num]; num++)
-                               ; /* just counting */
-                       if (argc != num)
-                               exit(1); /* error message already given */
-               } else
-                       exit(1); /* error message already given */
-       }
-
        fill_directory(&dir, pathspec, ignored_too);
 
        if (show_only) {
index b0cd78856418319ed46b2e0cf372deffc050bb80..6b08016228d4d50cfd6863a11f33b2c13084a6eb 100644 (file)
@@ -484,11 +484,6 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        return post_checkout_hook(old.commit, new->commit, 1);
 }
 
-static int git_checkout_config(const char *var, const char *value)
-{
-       return git_default_config(var, value);
-}
-
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
        struct checkout_opts opts;
@@ -510,7 +505,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
 
-       git_config(git_checkout_config);
+       git_config(git_default_config);
 
        opts.track = git_branch_track;
 
index 3b220d5060b90318e2d2331d3cd0f5b6a70164ee..fefec3010c434219234e7832051ada9ed2e52335 100644 (file)
@@ -10,6 +10,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "quote.h"
 
 static int force = -1; /* unset */
 
@@ -34,7 +35,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        struct dir_struct dir;
        const char *path, *base;
        static const char **pathspec;
-       int prefix_offset = 0;
+       struct strbuf buf;
+       const char *qname;
        char *seen = NULL;
        struct option options[] = {
                OPT__QUIET(&quiet),
@@ -56,6 +58,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
 
+       strbuf_init(&buf, 0);
        memset(&dir, 0, sizeof(dir));
        if (ignored_only)
                dir.show_ignored = 1;
@@ -72,8 +75,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        if (!ignored)
                setup_standard_excludes(&dir);
 
-       if (prefix)
-               prefix_offset = strlen(prefix);
        pathspec = get_pathspec(prefix, argv);
        read_cache();
 
@@ -134,39 +135,34 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
                if (S_ISDIR(st.st_mode)) {
                        strbuf_addstr(&directory, ent->name);
+                       qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
                        if (show_only && (remove_directories || matches)) {
-                               printf("Would remove %s\n",
-                                      directory.buf + prefix_offset);
+                               printf("Would remove %s\n", qname);
                        } else if (remove_directories || matches) {
                                if (!quiet)
-                                       printf("Removing %s\n",
-                                              directory.buf + prefix_offset);
+                                       printf("Removing %s\n", qname);
                                if (remove_dir_recursively(&directory, 0) != 0) {
-                                       warning("failed to remove '%s'",
-                                               directory.buf + prefix_offset);
+                                       warning("failed to remove '%s'", qname);
                                        errors++;
                                }
                        } else if (show_only) {
-                               printf("Would not remove %s\n",
-                                      directory.buf + prefix_offset);
+                               printf("Would not remove %s\n", qname);
                        } else {
-                               printf("Not removing %s\n",
-                                      directory.buf + prefix_offset);
+                               printf("Not removing %s\n", qname);
                        }
                        strbuf_reset(&directory);
                } else {
                        if (pathspec && !matches)
                                continue;
+                       qname = quote_path_relative(ent->name, -1, &buf, prefix);
                        if (show_only) {
-                               printf("Would remove %s\n",
-                                      ent->name + prefix_offset);
+                               printf("Would remove %s\n", qname);
                                continue;
                        } else if (!quiet) {
-                               printf("Removing %s\n",
-                                      ent->name + prefix_offset);
+                               printf("Removing %s\n", qname);
                        }
                        if (unlink(ent->name) != 0) {
-                               warning("failed to remove '%s'", ent->name);
+                               warning("failed to remove '%s'", qname);
                                errors++;
                        }
                }
index 05e309f5ad15f9a6f85df34322bb59152b4e37be..df554b30af3ddcb4d0141838c06f82d433cbf3a4 100644 (file)
@@ -17,12 +17,16 @@ static const char * const describe_usage[] = {
 static int debug;      /* Display lots of verbose info */
 static int all;        /* Default to annotated tags only */
 static int tags;       /* But allow any tags if --tags is specified */
+static int longformat;
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
 const char *pattern = NULL;
+static int always;
 
 struct commit_name {
+       struct tag *tag;
        int prio; /* annotated tag = 2, tag = 1, head = 0 */
+       unsigned char sha1[20];
        char path[FLEX_ARRAY]; /* more */
 };
 static const char *prio_names[] = {
@@ -31,14 +35,17 @@ static const char *prio_names[] = {
 
 static void add_to_known_names(const char *path,
                               struct commit *commit,
-                              int prio)
+                              int prio,
+                              const unsigned char *sha1)
 {
        struct commit_name *e = commit->util;
        if (!e || e->prio < prio) {
                size_t len = strlen(path)+1;
                free(e);
                e = xmalloc(sizeof(struct commit_name) + len);
+               e->tag = NULL;
                e->prio = prio;
+               hashcpy(e->sha1, sha1);
                memcpy(e->path, path, len);
                commit->util = e;
        }
@@ -89,7 +96,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
                if (!tags && prio < 2)
                        return 0;
        }
-       add_to_known_names(all ? path + 5 : path + 10, commit, prio);
+       add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
        return 0;
 }
 
@@ -146,6 +153,27 @@ static unsigned long finish_depth_computation(
        return seen_commits;
 }
 
+static void display_name(struct commit_name *n)
+{
+       if (n->prio == 2 && !n->tag) {
+               n->tag = lookup_tag(n->sha1);
+               if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
+                       die("annotated tag %s not available", n->path);
+               if (strcmp(n->tag->tag, n->path))
+                       warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
+       }
+
+       if (n->tag)
+               printf("%s", n->tag->tag);
+       else
+               printf("%s", n->path);
+}
+
+static void show_suffix(int depth, const unsigned char *sha1)
+{
+       printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
+}
+
 static void describe(const char *arg, int last_one)
 {
        unsigned char sha1[20];
@@ -170,7 +198,13 @@ static void describe(const char *arg, int last_one)
 
        n = cmit->util;
        if (n) {
-               printf("%s\n", n->path);
+               /*
+                * Exact match to an existing ref.
+                */
+               display_name(n);
+               if (longformat)
+                       show_suffix(0, n->tag->tagged->sha1);
+               printf("\n");
                return;
        }
 
@@ -224,8 +258,14 @@ static void describe(const char *arg, int last_one)
                }
        }
 
-       if (!match_cnt)
-               die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+       if (!match_cnt) {
+               const unsigned char *sha1 = cmit->object.sha1;
+               if (always) {
+                       printf("%s\n", find_unique_abbrev(sha1, abbrev));
+                       return;
+               }
+               die("cannot describe '%s'", sha1_to_hex(sha1));
+       }
 
        qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
 
@@ -252,12 +292,11 @@ static void describe(const char *arg, int last_one)
                                sha1_to_hex(gave_up_on->object.sha1));
                }
        }
-       if (abbrev == 0)
-               printf("%s\n", all_matches[0].name->path );
-       else
-               printf("%s-%d-g%s\n", all_matches[0].name->path,
-                      all_matches[0].depth,
-                      find_unique_abbrev(cmit->object.sha1, abbrev));
+
+       display_name(all_matches[0].name);
+       if (abbrev)
+               show_suffix(all_matches[0].depth, cmit->object.sha1);
+       printf("\n");
 
        if (!last_one)
                clear_commit_marks(cmit, -1);
@@ -271,6 +310,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "debug",      &debug, "debug search strategy on stderr"),
                OPT_BOOLEAN(0, "all",        &all, "use any ref in .git/refs"),
                OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
+               OPT_BOOLEAN(0, "long",       &longformat, "always use long format"),
                OPT__ABBREV(&abbrev),
                OPT_SET_INT(0, "exact-match", &max_candidates,
                            "only output exact matches", 0),
@@ -278,6 +318,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                            "consider <n> most recent tags (default: 10)"),
                OPT_STRING(0, "match",       &pattern, "pattern",
                           "only consider tags matching <pattern>"),
+               OPT_BOOLEAN(0, "always",     &always,
+                          "show abbreviated commit object as fallback"),
                OPT_END(),
        };
 
@@ -289,12 +331,17 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 
        save_commit_buffer = 0;
 
+       if (longformat && abbrev == 0)
+               die("--long is incompatible with --abbrev=0");
+
        if (contains) {
-               const char **args = xmalloc((6 + argc) * sizeof(char*));
+               const char **args = xmalloc((7 + argc) * sizeof(char*));
                int i = 0;
                args[i++] = "name-rev";
                args[i++] = "--name-only";
                args[i++] = "--no-undefined";
+               if (always)
+                       args[i++] = "--always";
                if (!all) {
                        args[i++] = "--tags";
                        if (pattern) {
index 5ea48ca7dbc22785f4e4f3c35f3dc48ee18f0d8a..7b280242244094774c909f67d6a8ed7902389c5a 100644 (file)
@@ -18,7 +18,7 @@ static struct fetch_pack_args args = {
 };
 
 static const char fetch_pack_usage[] =
-"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
 
 #define COMPLETE       (1U << 0)
 #define COMMON         (1U << 1)
@@ -41,7 +41,8 @@ static void rev_list_push(struct commit *commit, int mark)
                commit->object.flags |= mark;
 
                if (!(commit->object.parsed))
-                       parse_commit(commit);
+                       if (parse_commit(commit))
+                               return;
 
                insert_by_date(commit, &rev_list);
 
@@ -83,7 +84,8 @@ static void mark_common(struct commit *commit,
                        if (!ancestors_only && !(o->flags & POPPED))
                                non_common_revs--;
                        if (!o->parsed && !dont_parse)
-                               parse_commit(commit);
+                               if (parse_commit(commit))
+                                       return;
 
                        for (parents = commit->parents;
                                        parents;
@@ -103,20 +105,20 @@ static const unsigned char* get_rev(void)
 
        while (commit == NULL) {
                unsigned int mark;
-               struct commit_list* parents;
+               struct commit_list *parents = NULL;
 
                if (rev_list == NULL || non_common_revs == 0)
                        return NULL;
 
                commit = rev_list->item;
                if (!(commit->object.parsed))
-                       parse_commit(commit);
+                       if (!parse_commit(commit))
+                               parents = commit->parents;
+
                commit->object.flags |= POPPED;
                if (!(commit->object.flags & COMMON))
                        non_common_revs--;
 
-               parents = commit->parents;
-
                if (commit->object.flags & COMMON) {
                        /* do not send "have", and ignore ancestors */
                        commit = NULL;
@@ -174,13 +176,14 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                }
 
                if (!fetching)
-                       packet_write(fd[1], "want %s%s%s%s%s%s%s\n",
+                       packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
                                     sha1_to_hex(remote),
                                     (multi_ack ? " multi_ack" : ""),
                                     (use_sideband == 2 ? " side-band-64k" : ""),
                                     (use_sideband == 1 ? " side-band" : ""),
                                     (args.use_thin_pack ? " thin-pack" : ""),
                                     (args.no_progress ? " no-progress" : ""),
+                                    (args.include_tag ? " include-tag" : ""),
                                     " ofs-delta");
                else
                        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -212,7 +215,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                                if (!lookup_object(sha1))
                                        die("object not found: %s", line);
                                /* make sure that it is parsed as shallow */
-                               parse_object(sha1);
+                               if (!parse_object(sha1))
+                                       die("error in object: %s", line);
                                if (unregister_shallow(sha1))
                                        die("no shallow found: %s", line);
                                continue;
@@ -386,7 +390,6 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
        int retval;
        unsigned long cutoff = 0;
 
-       track_object_refs = 0;
        save_commit_buffer = 0;
 
        for (ref = *refs; ref; ref = ref->next) {
@@ -681,6 +684,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                                args.use_thin_pack = 1;
                                continue;
                        }
+                       if (!strcmp("--include-tag", arg)) {
+                               args.include_tag = 1;
+                               continue;
+                       }
                        if (!strcmp("--all", arg)) {
                                args.fetch_all = 1;
                                continue;
index ac335f20feba9a9e68098c9e89d69cf61c1c3ed7..55f611e3c26e55df43eca1f40df74d335c850cc0 100644 (file)
@@ -101,6 +101,10 @@ static void add_merge_config(struct ref **head,
        }
 }
 
+static void find_non_local_tags(struct transport *transport,
+                       struct ref **head,
+                       struct ref ***tail);
+
 static struct ref *get_ref_map(struct transport *transport,
                               struct refspec *refs, int ref_count, int tags,
                               int *autotags)
@@ -157,8 +161,11 @@ static struct ref *get_ref_map(struct transport *transport,
                        if (!ref_map)
                                die("Couldn't find remote ref HEAD");
                        ref_map->merge = 1;
+                       tail = &ref_map->next;
                }
        }
+       if (tags == TAGS_DEFAULT && *autotags)
+               find_non_local_tags(transport, &ref_map, &tail);
        ref_remove_duplicates(ref_map);
 
        return ref_map;
@@ -452,18 +459,28 @@ static int add_existing(const char *refname, const unsigned char *sha1,
        return 0;
 }
 
-static struct ref *find_non_local_tags(struct transport *transport,
-                                      struct ref *fetch_map)
+static int will_fetch(struct ref **head, const unsigned char *sha1)
+{
+       struct ref *rm = *head;
+       while (rm) {
+               if (!hashcmp(rm->old_sha1, sha1))
+                       return 1;
+               rm = rm->next;
+       }
+       return 0;
+}
+
+static void find_non_local_tags(struct transport *transport,
+                       struct ref **head,
+                       struct ref ***tail)
 {
-       static struct path_list existing_refs = { NULL, 0, 0, 0 };
+       struct path_list existing_refs = { NULL, 0, 0, 0 };
        struct path_list new_refs = { NULL, 0, 0, 1 };
        char *ref_name;
        int ref_name_len;
        const unsigned char *ref_sha1;
        const struct ref *tag_ref;
        struct ref *rm = NULL;
-       struct ref *ref_map = NULL;
-       struct ref **tail = &ref_map;
        const struct ref *ref;
 
        for_each_ref(add_existing, &existing_refs);
@@ -489,7 +506,8 @@ static struct ref *find_non_local_tags(struct transport *transport,
 
                if (!path_list_has_path(&existing_refs, ref_name) &&
                    !path_list_has_path(&new_refs, ref_name) &&
-                   has_sha1_file(ref->old_sha1)) {
+                   (has_sha1_file(ref->old_sha1) ||
+                    will_fetch(head, ref->old_sha1))) {
                        path_list_insert(ref_name, &new_refs);
 
                        rm = alloc_ref(strlen(ref_name) + 1);
@@ -498,19 +516,19 @@ static struct ref *find_non_local_tags(struct transport *transport,
                        strcpy(rm->peer_ref->name, ref_name);
                        hashcpy(rm->old_sha1, ref_sha1);
 
-                       *tail = rm;
-                       tail = &rm->next;
+                       **tail = rm;
+                       *tail = &rm->next;
                }
                free(ref_name);
        }
-
-       return ref_map;
+       path_list_clear(&existing_refs, 0);
+       path_list_clear(&new_refs, 0);
 }
 
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
-       struct ref *ref_map, *fetch_map;
+       struct ref *ref_map;
        struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
        if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
@@ -537,26 +555,28 @@ static int do_fetch(struct transport *transport,
                        read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
        }
 
+       if (tags == TAGS_DEFAULT && autotags)
+               transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
        if (fetch_refs(transport, ref_map)) {
                free_refs(ref_map);
                return 1;
        }
-
-       fetch_map = ref_map;
+       free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
         * following ... */
        if (tags == TAGS_DEFAULT && autotags) {
-               ref_map = find_non_local_tags(transport, fetch_map);
+               struct ref **tail = &ref_map;
+               ref_map = NULL;
+               find_non_local_tags(transport, &ref_map, &tail);
                if (ref_map) {
+                       transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
                        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
                        fetch_refs(transport, ref_map);
                }
                free_refs(ref_map);
        }
 
-       free_refs(fetch_map);
-
        transport_disconnect(transport);
 
        return 0;
index cc7524be80f51bca45ef8a205900cf50135bfc5d..78a6e1ff7101f7bb616365e5d4c9d9cf7f8963ae 100644 (file)
@@ -8,6 +8,7 @@
 #include "pack.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
+#include "fsck.h"
 #include "parse-options.h"
 
 #define REACHABLE 0x0001
@@ -54,13 +55,75 @@ static int objerror(struct object *obj, const char *err, ...)
        return -1;
 }
 
-static int objwarning(struct object *obj, const char *err, ...)
+static int fsck_error_func(struct object *obj, int type, const char *err, ...)
 {
        va_list params;
        va_start(params, err);
-       objreport(obj, "warning", err, params);
+       objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
        va_end(params);
-       return -1;
+       return (type == FSCK_WARN) ? 0 : 1;
+}
+
+static int mark_object(struct object *obj, int type, void *data)
+{
+       struct tree *tree = NULL;
+       struct object *parent = data;
+       int result;
+
+       if (!obj) {
+               printf("broken link from %7s %s\n",
+                          typename(parent->type), sha1_to_hex(parent->sha1));
+               printf("broken link from %7s %s\n",
+                          (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
+               errors_found |= ERROR_REACHABLE;
+               return 1;
+       }
+
+       if (type != OBJ_ANY && obj->type != type)
+               objerror(parent, "wrong object type in link");
+
+       if (obj->flags & REACHABLE)
+               return 0;
+       obj->flags |= REACHABLE;
+       if (!obj->parsed) {
+               if (parent && !has_sha1_file(obj->sha1)) {
+                       printf("broken link from %7s %s\n",
+                                typename(parent->type), sha1_to_hex(parent->sha1));
+                       printf("              to %7s %s\n",
+                                typename(obj->type), sha1_to_hex(obj->sha1));
+                       errors_found |= ERROR_REACHABLE;
+               }
+               return 1;
+       }
+
+       if (obj->type == OBJ_TREE) {
+               obj->parsed = 0;
+               tree = (struct tree *)obj;
+               if (parse_tree(tree) < 0)
+                       return 1; /* error already displayed */
+       }
+       result = fsck_walk(obj, mark_object, obj);
+       if (tree) {
+               free(tree->buffer);
+               tree->buffer = NULL;
+       }
+       if (result < 0)
+               result = 1;
+
+       return result;
+}
+
+static void mark_object_reachable(struct object *obj)
+{
+       mark_object(obj, OBJ_ANY, 0);
+}
+
+static int mark_used(struct object *obj, int type, void *data)
+{
+       if (!obj)
+               return 1;
+       obj->used = 1;
+       return 0;
 }
 
 /*
@@ -68,8 +131,6 @@ static int objwarning(struct object *obj, const char *err, ...)
  */
 static void check_reachable_object(struct object *obj)
 {
-       const struct object_refs *refs;
-
        /*
         * We obviously want the object to be parsed,
         * except if it was in a pack-file and we didn't
@@ -82,25 +143,6 @@ static void check_reachable_object(struct object *obj)
                errors_found |= ERROR_REACHABLE;
                return;
        }
-
-       /*
-        * Check that everything that we try to reference is also good.
-        */
-       refs = lookup_object_refs(obj);
-       if (refs) {
-               unsigned j;
-               for (j = 0; j < refs->count; j++) {
-                       struct object *ref = refs->ref[j];
-                       if (ref->parsed ||
-                           (has_sha1_file(ref->sha1)))
-                               continue;
-                       printf("broken link from %7s %s\n",
-                              typename(obj->type), sha1_to_hex(obj->sha1));
-                       printf("              to %7s %s\n",
-                              typename(ref->type), sha1_to_hex(ref->sha1));
-                       errors_found |= ERROR_REACHABLE;
-               }
-       }
 }
 
 /*
@@ -204,230 +246,56 @@ static void check_connectivity(void)
        }
 }
 
-/*
- * The entries in a tree are ordered in the _path_ order,
- * which means that a directory entry is ordered by adding
- * a slash to the end of it.
- *
- * So a directory called "a" is ordered _after_ a file
- * called "a.c", because "a/" sorts after "a.c".
- */
-#define TREE_UNORDERED (-1)
-#define TREE_HAS_DUPS  (-2)
-
-static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
+static int fsck_sha1(const unsigned char *sha1)
 {
-       int len1 = strlen(name1);
-       int len2 = strlen(name2);
-       int len = len1 < len2 ? len1 : len2;
-       unsigned char c1, c2;
-       int cmp;
-
-       cmp = memcmp(name1, name2, len);
-       if (cmp < 0)
+       struct object *obj = parse_object(sha1);
+       if (!obj) {
+               errors_found |= ERROR_OBJECT;
+               return error("%s: object corrupt or missing",
+                            sha1_to_hex(sha1));
+       }
+       if (obj->flags & SEEN)
                return 0;
-       if (cmp > 0)
-               return TREE_UNORDERED;
-
-       /*
-        * Ok, the first <len> characters are the same.
-        * Now we need to order the next one, but turn
-        * a '\0' into a '/' for a directory entry.
-        */
-       c1 = name1[len];
-       c2 = name2[len];
-       if (!c1 && !c2)
-               /*
-                * git-write-tree used to write out a nonsense tree that has
-                * entries with the same name, one blob and one tree.  Make
-                * sure we do not have duplicate entries.
-                */
-               return TREE_HAS_DUPS;
-       if (!c1 && S_ISDIR(mode1))
-               c1 = '/';
-       if (!c2 && S_ISDIR(mode2))
-               c2 = '/';
-       return c1 < c2 ? 0 : TREE_UNORDERED;
-}
-
-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;
-       int not_properly_sorted = 0;
-       struct tree_desc desc;
-       unsigned o_mode;
-       const char *o_name;
-       const unsigned char *o_sha1;
+       obj->flags |= SEEN;
 
        if (verbose)
-               fprintf(stderr, "Checking tree %s\n",
-                               sha1_to_hex(item->object.sha1));
-
-       init_tree_desc(&desc, item->buffer, item->size);
-
-       o_mode = 0;
-       o_name = NULL;
-       o_sha1 = NULL;
-       while (desc.size) {
-               unsigned mode;
-               const char *name;
-               const unsigned char *sha1;
-
-               sha1 = tree_entry_extract(&desc, &name, &mode);
-
-               if (strchr(name, '/'))
-                       has_full_path = 1;
-               if (!*name)
-                       has_empty_name = 1;
-               has_zero_pad |= *(char *)desc.buffer == '0';
-               update_tree_entry(&desc);
-
-               switch (mode) {
-               /*
-                * Standard modes..
-                */
-               case S_IFREG | 0755:
-               case S_IFREG | 0644:
-               case S_IFLNK:
-               case S_IFDIR:
-               case S_IFGITLINK:
-                       break;
-               /*
-                * This is nonstandard, but we had a few of these
-                * early on when we honored the full set of mode
-                * bits..
-                */
-               case S_IFREG | 0664:
-                       if (!check_strict)
-                               break;
-               default:
-                       has_bad_modes = 1;
-               }
+               fprintf(stderr, "Checking %s %s\n",
+                       typename(obj->type), sha1_to_hex(obj->sha1));
 
-               if (o_name) {
-                       switch (verify_ordered(o_mode, o_name, mode, name)) {
-                       case TREE_UNORDERED:
-                               not_properly_sorted = 1;
-                               break;
-                       case TREE_HAS_DUPS:
-                               has_dup_entries = 1;
-                               break;
-                       default:
-                               break;
-                       }
-               }
+       if (fsck_walk(obj, mark_used, 0))
+               objerror(obj, "broken links");
+       if (fsck_object(obj, check_strict, fsck_error_func))
+               return -1;
 
-               o_mode = mode;
-               o_name = name;
-               o_sha1 = sha1;
-       }
-       free(item->buffer);
-       item->buffer = NULL;
+       if (obj->type == OBJ_TREE) {
+               struct tree *item = (struct tree *) obj;
 
-       retval = 0;
-       if (has_full_path) {
-               objwarning(&item->object, "contains full pathnames");
+               free(item->buffer);
+               item->buffer = NULL;
        }
-       if (has_empty_name) {
-               objwarning(&item->object, "contains empty pathname");
-       }
-       if (has_zero_pad) {
-               objwarning(&item->object, "contains zero-padded file modes");
-       }
-       if (has_bad_modes) {
-               objwarning(&item->object, "contains bad file modes");
-       }
-       if (has_dup_entries) {
-               retval = objerror(&item->object, "contains duplicate file entries");
-       }
-       if (not_properly_sorted) {
-               retval = objerror(&item->object, "not properly sorted");
-       }
-       return retval;
-}
 
-static int fsck_commit(struct commit *commit)
-{
-       char *buffer = commit->buffer;
-       unsigned char tree_sha1[20], sha1[20];
+       if (obj->type == OBJ_COMMIT) {
+               struct commit *commit = (struct commit *) obj;
 
-       if (verbose)
-               fprintf(stderr, "Checking commit %s\n",
-                       sha1_to_hex(commit->object.sha1));
-
-       if (!commit->date)
-               return objerror(&commit->object, "invalid author/committer line");
-
-       if (memcmp(buffer, "tree ", 5))
-               return objerror(&commit->object, "invalid format - expected 'tree' line");
-       if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
-               return objerror(&commit->object, "invalid 'tree' line format - bad sha1");
-       buffer += 46;
-       while (!memcmp(buffer, "parent ", 7)) {
-               if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n')
-                       return objerror(&commit->object, "invalid 'parent' line format - bad sha1");
-               buffer += 48;
-       }
-       if (memcmp(buffer, "author ", 7))
-               return objerror(&commit->object, "invalid format - expected 'author' line");
-       free(commit->buffer);
-       commit->buffer = NULL;
-       if (!commit->tree)
-               return objerror(&commit->object, "could not load commit's tree %s", tree_sha1);
-       if (!commit->parents && show_root)
-               printf("root %s\n", sha1_to_hex(commit->object.sha1));
-       return 0;
-}
+               free(commit->buffer);
+               commit->buffer = NULL;
 
-static int fsck_tag(struct tag *tag)
-{
-       struct object *tagged = tag->tagged;
+               if (!commit->parents && show_root)
+                       printf("root %s\n", sha1_to_hex(commit->object.sha1));
+       }
 
-       if (verbose)
-               fprintf(stderr, "Checking tag %s\n",
-                       sha1_to_hex(tag->object.sha1));
+       if (obj->type == OBJ_TAG) {
+               struct tag *tag = (struct tag *) obj;
 
-       if (!tagged) {
-               return objerror(&tag->object, "could not load tagged object");
+               if (show_tags && tag->tagged) {
+                       printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
+                       printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
+               }
        }
-       if (!show_tags)
-               return 0;
 
-       printf("tagged %s %s", typename(tagged->type), sha1_to_hex(tagged->sha1));
-       printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
        return 0;
 }
 
-static int fsck_sha1(const unsigned char *sha1)
-{
-       struct object *obj = parse_object(sha1);
-       if (!obj) {
-               errors_found |= ERROR_OBJECT;
-               return error("%s: object corrupt or missing",
-                            sha1_to_hex(sha1));
-       }
-       if (obj->flags & SEEN)
-               return 0;
-       obj->flags |= SEEN;
-       if (obj->type == OBJ_BLOB)
-               return 0;
-       if (obj->type == OBJ_TREE)
-               return fsck_tree((struct tree *) obj);
-       if (obj->type == OBJ_COMMIT)
-               return fsck_commit((struct commit *) obj);
-       if (obj->type == OBJ_TAG)
-               return fsck_tag((struct tag *) obj);
-
-       /* By now, parse_object() would've returned NULL instead. */
-       return objerror(obj, "unknown type '%d' (internal fsck error)",
-                       obj->type);
-}
-
 /*
  * This is the sorting chunk size: make it reasonably
  * big so that we can sort well..
@@ -538,13 +406,13 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
                obj = lookup_object(osha1);
                if (obj) {
                        obj->used = 1;
-                       mark_reachable(obj, REACHABLE);
+                       mark_object_reachable(obj);
                }
        }
        obj = lookup_object(nsha1);
        if (obj) {
                obj->used = 1;
-               mark_reachable(obj, REACHABLE);
+               mark_object_reachable(obj);
        }
        return 0;
 }
@@ -574,7 +442,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
                error("%s: not a commit", refname);
        default_refs++;
        obj->used = 1;
-       mark_reachable(obj, REACHABLE);
+       mark_object_reachable(obj);
 
        return 0;
 }
@@ -660,7 +528,7 @@ static int fsck_cache_tree(struct cache_tree *it)
                              sha1_to_hex(it->sha1));
                        return 1;
                }
-               mark_reachable(obj, REACHABLE);
+               mark_object_reachable(obj);
                obj->used = 1;
                if (obj->type != OBJ_TREE)
                        err |= objerror(obj, "non-tree in cache-tree");
@@ -693,7 +561,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 {
        int i, heads;
 
-       track_object_refs = 1;
        errors_found = 0;
 
        argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0);
@@ -741,7 +608,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                                continue;
 
                        obj->used = 1;
-                       mark_reachable(obj, REACHABLE);
+                       mark_object_reachable(obj);
                        heads++;
                        continue;
                }
@@ -773,7 +640,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                                continue;
                        obj = &blob->object;
                        obj->used = 1;
-                       mark_reachable(obj, REACHABLE);
+                       mark_object_reachable(obj);
                }
                if (active_cache_tree)
                        fsck_cache_tree(active_cache_tree);
index 299093ff9134f517e53b5105ec356e6575df6214..b1f33891c36fe38d855ace535cf9218fe9603541 100644 (file)
@@ -59,7 +59,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
                url = rewritten_url;
        }
 
-       walker = get_http_walker(url);
+       walker = get_http_walker(url, NULL);
        walker->get_tree = get_tree;
        walker->get_history = get_history;
        walker->get_all = get_all;
index bbadbc0de2fe1a2c85f3488420172a4db2ab6b4a..d983cbc7bc98f1b28332c18946e5111a6dcba9ab 100644 (file)
@@ -20,6 +20,7 @@
 
 static int default_show_root = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
+static const char *fmt_pretty;
 
 static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
 {
@@ -54,6 +55,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
+       if (fmt_pretty)
+               rev->commit_format = get_commit_format(fmt_pretty);
        rev->verbose_header = 1;
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
        rev->show_root_diff = default_show_root;
@@ -221,6 +224,8 @@ static int cmd_log_walk(struct rev_info *rev)
 
 static int git_log_config(const char *var, const char *value)
 {
+       if (!strcmp(var, "format.pretty"))
+               return git_config_string(&fmt_pretty, var, value);
        if (!strcmp(var, "format.subjectprefix")) {
                if (!value)
                        config_error_nonbool(var);
@@ -683,6 +688,10 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        strbuf_release(&sb);
 
        shortlog_init(&log);
+       log.wrap_lines = 1;
+       log.wrap = 72;
+       log.in1 = 2;
+       log.in2 = 4;
        for (i = 0; i < nr; i++)
                shortlog_add_commit(&log, list[i]);
 
@@ -694,8 +703,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        if (!origin)
                return;
 
-       diff_setup(&opts);
-       opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+       memcpy(&opts, &rev->diffopt, sizeof(opts));
+       opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
 
        diff_setup_done(&opts);
 
index 25dbfb44999566d0491b3f870a47f7df80aa7649..dc7eab89b34fed32dbb198a9aa9a7503fc162216 100644 (file)
@@ -574,17 +574,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        pathspec = get_pathspec(prefix, argv + i);
 
        /* Verify that the pathspec matches the prefix */
-       if (pathspec) {
-               if (argc != i) {
-                       int cnt;
-                       for (cnt = 0; pathspec[cnt]; cnt++)
-                               ;
-                       if (cnt != (argc - i))
-                               exit(1); /* error message already given */
-               }
+       if (pathspec)
                prefix = verify_pathspec(prefix);
-       } else if (argc != i)
-               exit(1); /* error message already given */
 
        /* Treat unmatching pathspec elements as errors */
        if (pathspec && error_unmatch) {
index 023754986e347a9529d6295d600f665f32f14577..8907a89d6c3914f4652ef387776f581015778396 100644 (file)
@@ -94,11 +94,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
 
        ref = transport_get_remote_refs(transport);
-       transport_disconnect(transport);
-
-       if (!ref)
+       if (transport_disconnect(transport))
                return 1;
-
        for ( ; ref; ref = ref->next) {
                if (!check_ref_type(ref, flags))
                        continue;
index 68aa2a68bb8f4232732829ba7d9459bbef3c7ad5..94f6dd2aad2da512bab0be2e84859eced6553cc9 100644 (file)
@@ -19,7 +19,6 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
                                  int count, int base_name)
 {
        int i;
-       int len = prefix ? strlen(prefix) : 0;
        const char **result = xmalloc((count + 1) * sizeof(const char *));
        memcpy(result, pathspec, count * sizeof(const char *));
        result[count] = NULL;
@@ -33,11 +32,8 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
                        if (last_slash)
                                result[i] = last_slash + 1;
                }
-               result[i] = prefix_path(prefix, len, result[i]);
-               if (!result[i])
-                       exit(1); /* error already given */
        }
-       return result;
+       return get_pathspec(prefix, result);
 }
 
 static void show_list(const char *label, struct path_list *list)
index f22c8b5f5da0031657f10ddb827d33a07056e3a5..384da4db13d9ff82b9bd45e6ab26b5dbf925dc22 100644 (file)
@@ -125,7 +125,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
 }
 
 /* returns a static buffer */
-static const char *get_rev_name(struct object *o)
+static const char *get_rev_name(const struct object *o)
 {
        static char buffer[1024];
        struct rev_name *n;
@@ -151,6 +151,26 @@ static const char *get_rev_name(struct object *o)
        }
 }
 
+static void show_name(const struct object *obj,
+                     const char *caller_name,
+                     int always, int allow_undefined, int name_only)
+{
+       const char *name;
+       const unsigned char *sha1 = obj->sha1;
+
+       if (!name_only)
+               printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
+       name = get_rev_name(obj);
+       if (name)
+               printf("%s\n", name);
+       else if (allow_undefined)
+               printf("undefined\n");
+       else if (always)
+               printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
+       else
+               die("cannot describe '%s'", sha1_to_hex(sha1));
+}
+
 static char const * const name_rev_usage[] = {
        "git-name-rev [options] ( --all | --stdin | <commit>... )",
        NULL
@@ -159,7 +179,7 @@ static char const * const name_rev_usage[] = {
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = { 0, 0, NULL };
-       int all = 0, transform_stdin = 0, allow_undefined = 1;
+       int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
                OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
@@ -170,6 +190,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
                OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
                OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
+               OPT_BOOLEAN(0, "always",     &always,
+                          "show abbreviated commit object as fallback"),
                OPT_END(),
        };
 
@@ -258,35 +280,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                int i, max;
 
                max = get_max_object_index();
-               for (i = 0; i < max; i++) {
-                       struct object * obj = get_indexed_object(i);
-                       const char *name;
-                       if (!obj)
-                               continue;
-                       if (!data.name_only)
-                               printf("%s ", sha1_to_hex(obj->sha1));
-                       name = get_rev_name(obj);
-                       if (name)
-                               printf("%s\n", name);
-                       else if (allow_undefined)
-                               printf("undefined\n");
-                       else
-                               die("cannot describe '%s'", sha1_to_hex(obj->sha1));
-               }
+               for (i = 0; i < max; i++)
+                       show_name(get_indexed_object(i), NULL,
+                                 always, allow_undefined, data.name_only);
        } else {
                int i;
-               for (i = 0; i < revs.nr; i++) {
-                       const char *name;
-                       if (!data.name_only)
-                               printf("%s ", revs.objects[i].name);
-                       name = get_rev_name(revs.objects[i].item);
-                       if (name)
-                               printf("%s\n", name);
-                       else if (allow_undefined)
-                               printf("undefined\n");
-                       else
-                               die("cannot describe '%s'", sha1_to_hex(revs.objects[i].item->sha1));
-               }
+               for (i = 0; i < revs.nr; i++)
+                       show_name(revs.objects[i].item, revs.objects[i].name,
+                                 always, allow_undefined, data.name_only);
        }
 
        return 0;
index 1bba6e6a64220ef2121ce8817ccae123739f4e8e..f504cff7566159332c89f830a871da0ea05868fe 100644 (file)
@@ -8,12 +8,14 @@
 #include "tree.h"
 #include "delta.h"
 #include "pack.h"
+#include "pack-revindex.h"
 #include "csum-file.h"
 #include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
 #include "progress.h"
+#include "refs.h"
 
 #ifdef THREADED_DELTA_SEARCH
 #include "thread-utils.h"
@@ -26,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
        [--window=N] [--window-memory=N] [--depth=N] \n\
        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
        [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
-       [--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
+       [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
+       [<ref-list | <object-list]";
 
 struct object_entry {
        struct pack_idx_entry idx;
@@ -62,7 +65,7 @@ static struct pack_idx_entry **written_list;
 static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
 
 static int non_empty;
-static int no_reuse_delta, no_reuse_object, keep_unreachable;
+static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
 static int local;
 static int incremental;
 static int allow_ofs_delta;
@@ -92,158 +95,12 @@ static unsigned long window_memory_limit = 0;
 static int *object_ix;
 static int object_ix_hashsz;
 
-/*
- * Pack index for existing packs give us easy access to the offsets into
- * corresponding pack file where each object's data starts, but the entries
- * do not store the size of the compressed representation (uncompressed
- * size is easily available by examining the pack entry header).  It is
- * also rather expensive to find the sha1 for an object given its offset.
- *
- * We build a hashtable of existing packs (pack_revindex), and keep reverse
- * index here -- pack index file is sorted by object name mapping to offset;
- * this pack_revindex[].revindex array is a list of offset/index_nr pairs
- * ordered by offset, so if you know the offset of an object, next offset
- * is where its packed representation ends and the index_nr can be used to
- * get the object sha1 from the main index.
- */
-struct revindex_entry {
-       off_t offset;
-       unsigned int nr;
-};
-struct pack_revindex {
-       struct packed_git *p;
-       struct revindex_entry *revindex;
-};
-static struct  pack_revindex *pack_revindex;
-static int pack_revindex_hashsz;
-
 /*
  * stats
  */
 static uint32_t written, written_delta;
 static uint32_t reused, reused_delta;
 
-static int pack_revindex_ix(struct packed_git *p)
-{
-       unsigned long ui = (unsigned long)p;
-       int i;
-
-       ui = ui ^ (ui >> 16); /* defeat structure alignment */
-       i = (int)(ui % pack_revindex_hashsz);
-       while (pack_revindex[i].p) {
-               if (pack_revindex[i].p == p)
-                       return i;
-               if (++i == pack_revindex_hashsz)
-                       i = 0;
-       }
-       return -1 - i;
-}
-
-static void prepare_pack_ix(void)
-{
-       int num;
-       struct packed_git *p;
-       for (num = 0, p = packed_git; p; p = p->next)
-               num++;
-       if (!num)
-               return;
-       pack_revindex_hashsz = num * 11;
-       pack_revindex = xcalloc(sizeof(*pack_revindex), pack_revindex_hashsz);
-       for (p = packed_git; p; p = p->next) {
-               num = pack_revindex_ix(p);
-               num = - 1 - num;
-               pack_revindex[num].p = p;
-       }
-       /* revindex elements are lazily initialized */
-}
-
-static int cmp_offset(const void *a_, const void *b_)
-{
-       const struct revindex_entry *a = a_;
-       const struct revindex_entry *b = b_;
-       return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
-}
-
-/*
- * Ordered list of offsets of objects in the pack.
- */
-static void prepare_pack_revindex(struct pack_revindex *rix)
-{
-       struct packed_git *p = rix->p;
-       int num_ent = p->num_objects;
-       int i;
-       const char *index = p->index_data;
-
-       rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
-       index += 4 * 256;
-
-       if (p->index_version > 1) {
-               const uint32_t *off_32 =
-                       (uint32_t *)(index + 8 + p->num_objects * (20 + 4));
-               const uint32_t *off_64 = off_32 + p->num_objects;
-               for (i = 0; i < num_ent; i++) {
-                       uint32_t off = ntohl(*off_32++);
-                       if (!(off & 0x80000000)) {
-                               rix->revindex[i].offset = off;
-                       } else {
-                               rix->revindex[i].offset =
-                                       ((uint64_t)ntohl(*off_64++)) << 32;
-                               rix->revindex[i].offset |=
-                                       ntohl(*off_64++);
-                       }
-                       rix->revindex[i].nr = i;
-               }
-       } else {
-               for (i = 0; i < num_ent; i++) {
-                       uint32_t hl = *((uint32_t *)(index + 24 * i));
-                       rix->revindex[i].offset = ntohl(hl);
-                       rix->revindex[i].nr = i;
-               }
-       }
-
-       /* This knows the pack format -- the 20-byte trailer
-        * follows immediately after the last object data.
-        */
-       rix->revindex[num_ent].offset = p->pack_size - 20;
-       rix->revindex[num_ent].nr = -1;
-       qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
-}
-
-static struct revindex_entry * find_packed_object(struct packed_git *p,
-                                                 off_t ofs)
-{
-       int num;
-       int lo, hi;
-       struct pack_revindex *rix;
-       struct revindex_entry *revindex;
-       num = pack_revindex_ix(p);
-       if (num < 0)
-               die("internal error: pack revindex uninitialized");
-       rix = &pack_revindex[num];
-       if (!rix->revindex)
-               prepare_pack_revindex(rix);
-       revindex = rix->revindex;
-       lo = 0;
-       hi = p->num_objects + 1;
-       do {
-               int mi = (lo + hi) / 2;
-               if (revindex[mi].offset == ofs) {
-                       return revindex + mi;
-               }
-               else if (ofs < revindex[mi].offset)
-                       hi = mi;
-               else
-                       lo = mi + 1;
-       } while (lo < hi);
-       die("internal error: pack revindex corrupt");
-}
-
-static const unsigned char *find_packed_object_name(struct packed_git *p,
-                                                   off_t ofs)
-{
-       struct revindex_entry *entry = find_packed_object(p, ofs);
-       return nth_packed_object_sha1(p, entry->nr);
-}
 
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
 {
@@ -510,7 +367,7 @@ static unsigned long write_object(struct sha1file *f,
                }
                hdrlen = encode_header(obj_type, entry->size, header);
                offset = entry->in_pack_offset;
-               revidx = find_packed_object(p, offset);
+               revidx = find_pack_revindex(p, offset);
                datalen = revidx[1].offset - offset;
                if (!pack_to_stdout && p->index_version > 1 &&
                    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
@@ -1162,8 +1019,11 @@ static void check_object(struct object_entry *entry)
                                die("delta base offset out of bound for %s",
                                    sha1_to_hex(entry->idx.sha1));
                        ofs = entry->in_pack_offset - ofs;
-                       if (!no_reuse_delta && !entry->preferred_base)
-                               base_ref = find_packed_object_name(p, ofs);
+                       if (!no_reuse_delta && !entry->preferred_base) {
+                               struct revindex_entry *revidx;
+                               revidx = find_pack_revindex(p, ofs);
+                               base_ref = nth_packed_object_sha1(p, revidx->nr);
+                       }
                        entry->in_pack_header_size = used + used_0;
                        break;
                }
@@ -1240,9 +1100,11 @@ static void get_object_details(void)
                sorted_by_offset[i] = objects + i;
        qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
 
-       prepare_pack_ix();
+       init_pack_revindex();
+
        for (i = 0; i < nr_objects; i++)
                check_object(sorted_by_offset[i]);
+
        free(sorted_by_offset);
 }
 
@@ -1770,6 +1632,18 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
 #define ll_find_deltas(l, s, w, d, p)  find_deltas(l, &s, w, d, p)
 #endif
 
+static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+       unsigned char peeled[20];
+
+       if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+           !peel_ref(path, peeled)        && /* peelable? */
+           !is_null_sha1(peeled)          && /* annotated tag? */
+           locate_object_entry(peeled))      /* object packed? */
+               add_object_entry(sha1, OBJ_TAG, NULL, 0);
+       return 0;
+}
+
 static void prepare_pack(int window, int depth)
 {
        struct object_entry **delta_list;
@@ -2013,7 +1887,6 @@ static void get_object_list(int ac, const char **av)
 
        init_revisions(&revs, NULL);
        save_commit_buffer = 0;
-       track_object_refs = 0;
        setup_revisions(ac, av, &revs, NULL);
 
        while (fgets(line, sizeof(line), stdin) != NULL) {
@@ -2174,6 +2047,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        keep_unreachable = 1;
                        continue;
                }
+               if (!strcmp("--include-tag", arg)) {
+                       include_tag = 1;
+                       continue;
+               }
                if (!strcmp("--unpacked", arg) ||
                    !prefixcmp(arg, "--unpacked=") ||
                    !strcmp("--reflog", arg) ||
@@ -2250,6 +2127,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                rp_av[rp_ac] = NULL;
                get_object_list(rp_ac, rp_av);
        }
+       if (include_tag && nr_result)
+               for_each_ref(add_ref_tag, NULL);
        stop_progress(&progress_state);
 
        if (non_empty && !nr_result)
index ab53c8cb7c08298c3ab0f69bf600a70385dafa64..280e24e1514f2989b571acc768d7067cbb223933 100644 (file)
@@ -14,6 +14,8 @@
 
 static const char reflog_expire_usage[] =
 "git-reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+static const char reflog_delete_usage[] =
+"git-reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
 
 static unsigned long default_reflog_expire;
 static unsigned long default_reflog_expire_unreachable;
@@ -22,9 +24,12 @@ struct cmd_reflog_expire_cb {
        struct rev_info revs;
        int dry_run;
        int stalefix;
+       int rewrite;
+       int updateref;
        int verbose;
        unsigned long expire_total;
        unsigned long expire_unreachable;
+       int recno;
 };
 
 struct expire_reflog_cb {
@@ -32,6 +37,7 @@ struct expire_reflog_cb {
        const char *ref;
        struct commit *ref_commit;
        struct cmd_reflog_expire_cb *cmd;
+       unsigned char last_kept_sha1[20];
 };
 
 struct collected_reflog {
@@ -213,6 +219,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
        if (timestamp < cb->cmd->expire_total)
                goto prune;
 
+       if (cb->cmd->rewrite)
+               osha1 = cb->last_kept_sha1;
+
        old = new = NULL;
        if (cb->cmd->stalefix &&
            (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
@@ -230,6 +239,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
                        goto prune;
        }
 
+       if (cb->cmd->recno && --(cb->cmd->recno) == 0)
+               goto prune;
+
        if (cb->newlog) {
                char sign = (tz < 0) ? '-' : '+';
                int zone = (tz < 0) ? (-tz) : tz;
@@ -237,6 +249,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
                        sha1_to_hex(osha1), sha1_to_hex(nsha1),
                        email, timestamp, sign, zone,
                        message);
+               hashcpy(cb->last_kept_sha1, nsha1);
        }
        if (cb->cmd->verbose)
                printf("keep %s", message);
@@ -280,10 +293,20 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
                        status |= error("%s: %s", strerror(errno),
                                        newlog_path);
                        unlink(newlog_path);
+               } else if (cmd->updateref &&
+                       (write_in_full(lock->lock_fd,
+                               sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+                        write_in_full(lock->lock_fd, "\n", 1) != 1 ||
+                        close_ref(lock) < 0)) {
+                       status |= error("Couldn't write %s",
+                               lock->lk->filename);
+                       unlink(newlog_path);
                } else if (rename(newlog_path, log_file)) {
                        status |= error("cannot rename %s to %s",
                                        newlog_path, log_file);
                        unlink(newlog_path);
+               } else if (cmd->updateref && commit_ref(lock)) {
+                       status |= error("Couldn't set %s", lock->ref_name);
                }
        }
        free(newlog_path);
@@ -358,6 +381,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                        cb.expire_unreachable = approxidate(arg + 21);
                else if (!strcmp(arg, "--stale-fix"))
                        cb.stalefix = 1;
+               else if (!strcmp(arg, "--rewrite"))
+                       cb.rewrite = 1;
+               else if (!strcmp(arg, "--updateref"))
+                       cb.updateref = 1;
                else if (!strcmp(arg, "--all"))
                        do_all = 1;
                else if (!strcmp(arg, "--verbose"))
@@ -406,6 +433,78 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        return status;
 }
 
+static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
+{
+       struct cmd_reflog_expire_cb *cb = cb_data;
+       if (!cb->expire_total || timestamp < cb->expire_total)
+               cb->recno++;
+       return 0;
+}
+
+static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
+{
+       struct cmd_reflog_expire_cb cb;
+       int i, status = 0;
+
+       memset(&cb, 0, sizeof(cb));
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+                       cb.dry_run = 1;
+               else if (!strcmp(arg, "--rewrite"))
+                       cb.rewrite = 1;
+               else if (!strcmp(arg, "--updateref"))
+                       cb.updateref = 1;
+               else if (!strcmp(arg, "--verbose"))
+                       cb.verbose = 1;
+               else if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               else if (arg[0] == '-')
+                       usage(reflog_delete_usage);
+               else
+                       break;
+       }
+
+       if (argc - i < 1)
+               return error("Nothing to delete?");
+
+       for ( ; i < argc; i++) {
+               const char *spec = strstr(argv[i], "@{");
+               unsigned char sha1[20];
+               char *ep, *ref;
+               int recno;
+
+               if (!spec) {
+                       status |= error("Not a reflog: %s", argv[i]);
+                       continue;
+               }
+
+               if (!dwim_ref(argv[i], spec - argv[i], sha1, &ref)) {
+                       status |= error("%s points nowhere!", argv[i]);
+                       continue;
+               }
+
+               recno = strtoul(spec + 2, &ep, 10);
+               if (*ep == '}') {
+                       cb.recno = -recno;
+                       for_each_reflog_ent(ref, count_reflog_ent, &cb);
+               } else {
+                       cb.expire_total = approxidate(spec + 2);
+                       for_each_reflog_ent(ref, count_reflog_ent, &cb);
+                       cb.expire_total = 0;
+               }
+
+               status |= expire_reflog(ref, sha1, 0, &cb);
+               free(ref);
+       }
+       return status;
+}
+
 /*
  * main "reflog"
  */
@@ -425,6 +524,9 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
        if (!strcmp(argv[1], "expire"))
                return cmd_reflog_expire(argc - 1, argv + 1, prefix);
 
+       if (!strcmp(argv[1], "delete"))
+               return cmd_reflog_delete(argc - 1, argv + 1, prefix);
+
        /* Not a recognized reflog command..*/
        usage(reflog_usage);
 }
index af0037ec6e3456ccd688da6375c76db4a59f099a..79424bb26efb4525b6bc2080e639b51d59eaabd9 100644 (file)
 #include "diffcore.h"
 #include "tree.h"
 #include "branch.h"
+#include "parse-options.h"
 
-static const char builtin_reset_usage[] =
-"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
+static const char * const git_reset_usage[] = {
+       "git-reset [--mixed | --soft | --hard] [-q] [<commit>]",
+       "git-reset [--mixed] <commit> [--] <paths>...",
+       NULL
+};
 
 static char *args_to_str(const char **argv)
 {
@@ -63,14 +67,10 @@ static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
 
 static void print_new_head_line(struct commit *commit)
 {
-       const char *hex, *dots = "...", *body;
+       const char *hex, *body;
 
        hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
-       if (!hex) {
-               hex = sha1_to_hex(commit->object.sha1);
-               dots = "";
-       }
-       printf("HEAD is now at %s%s", hex, dots);
+       printf("HEAD is now at %s", hex);
        body = strstr(commit->buffer, "\n\n");
        if (body) {
                const char *eol;
@@ -169,40 +169,31 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
-       int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0;
+       int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
        const char *rev = "HEAD";
        unsigned char sha1[20], *orig = NULL, sha1_orig[20],
                                *old_orig = NULL, sha1_old_orig[20];
        struct commit *commit;
        char *reflog_action, msg[1024];
+       const struct option options[] = {
+               OPT_SET_INT(0, "mixed", &reset_type,
+                                               "reset HEAD and index", MIXED),
+               OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
+               OPT_SET_INT(0, "hard", &reset_type,
+                               "reset HEAD, index and working tree", HARD),
+               OPT_BOOLEAN('q', NULL, &quiet,
+                               "disable showing new HEAD in hard reset"),
+               OPT_END()
+       };
 
        git_config(git_default_config);
 
+       argc = parse_options(argc, argv, options, git_reset_usage,
+                                               PARSE_OPT_KEEP_DASHDASH);
        reflog_action = args_to_str(argv);
        setenv("GIT_REFLOG_ACTION", reflog_action, 0);
 
-       while (i < argc) {
-               if (!strcmp(argv[i], "--mixed")) {
-                       reset_type = MIXED;
-                       i++;
-               }
-               else if (!strcmp(argv[i], "--soft")) {
-                       reset_type = SOFT;
-                       i++;
-               }
-               else if (!strcmp(argv[i], "--hard")) {
-                       reset_type = HARD;
-                       i++;
-               }
-               else if (!strcmp(argv[i], "-q")) {
-                       quiet = 1;
-                       i++;
-               }
-               else
-                       break;
-       }
-
-       if (i < argc && argv[i][0] != '-')
+       if (i < argc && strcmp(argv[i], "--"))
                rev = argv[i++];
 
        if (get_sha1(rev, sha1))
@@ -215,8 +206,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (i < argc && !strcmp(argv[i], "--"))
                i++;
-       else if (i < argc && argv[i][0] == '-')
-               usage(builtin_reset_usage);
 
        /* git reset tree [--] paths... can be used to
         * load chosen paths from the tree into the index without
index 6754e7f124cd3baa263d4b4f2c7e7be453260112..d0a14169211aa499c726a436de827ea4d328f6c2 100644 (file)
@@ -610,7 +610,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                usage(rev_list_usage);
 
        save_commit_buffer = revs.verbose_header || revs.grep_filter;
-       track_object_refs = 0;
        if (bisect_list)
                revs.limited = 1;
 
index b6dee6a56c21be816725a4366f448dac98e62ae6..607a2f0337c3d3f1fb8bdac7443e3a7f56e92305 100644 (file)
@@ -9,6 +9,8 @@
 #include "utf8.h"
 #include "parse-options.h"
 #include "cache-tree.h"
+#include "diff.h"
+#include "revision.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -246,6 +248,17 @@ static char *help_msg(const unsigned char *sha1)
        return helpbuf;
 }
 
+static int index_is_dirty(void)
+{
+       struct rev_info rev;
+       init_revisions(&rev, NULL);
+       setup_revisions(0, NULL, &rev, "HEAD");
+       DIFF_OPT_SET(&rev.diffopt, QUIET);
+       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
+       run_diff_index(&rev, 1);
+       return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+}
+
 static int revert_or_cherry_pick(int argc, const char **argv)
 {
        unsigned char head[20];
@@ -274,12 +287,11 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                if (write_cache_as_tree(head, 0, NULL))
                        die ("Your index file is unmerged.");
        } else {
-               struct wt_status s;
-
                if (get_sha1("HEAD", head))
                        die ("You do not have a valid HEAD");
-               wt_status_prepare(&s);
-               if (s.commitable)
+               if (read_cache() < 0)
+                       die("could not read the index");
+               if (index_is_dirty())
                        die ("Dirty index: cannot %s", me);
                discard_cache();
        }
index b0cfae83fc4dcdd8f58e29b87fd8aa4b6af0f6c0..930e0fb3fdfee2293d0fca650d7139f09f2d381e 100644 (file)
@@ -264,9 +264,7 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
 
 static const char *status_abbrev(unsigned char sha1[20])
 {
-       const char *abbrev;
-       abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
-       return abbrev ? abbrev : sha1_to_hex(sha1);
+       return find_unique_abbrev(sha1, DEFAULT_ABBREV);
 }
 
 static void print_ok_ref_status(struct ref *ref)
index af31abaaf862bb85a72e8052cf28a874d34c714a..b22b0edd65eefacabb648962b9c20c695609a3f5 100644 (file)
@@ -70,11 +70,12 @@ static void insert_one_record(struct shortlog *log,
        else
                free(buffer);
 
+       /* Skip any leading whitespace, including any blank lines. */
+       while (*oneline && isspace(*oneline))
+               oneline++;
        eol = strchr(oneline, '\n');
        if (!eol)
                eol = oneline + strlen(oneline);
-       while (*oneline && isspace(*oneline) && *oneline != '\n')
-               oneline++;
        if (!prefixcmp(oneline, "[PATCH")) {
                char *eob = strchr(oneline, ']');
                if (eob && (!eol || eob < eol))
index 1e51865c52231e80cfdbbb19c8b6fa86ee8855d2..50e07faa12f495b3270e1446573b88a255336877 100644 (file)
@@ -8,6 +8,7 @@
 #include "tag.h"
 #include "tree.h"
 #include "progress.h"
+#include "decorate.h"
 
 static int dry_run, quiet, recover, has_errors;
 static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
@@ -18,6 +19,18 @@ static unsigned int offset, len;
 static off_t consumed_bytes;
 static SHA_CTX ctx;
 
+struct obj_buffer {
+       char *buffer;
+       unsigned long size;
+};
+
+static struct decoration obj_decorate;
+
+static struct obj_buffer *lookup_object_buffer(struct object *base)
+{
+       return lookup_decoration(&obj_decorate, base);
+}
+
 /*
  * Make sure at least "min" bytes are available in the buffer, and
  * return the pointer to the buffer.
@@ -189,6 +202,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
        void *delta_data, *base;
        unsigned long base_size;
        unsigned char base_sha1[20];
+       struct object *obj;
 
        if (type == OBJ_REF_DELTA) {
                hashcpy(base_sha1, fill(20));
@@ -252,6 +266,15 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                }
        }
 
+       obj = lookup_object(base_sha1);
+       if (obj) {
+               struct obj_buffer *obj_buf = lookup_object_buffer(obj);
+               if (obj_buf) {
+                       resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size);
+                       return;
+               }
+       }
+
        base = read_sha1_file(base_sha1, &type, &base_size);
        if (!base) {
                error("failed to read delta-pack base object %s",
index 4e31c273f48e3983aaf99dc6525982d34b6fed06..4958bbbf11f5f796feedfa7480b827029f912d01 100644 (file)
@@ -40,8 +40,8 @@ static int verify_one_pack(const char *path, int verbose)
        if (!pack)
                return error("packfile %s not found.", arg);
 
+       install_packed_git(pack);
        err = verify_pack(pack, verbose);
-       free(pack);
 
        return err;
 }
diff --git a/cache.h b/cache.h
index f16d341f5233b154c6fc1518ae0bd619c8f05c38..e23030262328761669f4704a3e3731a5e93d5b0c 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -274,6 +274,7 @@ enum object_type {
        /* 5 for future expansion */
        OBJ_OFS_DELTA = 6,
        OBJ_REF_DELTA = 7,
+       OBJ_ANY,
        OBJ_MAX,
 };
 
index 22ce7768639465f896e52b08a73cf5605940700b..94d5b3d2618d335585084170ef6a3a8e67f34c62 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -193,7 +193,7 @@ static void prepare_commit_graft(void)
        commit_graft_prepared = 1;
 }
 
-static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
+struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
 {
        int pos;
        prepare_commit_graft();
@@ -290,17 +290,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
        }
        item->date = parse_commit_date(bufptr, tail);
 
-       if (track_object_refs) {
-               unsigned i = 0;
-               struct commit_list *p;
-               struct object_refs *refs = alloc_object_refs(n_refs);
-               if (item->tree)
-                       refs->ref[i++] = &item->tree->object;
-               for (p = item->parents; p; p = p->next)
-                       refs->ref[i++] = &p->item->object;
-               set_object_refs(&item->object, refs);
-       }
-
        return 0;
 }
 
index 80d65b96dafe33b128e4444d81046360eeb105af..a1e95914263355e2021f54be4837ed25f1210757 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -116,6 +116,7 @@ struct commit_graft {
 struct commit_graft *read_graft_line(char *buf, int len);
 int register_commit_graft(struct commit_graft *, int);
 int read_graft_file(const char *graft_file);
+struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
 
diff --git a/compat/snprintf.c b/compat/snprintf.c
new file mode 100644 (file)
index 0000000..dbfc2d6
--- /dev/null
@@ -0,0 +1,40 @@
+#include "../git-compat-util.h"
+
+#undef vsnprintf
+int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
+{
+       char *s;
+       int ret;
+
+       ret = vsnprintf(str, maxsize, format, ap);
+       if (ret != -1)
+               return ret;
+
+       s = NULL;
+       if (maxsize < 128)
+               maxsize = 128;
+
+       while (ret == -1) {
+               maxsize *= 4;
+               str = realloc(s, maxsize);
+               if (! str)
+                       break;
+               s = str;
+               ret = vsnprintf(str, maxsize, format, ap);
+       }
+       free(s);
+       return ret;
+}
+
+int git_snprintf(char *str, size_t maxsize, const char *format, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, format);
+       ret = git_vsnprintf(str, maxsize, format, ap);
+       va_end(ap);
+
+       return ret;
+}
+
index ee6c33df0385291623b900ac2b2cc088011b764d..8e1cd5f03de41ca929b6575ea8ed46e6bcfe2e7e 100644 (file)
@@ -46,3 +46,4 @@ NO_MKDTEMP=@NO_MKDTEMP@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
index 85d7ef570d390d56d3ec3ba8af406eee30d700ea..287149d304b8a631d879bd3be7b059c6f074e5fe 100644 (file)
@@ -326,6 +326,40 @@ else
        NO_C99_FORMAT=
 fi
 AC_SUBST(NO_C99_FORMAT)
+#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+AC_CACHE_CHECK([whether snprintf() and/or vsnprintf() return bogus value],
+ [ac_cv_snprintf_returns_bogus],
+[
+AC_RUN_IFELSE(
+       [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+               #include "stdarg.h"
+
+               int test_vsnprintf(char *str, size_t maxsize, const char *format, ...)
+               {
+                 int ret;
+                 va_list ap;
+                 va_start(ap, format);
+                 ret = vsnprintf(str, maxsize, format, ap);
+                 va_end(ap);
+                 return ret;
+               }],
+               [[char buf[6];
+                 if (test_vsnprintf(buf, 3, "%s", "12345") != 5
+                     || strcmp(buf, "12")) return 1;
+                 if (snprintf(buf, 3, "%s", "12345") != 5
+                     || strcmp(buf, "12")) return 1]])],
+       [ac_cv_snprintf_returns_bogus=no],
+       [ac_cv_snprintf_returns_bogus=yes])
+])
+if test $ac_cv_snprintf_returns_bogus = yes; then
+       SNPRINTF_RETURNS_BOGUS=UnfortunatelyYes
+else
+       SNPRINTF_RETURNS_BOGUS=
+fi
+AC_SUBST(SNPRINTF_RETURNS_BOGUS)
 
 
 ## Checks for library functions.
index 8f70e1efc11553ce2360fab8693e2749a3fc36b4..848c067b57398154de6dc5287648b4103841217a 100755 (executable)
@@ -70,7 +70,15 @@ __git_ps1 ()
                local b
                if [ -d "$g/../.dotest" ]
                then
-                       r="|AM/REBASE"
+                       if test -f "$g/../.dotest/rebasing"
+                       then
+                               r="|REBASE"
+                       elif test -f "$g/../.dotest/applying"
+                       then
+                               r="|AM"
+                       else
+                               r="|AM/REBASE"
+                       fi
                        b="$(git symbolic-ref HEAD 2>/dev/null)"
                elif [ -f "$g/.dotest-merge/interactive" ]
                then
@@ -506,7 +514,33 @@ _git_bisect ()
 
 _git_branch ()
 {
-       __gitcomp "$(__git_refs)"
+       local i c=1 only_local_ref="n" has_r="n"
+
+       while [ $c -lt $COMP_CWORD ]; do
+               i="${COMP_WORDS[c]}"
+               case "$i" in
+               -d|-m)  only_local_ref="y" ;;
+               -r)     has_r="y" ;;
+               esac
+               c=$((++c))
+       done
+
+       case "${COMP_WORDS[COMP_CWORD]}" in
+       --*=*)  COMPREPLY=() ;;
+       --*)
+               __gitcomp "
+                       --color --no-color --verbose --abbrev= --no-abbrev
+                       --track --no-track
+                       "
+               ;;
+       *)
+               if [ $only_local_ref = "y" -a $has_r = "n" ]; then
+                       __gitcomp "$(__git_heads)"
+               else
+                       __gitcomp "$(__git_refs)"
+               fi
+               ;;
+       esac
 }
 
 _git_bundle ()
index c9268234a5b7342849a7a711e0bb0c6e4f003754..4fa853fae76dc2ac132e489f5f1b630b3fc0f1de 100644 (file)
@@ -1299,7 +1299,7 @@ Return the list of files that haven't been handled."
   (let (author-name author-email subject date msg)
     (with-temp-buffer
       (let ((coding-system (git-get-logoutput-coding-system)))
-        (git-call-process-env t nil "log" "-1" commit)
+        (git-call-process-env t nil "log" "-1" "--pretty=medium" commit)
         (goto-char (point-min))
         (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
           (setq author-name (match-string 1))
index be96600753fa3a963ea5a260e0b0f23b4ff1ed16..650ea341762650cc9a6b517322d9ee3d7aa3b604 100755 (executable)
@@ -850,29 +850,32 @@ class P4Sync(Command):
 
     ## Should move this out, doesn't use SELF.
     def readP4Files(self, files):
+        filesForCommit = []
+        filesToRead = []
+
         for f in files:
+            includeFile = True
             for val in self.clientSpecDirs:
                 if f['path'].startswith(val[0]):
-                    if val[1] > 0:
-                        f['include'] = True
-                    else:
-                        f['include'] = False
+                    if val[1] <= 0:
+                        includeFile = False
                     break
 
-        files = [f for f in files
-                 if f['action'] != 'delete' and
-                 (f.has_key('include') == False or f['include'] == True)]
+            if includeFile:
+                filesForCommit.append(f)
+                if f['action'] != 'delete':
+                    filesToRead.append(f)
 
-        if not files:
-            return []
+        filedata = []
+        if len(filesToRead) > 0:
+            filedata = p4CmdList('-x - print',
+                                 stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
+                                                  for f in filesToRead]),
+                                 stdin_mode='w+')
 
-        filedata = p4CmdList('-x - print',
-                             stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
-                                              for f in files]),
-                             stdin_mode='w+')
-        if "p4ExitCode" in filedata[0]:
-            die("Problems executing p4. Error: [%d]."
-                % (filedata[0]['p4ExitCode']));
+            if "p4ExitCode" in filedata[0]:
+                die("Problems executing p4. Error: [%d]."
+                    % (filedata[0]['p4ExitCode']));
 
         j = 0;
         contents = {}
@@ -896,10 +899,12 @@ class P4Sync(Command):
 
             contents[stat['depotFile']] = text
 
-        for f in files:
-            assert not f.has_key('data')
-            f['data'] = contents[f['path']]
-        return files
+        for f in filesForCommit:
+            path = f['path']
+            if contents.has_key(path):
+                f['data'] = contents[path]
+
+        return filesForCommit
 
     def commit(self, details, files, branch, branchPrefixes, parent = ""):
         epoch = details["time"]
index 77c88ebf1f1029083614c2ff63011bd1b2d269fb..62a740c48248d94e1e0577c8676b13b7a726f21c 100644 (file)
@@ -567,7 +567,7 @@ generate_general_email()
        echo ""
        if [ "$newrev_type" = "commit" ]; then
                echo $LOGBEGIN
-               git show --no-color --root -s $newrev
+               git show --no-color --root -s --pretty=medium $newrev
                echo $LOGEND
        else
                # What can we do here?  The tag marks an object that is not
index aab501ea08129cc3b8304fb8f76f206578273f85..f4a7b62cd9f1a397118b95792c04c2f70f910f9e 100755 (executable)
@@ -93,7 +93,7 @@
 my @depths;
 
 while (<STDIN>) {
-    my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_);
+    my ($sha1, $type, $size, $space, $offset, $depth, $parent) = split(/\s+/, $_);
     next unless ($sha1 =~ /^[0-9a-f]{40}$/);
     $depths{$sha1} = $depth || 0;
     push(@depths, $depth || 0);
index 41a60af624ae661e9bd96a935ecdc855625b669e..2b4a6f145cc0f37c95da31df23dfde3ba7fa63a7 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1149,6 +1149,11 @@ int main(int argc, char **argv)
                usage(daemon_usage);
        }
 
+       if (log_syslog) {
+               openlog("git-daemon", 0, LOG_DAEMON);
+               set_die_routine(daemon_die);
+       }
+
        if (inetd_mode && (group_name || user_name))
                die("--user and --group are incompatible with --inetd");
 
@@ -1176,14 +1181,17 @@ int main(int argc, char **argv)
                }
        }
 
-       if (log_syslog) {
-               openlog("git-daemon", 0, LOG_DAEMON);
-               set_die_routine(daemon_die);
-       }
-
        if (strict_paths && (!ok_paths || !*ok_paths))
                die("option --strict-paths requires a whitelist");
 
+       if (base_path) {
+               struct stat st;
+
+               if (stat(base_path, &st) || !S_ISDIR(st.st_mode))
+                       die("base-path '%s' does not exist or "
+                           "is not a directory", base_path);
+       }
+
        if (inetd_mode) {
                struct sockaddr_storage ss;
                struct sockaddr *peer = (struct sockaddr *)&ss;
index 94b150e830c5d9bd828a5e050166eed27e0b0e62..4581b594d0a65d5bbe782b1db5d69f33189a0e50 100644 (file)
@@ -472,22 +472,21 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 static void diff_index_show_file(struct rev_info *revs,
                                 const char *prefix,
                                 struct cache_entry *ce,
-                                unsigned char *sha1, unsigned int mode)
+                                const unsigned char *sha1, unsigned int mode)
 {
        diff_addremove(&revs->diffopt, prefix[0], mode,
                       sha1, ce->name, NULL);
 }
 
 static int get_stat_data(struct cache_entry *ce,
-                        unsigned char **sha1p,
+                        const unsigned char **sha1p,
                         unsigned int *modep,
                         int cached, int match_missing)
 {
-       unsigned char *sha1 = ce->sha1;
+       const unsigned char *sha1 = ce->sha1;
        unsigned int mode = ce->ce_mode;
 
        if (!cached) {
-               static unsigned char no_sha1[20];
                int changed;
                struct stat st;
                if (lstat(ce->name, &st) < 0) {
@@ -501,7 +500,7 @@ static int get_stat_data(struct cache_entry *ce,
                changed = ce_match_stat(ce, &st, 0);
                if (changed) {
                        mode = ce_mode_from_stat(ce, st.st_mode);
-                       sha1 = no_sha1;
+                       sha1 = null_sha1;
                }
        }
 
@@ -514,7 +513,7 @@ static void show_new_file(struct rev_info *revs,
                          struct cache_entry *new,
                          int cached, int match_missing)
 {
-       unsigned char *sha1;
+       const unsigned char *sha1;
        unsigned int mode;
 
        /* New file in the index: it might actually be different in
@@ -533,7 +532,7 @@ static int show_modified(struct rev_info *revs,
                         int cached, int match_missing)
 {
        unsigned int mode, oldmode;
-       unsigned char *sha1;
+       const unsigned char *sha1;
 
        if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
                if (report_missing)
diff --git a/diff.c b/diff.c
index ad16164232f22207c2aa52c3b8c6ed4dcc3067b1..00e1590c6eec291c2a6b3f831b62f402768c7042 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2581,8 +2581,6 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
                return sha1_to_hex(sha1);
 
        abbrev = find_unique_abbrev(sha1, len);
-       if (!abbrev)
-               return sha1_to_hex(sha1);
        abblen = strlen(abbrev);
        if (abblen < 37) {
                static char hex[41];
@@ -3189,11 +3187,8 @@ static void diffcore_apply_filter(const char *filter)
 static int diff_filespec_is_identical(struct diff_filespec *one,
                                      struct diff_filespec *two)
 {
-       if (S_ISGITLINK(one->mode)) {
-               diff_fill_sha1_info(one);
-               diff_fill_sha1_info(two);
-               return !hashcmp(one->sha1, two->sha1);
-       }
+       if (S_ISGITLINK(one->mode))
+               return 0;
        if (diff_populate_filespec(one, 0))
                return 0;
        if (diff_populate_filespec(two, 0))
index 0d3449f2cef93ae0ece87f4204d5e817d6e62439..655913ddb27f758ce494fc18551e852c9a6aa9f1 100644 (file)
@@ -2291,7 +2291,8 @@ static void cmd_reset_branch(void)
        else
                b = new_branch(sp);
        read_next_command();
-       if (!cmd_from(b) && command_buf.len > 0)
+       cmd_from(b);
+       if (command_buf.len > 0)
                unread_command_buf = 1;
 }
 
@@ -2377,6 +2378,7 @@ int main(int argc, const char **argv)
 {
        unsigned int i, show_stats = 1;
 
+       setup_git_directory();
        git_config(git_pack_config);
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
index 8d35ef60bf6d975939362a9c01ece4026140f8c9..8bd9c32561e79d194d27fa10cc98a26aa2cb673c 100644 (file)
@@ -12,7 +12,8 @@ struct fetch_pack_args
                use_thin_pack:1,
                fetch_all:1,
                verbose:1,
-               no_progress:1;
+               no_progress:1,
+               include_tag:1;
 };
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
diff --git a/fsck.c b/fsck.c
new file mode 100644 (file)
index 0000000..797e317
--- /dev/null
+++ b/fsck.c
@@ -0,0 +1,333 @@
+#include "cache.h"
+#include "object.h"
+#include "blob.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "commit.h"
+#include "tag.h"
+#include "fsck.h"
+
+static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)
+{
+       struct tree_desc desc;
+       struct name_entry entry;
+       int res = 0;
+
+       if (parse_tree(tree))
+               return -1;
+
+       init_tree_desc(&desc, tree->buffer, tree->size);
+       while (tree_entry(&desc, &entry)) {
+               int result;
+
+               if (S_ISGITLINK(entry.mode))
+                       continue;
+               if (S_ISDIR(entry.mode))
+                       result = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);
+               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
+                       result = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);
+               else {
+                       result = error("in tree %s: entry %s has bad mode %.6o\n",
+                                       sha1_to_hex(tree->object.sha1), entry.path, entry.mode);
+               }
+               if (result < 0)
+                       return result;
+               if (!res)
+                       res = result;
+       }
+       return res;
+}
+
+static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)
+{
+       struct commit_list *parents;
+       int res;
+       int result;
+
+       if (parse_commit(commit))
+               return -1;
+
+       result = walk((struct object *)commit->tree, OBJ_TREE, data);
+       if (result < 0)
+               return result;
+       res = result;
+
+       parents = commit->parents;
+       while (parents) {
+               result = walk((struct object *)parents->item, OBJ_COMMIT, data);
+               if (result < 0)
+                       return result;
+               if (!res)
+                       res = result;
+               parents = parents->next;
+       }
+       return res;
+}
+
+static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)
+{
+       if (parse_tag(tag))
+               return -1;
+       return walk(tag->tagged, OBJ_ANY, data);
+}
+
+int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)
+{
+       if (!obj)
+               return -1;
+       switch (obj->type) {
+       case OBJ_BLOB:
+               return 0;
+       case OBJ_TREE:
+               return fsck_walk_tree((struct tree *)obj, walk, data);
+       case OBJ_COMMIT:
+               return fsck_walk_commit((struct commit *)obj, walk, data);
+       case OBJ_TAG:
+               return fsck_walk_tag((struct tag *)obj, walk, data);
+       default:
+               error("Unknown object type for %s", sha1_to_hex(obj->sha1));
+               return -1;
+       }
+}
+
+/*
+ * The entries in a tree are ordered in the _path_ order,
+ * which means that a directory entry is ordered by adding
+ * a slash to the end of it.
+ *
+ * So a directory called "a" is ordered _after_ a file
+ * called "a.c", because "a/" sorts after "a.c".
+ */
+#define TREE_UNORDERED (-1)
+#define TREE_HAS_DUPS  (-2)
+
+static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
+{
+       int len1 = strlen(name1);
+       int len2 = strlen(name2);
+       int len = len1 < len2 ? len1 : len2;
+       unsigned char c1, c2;
+       int cmp;
+
+       cmp = memcmp(name1, name2, len);
+       if (cmp < 0)
+               return 0;
+       if (cmp > 0)
+               return TREE_UNORDERED;
+
+       /*
+        * Ok, the first <len> characters are the same.
+        * Now we need to order the next one, but turn
+        * a '\0' into a '/' for a directory entry.
+        */
+       c1 = name1[len];
+       c2 = name2[len];
+       if (!c1 && !c2)
+               /*
+                * git-write-tree used to write out a nonsense tree that has
+                * entries with the same name, one blob and one tree.  Make
+                * sure we do not have duplicate entries.
+                */
+               return TREE_HAS_DUPS;
+       if (!c1 && S_ISDIR(mode1))
+               c1 = '/';
+       if (!c2 && S_ISDIR(mode2))
+               c2 = '/';
+       return c1 < c2 ? 0 : TREE_UNORDERED;
+}
+
+static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
+{
+       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;
+       int not_properly_sorted = 0;
+       struct tree_desc desc;
+       unsigned o_mode;
+       const char *o_name;
+       const unsigned char *o_sha1;
+
+       init_tree_desc(&desc, item->buffer, item->size);
+
+       o_mode = 0;
+       o_name = NULL;
+       o_sha1 = NULL;
+
+       while (desc.size) {
+               unsigned mode;
+               const char *name;
+               const unsigned char *sha1;
+
+               sha1 = tree_entry_extract(&desc, &name, &mode);
+
+               if (strchr(name, '/'))
+                       has_full_path = 1;
+               if (!*name)
+                       has_empty_name = 1;
+               has_zero_pad |= *(char *)desc.buffer == '0';
+               update_tree_entry(&desc);
+
+               switch (mode) {
+               /*
+                * Standard modes..
+                */
+               case S_IFREG | 0755:
+               case S_IFREG | 0644:
+               case S_IFLNK:
+               case S_IFDIR:
+               case S_IFGITLINK:
+                       break;
+               /*
+                * This is nonstandard, but we had a few of these
+                * early on when we honored the full set of mode
+                * bits..
+                */
+               case S_IFREG | 0664:
+                       if (!strict)
+                               break;
+               default:
+                       has_bad_modes = 1;
+               }
+
+               if (o_name) {
+                       switch (verify_ordered(o_mode, o_name, mode, name)) {
+                       case TREE_UNORDERED:
+                               not_properly_sorted = 1;
+                               break;
+                       case TREE_HAS_DUPS:
+                               has_dup_entries = 1;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               o_mode = mode;
+               o_name = name;
+               o_sha1 = sha1;
+       }
+
+       retval = 0;
+       if (has_full_path)
+               retval += error_func(&item->object, FSCK_WARN, "contains full pathnames");
+       if (has_empty_name)
+               retval += error_func(&item->object, FSCK_WARN, "contains empty pathname");
+       if (has_zero_pad)
+               retval += error_func(&item->object, FSCK_WARN, "contains zero-padded file modes");
+       if (has_bad_modes)
+               retval += error_func(&item->object, FSCK_WARN, "contains bad file modes");
+       if (has_dup_entries)
+               retval += error_func(&item->object, FSCK_ERROR, "contains duplicate file entries");
+       if (not_properly_sorted)
+               retval += error_func(&item->object, FSCK_ERROR, "not properly sorted");
+       return retval;
+}
+
+static int fsck_commit(struct commit *commit, fsck_error error_func)
+{
+       char *buffer = commit->buffer;
+       unsigned char tree_sha1[20], sha1[20];
+       struct commit_graft *graft;
+       int parents = 0;
+
+       if (!commit->date)
+               return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
+
+       if (memcmp(buffer, "tree ", 5))
+               return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
+       if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
+               return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1");
+       buffer += 46;
+       while (!memcmp(buffer, "parent ", 7)) {
+               if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n')
+                       return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
+               buffer += 48;
+               parents++;
+       }
+       graft = lookup_commit_graft(commit->object.sha1);
+       if (graft) {
+               struct commit_list *p = commit->parents;
+               parents = 0;
+               while (p) {
+                       p = p->next;
+                       parents++;
+               }
+               if (graft->nr_parent == -1 && !parents)
+                       ; /* shallow commit */
+               else if (graft->nr_parent != parents)
+                       return error_func(&commit->object, FSCK_ERROR, "graft objects missing");
+       } else {
+               struct commit_list *p = commit->parents;
+               while (p && parents) {
+                       p = p->next;
+                       parents--;
+               }
+               if (p || parents)
+                       return error_func(&commit->object, FSCK_ERROR, "parent objects missing");
+       }
+       if (memcmp(buffer, "author ", 7))
+               return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
+       if (!commit->tree)
+               return error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
+
+       return 0;
+}
+
+static int fsck_tag(struct tag *tag, fsck_error error_func)
+{
+       struct object *tagged = tag->tagged;
+
+       if (!tagged)
+               return error_func(&tag->object, FSCK_ERROR, "could not load tagged object");
+       return 0;
+}
+
+int fsck_object(struct object *obj, int strict, fsck_error error_func)
+{
+       if (!obj)
+               return error_func(obj, FSCK_ERROR, "no valid object to fsck");
+
+       if (obj->type == OBJ_BLOB)
+               return 0;
+       if (obj->type == OBJ_TREE)
+               return fsck_tree((struct tree *) obj, strict, error_func);
+       if (obj->type == OBJ_COMMIT)
+               return fsck_commit((struct commit *) obj, error_func);
+       if (obj->type == OBJ_TAG)
+               return fsck_tag((struct tag *) obj, error_func);
+
+       return error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)",
+                         obj->type);
+}
+
+int fsck_error_function(struct object *obj, int type, const char *fmt, ...)
+{
+       va_list ap;
+       int len;
+       struct strbuf sb;
+
+       strbuf_init(&sb, 0);
+       strbuf_addf(&sb, "object %s:", obj->sha1?sha1_to_hex(obj->sha1):"(null)");
+
+       va_start(ap, fmt);
+       len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap);
+       va_end(ap);
+
+       if (len < 0)
+               len = 0;
+       if (len >= strbuf_avail(&sb)) {
+               strbuf_grow(&sb, len + 2);
+               va_start(ap, fmt);
+               len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap);
+               va_end(ap);
+               if (len >= strbuf_avail(&sb))
+                       die("this should not happen, your snprintf is broken");
+       }
+
+       error(sb.buf);
+       strbuf_release(&sb);
+       return 1;
+}
diff --git a/fsck.h b/fsck.h
new file mode 100644 (file)
index 0000000..990ee02
--- /dev/null
+++ b/fsck.h
@@ -0,0 +1,32 @@
+#ifndef GIT_FSCK_H
+#define GIT_FSCK_H
+
+#define FSCK_ERROR 1
+#define FSCK_WARN 2
+
+/*
+ * callback function for fsck_walk
+ * type is the expected type of the object or OBJ_ANY
+ * the return value is:
+ *     0       everything OK
+ *     <0      error signaled and abort
+ *     >0      error signaled and do not abort
+ */
+typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);
+
+/* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
+typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);
+
+int fsck_error_function(struct object *obj, int type, const char *fmt, ...);
+
+/* descend in all linked child objects
+ * the return value is:
+ *    -1       error in processing the object
+ *    <0       return value of the callback, which lead to an abort
+ *    >0       return value of the first sigaled error >0 (in the case of no other errors)
+ *    0                everything OK
+ */
+int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);
+int fsck_object(struct object *obj, int strict, fsck_error error_func);
+
+#endif
index a2c6fea47af1e4616bc549f26f3fe1b364841abe..1f6b5e02c5e7da45a8e7966b600946cff9c24832 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -9,7 +9,7 @@ git-am [options] <mbox>|<Maildir>...
 git-am [options] --resolved
 git-am [options] --skip
 --
-d,dotest=       use <dir> and not .dotest
+d,dotest=       (removed -- do not use)
 i,interactive   run interactively
 b,binary        pass --allo-binary-replacement to git-apply
 3,3way          allow fall back on 3way merging if needed
@@ -21,9 +21,11 @@ C=              pass it through git-apply
 p=              pass it through git-apply
 resolvemsg=     override error message when patch failure occurs
 r,resolved      to be used after a patch failure
-skip            skip the current patch"
+skip            skip the current patch
+rebasing        (internal use for git-rebase)"
 
 . git-sh-setup
+prefix=$(git rev-parse --show-prefix)
 set_reflog_action am
 require_work_tree
 cd_to_toplevel
@@ -49,10 +51,6 @@ stop_here_user_resolve () {
     then
         cmdline="$cmdline -3"
     fi
-    if test '.dotest' != "$dotest"
-    then
-        cmdline="$cmdline -d=$dotest"
-    fi
     echo "When you have resolved this problem run \"$cmdline --resolved\"."
     echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
 
@@ -124,7 +122,8 @@ reread_subject () {
 }
 
 prec=4
-dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
+dotest=".dotest"
+sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
 resolvemsg= resume=
 git_apply_opt=
 
@@ -149,8 +148,11 @@ do
                resolved=t ;;
        --skip)
                skip=t ;;
+       --rebasing)
+               rebasing=t threeway=t keep=t binary=t ;;
        -d|--dotest)
-               shift; dotest=$1;;
+               die "-d option is no longer supported.  Do not use."
+               ;;
        --resolvemsg)
                shift; resolvemsg=$1 ;;
        --whitespace)
@@ -186,7 +188,7 @@ then
        0,)
                # No file input but without resume parameters; catch
                # user error to feed us a patch from standard input
-               # when there is already .dotest.  This is somewhat
+               # when there is already $dotest.  This is somewhat
                # unreliable -- stdin could be /dev/null for example
                # and the caller did not intend to feed us a patch but
                # wanted to continue unattended.
@@ -206,6 +208,24 @@ else
        # Start afresh.
        mkdir -p "$dotest" || exit
 
+       if test -n "$prefix" && test $# != 0
+       then
+               first=t
+               for arg
+               do
+                       test -n "$first" && {
+                               set x
+                               first=
+                       }
+                       case "$arg" in
+                       /*)
+                               set "$@" "$arg" ;;
+                       *)
+                               set "$@" "$prefix$arg" ;;
+                       esac
+               done
+               shift
+       fi
        git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
                rm -fr "$dotest"
                exit 1
@@ -220,6 +240,12 @@ else
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
        echo 1 >"$dotest/next"
+       if test -n "$rebasing"
+       then
+               : >"$dotest/rebasing"
+       else
+               : >"$dotest/applying"
+       fi
 fi
 
 case "$resolved" in
index 2a40703c851d7011723588dd7643dc034db49c31..73968e02b024f22068eb5500033f7a2f41d31e8f 100644 (file)
@@ -209,6 +209,15 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
 extern FILE *git_fopen(const char*, const char*);
 #endif
 
+#ifdef SNPRINTF_RETURNS_BOGUS
+#define snprintf git_snprintf
+extern int git_snprintf(char *str, size_t maxsize,
+                       const char *format, ...);
+#define vsnprintf git_vsnprintf
+extern int git_vsnprintf(char *str, size_t maxsize,
+                        const char *format, va_list ap);
+#endif
+
 #ifdef __GLIBC_PREREQ
 #if __GLIBC_PREREQ(2, 1)
 #define HAVE_STRCHRNUL
@@ -437,4 +446,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #define qsort git_qsort
 #endif
 
+#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
+# define FORCE_DIR_SET_GID S_ISGID
+#else
+# define FORCE_DIR_SET_GID 0
+#endif
+
 #endif
index 9516242338d95687b4354a6d21692cbd10547899..47f116f37ee1030ac0cab1f91feec04d673d94bd 100755 (executable)
@@ -15,7 +15,7 @@
 
 use strict;
 use warnings;
-use Getopt::Std;
+use Getopt::Long;
 use File::Spec;
 use File::Temp qw(tempfile tmpnam);
 use File::Path qw(mkpath);
@@ -29,7 +29,7 @@
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r);
 my (%conv_author_name, %conv_author_email);
 
 sub usage(;$) {
@@ -112,7 +112,12 @@ sub read_repo_config {
 
 my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
 read_repo_config($opts);
-getopts($opts) or usage();
+Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
+
+# turn the Getopt::Std specification in a Getopt::Long one,
+# with support for multiple -M options
+GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
+    or usage();
 usage if $opt_h;
 
 if (@ARGV == 0) {
@@ -164,10 +169,10 @@ sub read_repo_config {
 
 our @mergerx = ();
 if ($opt_m) {
-       @mergerx = ( qr/\b(?:from|of|merge|merging|merged) (\w+)/i );
+       @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
 }
-if ($opt_M) {
-       push (@mergerx, qr/$opt_M/);
+if (@opt_M) {
+       push (@mergerx, map { qr/$_/ } @opt_M);
 }
 
 # Remember UTC of our starting time
index afe3d0b7fe4d643688e672f62844cc86ec64fab5..7f632af20defd7a3cfd92008c51f7e599be13830 100755 (executable)
@@ -2556,7 +2556,7 @@ sub update
                     if ($base) {
                         my @merged;
                         # print "want to log between  $base $parent \n";
-                        open(GITLOG, '-|', 'git-log', "$base..$parent")
+                        open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
                          or die "Cannot call git-log: $!";
                         my $mergedhash;
                         while (<GITLOG>) {
index 49e13f0bb1ed2bcb6e85455f24dffa912927d67a..010353ad82881886b9739cc5f8de595acc9273ef 100755 (executable)
@@ -252,7 +252,16 @@ while read commit parents; do
                git read-tree -i -m $commit
                ;;
        *)
-               git read-tree -i -m $commit:"$filter_subdir"
+               # The commit may not have the subdirectory at all
+               err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
+                       if ! git rev-parse --verify $commit:"$filter_subdir" 2>/dev/null
+                       then
+                               rm -f "$GIT_INDEX_FILE"
+                       else
+                               echo >&2 "$err"
+                               false
+                       fi
+               }
        esac || die "Could not initialize the index"
 
        GIT_COMMIT=$commit
index 01e0a46ba56faa8a17ed8710346b18dd215899c7..4e321742ab8f103d829f33ab4d6ab5430fa132ba 100644 (file)
@@ -224,6 +224,11 @@ else
        ifeq ($(shell $(MSGFMT) >/dev/null 2>&1 || echo $$?),127)
                MSGFMT := $(TCL_PATH) po/po2msg.sh
        endif
+       ifeq (msgfmt,$(MSGFMT))
+       ifeq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null || echo $?),1)
+               MSGFMT := $(TCL_PATH) po/po2msg.sh
+       endif
+       endif
 endif
 
 msgsdir     = $(gg_libdir)/msgs
index 1c123a37e6941b6727c6101bb29cbc485a380c20..7dbbb1d79dc1d66db3a662045a13554f8d574004 100755 (executable)
@@ -37,6 +37,7 @@ use_strategies=
 
 allow_fast_forward=t
 allow_trivial_merge=t
+squash= no_commit=
 
 dropsave() {
        rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
@@ -70,7 +71,7 @@ finish_up_to_date () {
 squash_message () {
        echo Squashed commit of the following:
        echo
-       git log --no-merges ^"$head" $remoteheads
+       git log --no-merges --pretty=medium ^"$head" $remoteheads
 }
 
 finish () {
@@ -152,17 +153,21 @@ parse_config () {
                --summary)
                        show_diffstat=t ;;
                --squash)
-                       allow_fast_forward=t squash=t no_commit=t ;;
+                       test "$allow_fast_forward" = t ||
+                               die "You cannot combine --squash with --no-ff."
+                       squash=t no_commit=t ;;
                --no-squash)
-                       allow_fast_forward=t squash= no_commit= ;;
+                       squash= no_commit= ;;
                --commit)
-                       allow_fast_forward=t squash= no_commit= ;;
+                       no_commit= ;;
                --no-commit)
-                       allow_fast_forward=t squash= no_commit=t ;;
+                       no_commit=t ;;
                --ff)
-                       allow_fast_forward=t squash= no_commit= ;;
+                       allow_fast_forward=t ;;
                --no-ff)
-                       allow_fast_forward=false squash= no_commit= ;;
+                       test "$squash" != t ||
+                               die "You cannot combine --squash with --no-ff."
+                       allow_fast_forward=f ;;
                -s|--strategy)
                        shift
                        case " $all_strategies " in
index cbbb707959cc64427f7bbd7bfefb0a8f0f263596..5c86f69229042c16704d11ce404e43297cc7b72c 100755 (executable)
@@ -34,7 +34,7 @@ base_present () {
 
 cleanup_temp_files () {
     if test "$1" = --save-backup ; then
-       mv -- "$BACKUP" "$path.orig"
+       mv -- "$BACKUP" "$MERGED.orig"
        rm -f -- "$LOCAL" "$REMOTE" "$BASE"
     else
        rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
@@ -67,14 +67,14 @@ resolve_symlink_merge () {
        read ans
        case "$ans" in
            [lL]*)
-               git checkout-index -f --stage=2 -- "$path"
-               git add -- "$path"
+               git checkout-index -f --stage=2 -- "$MERGED"
+               git add -- "$MERGED"
                cleanup_temp_files --save-backup
                return
                ;;
            [rR]*)
-               git checkout-index -f --stage=3 -- "$path"
-               git add -- "$path"
+               git checkout-index -f --stage=3 -- "$MERGED"
+               git add -- "$MERGED"
                cleanup_temp_files --save-backup
                return
                ;;
@@ -95,12 +95,12 @@ resolve_deleted_merge () {
        read ans
        case "$ans" in
            [mMcC]*)
-               git add -- "$path"
+               git add -- "$MERGED"
                cleanup_temp_files --save-backup
                return
                ;;
            [dD]*)
-               git rm -- "$path" > /dev/null
+               git rm -- "$MERGED" > /dev/null
                cleanup_temp_files
                return
                ;;
@@ -112,11 +112,11 @@ resolve_deleted_merge () {
 }
 
 check_unchanged () {
-    if test "$path" -nt "$BACKUP" ; then
+    if test "$MERGED" -nt "$BACKUP" ; then
        status=0;
     else
        while true; do
-           echo "$path seems unchanged."
+           echo "$MERGED seems unchanged."
            printf "Was the merge successful? [y/n] "
            read answer < /dev/tty
            case "$answer" in
@@ -127,50 +127,38 @@ check_unchanged () {
     fi
 }
 
-save_backup () {
-    if test "$status" -eq 0; then
-       mv -- "$BACKUP" "$path.orig"
-    fi
-}
-
-remove_backup () {
-    if test "$status" -eq 0; then
-       rm "$BACKUP"
-    fi
-}
-
 merge_file () {
-    path="$1"
+    MERGED="$1"
 
-    f=`git ls-files -u -- "$path"`
+    f=`git ls-files -u -- "$MERGED"`
     if test -z "$f" ; then
-       if test ! -f "$path" ; then
-           echo "$path: file not found"
+       if test ! -f "$MERGED" ; then
+           echo "$MERGED: file not found"
        else
-           echo "$path: file does not need merging"
+           echo "$MERGED: file does not need merging"
        fi
        exit 1
     fi
 
-    ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')"
-    BACKUP="$path.BACKUP.$ext"
-    LOCAL="$path.LOCAL.$ext"
-    REMOTE="$path.REMOTE.$ext"
-    BASE="$path.BASE.$ext"
+    ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+    BACKUP="$MERGED.BACKUP.$ext"
+    LOCAL="$MERGED.LOCAL.$ext"
+    REMOTE="$MERGED.REMOTE.$ext"
+    BASE="$MERGED.BASE.$ext"
 
-    mv -- "$path" "$BACKUP"
-    cp -- "$BACKUP" "$path"
+    mv -- "$MERGED" "$BACKUP"
+    cp -- "$BACKUP" "$MERGED"
 
-    base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
-    local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
-    remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
+    base_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
+    local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
+    remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
 
-    base_present   && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null
-    local_present  && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null
-    remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null
+    base_present   && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
+    local_present  && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
+    remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
 
     if test -z "$local_mode" -o -z "$remote_mode"; then
-       echo "Deleted merge conflict for '$path':"
+       echo "Deleted merge conflict for '$MERGED':"
        describe_file "$local_mode" "local" "$LOCAL"
        describe_file "$remote_mode" "remote" "$REMOTE"
        resolve_deleted_merge
@@ -178,14 +166,14 @@ merge_file () {
     fi
 
     if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
-       echo "Symbolic link merge conflict for '$path':"
+       echo "Symbolic link merge conflict for '$MERGED':"
        describe_file "$local_mode" "local" "$LOCAL"
        describe_file "$remote_mode" "remote" "$REMOTE"
        resolve_symlink_merge
        return
     fi
 
-    echo "Normal merge conflict for '$path':"
+    echo "Normal merge conflict for '$MERGED':"
     describe_file "$local_mode" "local" "$LOCAL"
     describe_file "$remote_mode" "remote" "$REMOTE"
     printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
@@ -194,36 +182,32 @@ merge_file () {
     case "$merge_tool" in
        kdiff3)
            if base_present ; then
-               ("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
-                   -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+               ("$merge_tool_path" --auto --L1 "$MERGED (Base)" --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" \
+                   -o "$MERGED" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
            else
-               ("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
-                   -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+               ("$merge_tool_path" --auto --L1 "$MERGED (Local)" --L2 "$MERGED (Remote)" \
+                   -o "$MERGED" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
            fi
            status=$?
-           remove_backup
            ;;
        tkdiff)
            if base_present ; then
-               "$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+               "$merge_tool_path" -a "$BASE" -o "$MERGED" -- "$LOCAL" "$REMOTE"
            else
-               "$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE"
+               "$merge_tool_path" -o "$MERGED" -- "$LOCAL" "$REMOTE"
            fi
            status=$?
-           save_backup
            ;;
        meld|vimdiff)
            touch "$BACKUP"
-           "$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE"
+           "$merge_tool_path" -- "$LOCAL" "$MERGED" "$REMOTE"
            check_unchanged
-           save_backup
            ;;
        gvimdiff)
-               touch "$BACKUP"
-               "$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE"
-               check_unchanged
-               save_backup
-               ;;
+           touch "$BACKUP"
+           "$merge_tool_path" -f -- "$LOCAL" "$MERGED" "$REMOTE"
+           check_unchanged
+           ;;
        xxdiff)
            touch "$BACKUP"
            if base_present ; then
@@ -231,53 +215,68 @@ merge_file () {
                    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
                    -R 'Accel.Search: "Ctrl+F"' \
                    -R 'Accel.SearchForward: "Ctrl-G"' \
-                   --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
+                   --merged-file "$MERGED" -- "$LOCAL" "$BASE" "$REMOTE"
            else
                "$merge_tool_path" -X --show-merged-pane \
                    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
                    -R 'Accel.Search: "Ctrl+F"' \
                    -R 'Accel.SearchForward: "Ctrl-G"' \
-                   --merged-file "$path" -- "$LOCAL" "$REMOTE"
+                   --merged-file "$MERGED" -- "$LOCAL" "$REMOTE"
            fi
            check_unchanged
-           save_backup
            ;;
        opendiff)
            touch "$BACKUP"
            if base_present; then
-               "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
+               "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
            else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat
+               "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
            fi
            check_unchanged
-           save_backup
            ;;
        ecmerge)
            touch "$BACKUP"
            if base_present; then
-               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path"
+               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED"
            else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path"
+               "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED"
            fi
            check_unchanged
-           save_backup
            ;;
        emerge)
            if base_present ; then
-               "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
+               "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$MERGED")"
            else
-               "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
+               "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
            fi
            status=$?
-           save_backup
+           ;;
+       *)
+           if test -n "$merge_tool_cmd"; then
+               if test "$merge_tool_trust_exit_code" = "false"; then
+                   touch "$BACKUP"
+                   ( eval $merge_tool_cmd )
+                   check_unchanged
+               else
+                   ( eval $merge_tool_cmd )
+                   status=$?
+               fi
+           fi
            ;;
     esac
     if test "$status" -ne 0; then
-       echo "merge of $path failed" 1>&2
-       mv -- "$BACKUP" "$path"
+       echo "merge of $MERGED failed" 1>&2
+       mv -- "$BACKUP" "$MERGED"
        exit 1
     fi
-    git add -- "$path"
+
+    if test "$merge_keep_backup" = "true"; then
+       mv -- "$BACKUP" "$MERGED.orig"
+    else
+       rm -- "$BACKUP"
+    fi
+
+    git add -- "$MERGED"
     cleanup_temp_files
 }
 
@@ -309,12 +308,20 @@ do
     shift
 done
 
+valid_custom_tool()
+{
+    merge_tool_cmd="$(git config mergetool.$1.cmd)"
+    test -n "$merge_tool_cmd"
+}
+
 valid_tool() {
        case "$1" in
                kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
                        ;; # happy
                *)
-                       return 1
+                       if ! valid_custom_tool "$1"; then
+                               return 1
+                       fi
                        ;;
        esac
 }
@@ -380,10 +387,16 @@ else
 
     init_merge_tool_path "$merge_tool"
 
-    if ! type "$merge_tool_path" > /dev/null 2>&1; then
+    merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
+
+    if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
         echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
         exit 1
     fi
+
+    if ! test -z "$merge_tool_cmd"; then
+        merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
+    fi
 fi
 
 
index bdcea0ed703057e08fd4204245f4f0c8a308f8d0..452c5e7e01e168ec4fcdc5bb87caac3f326f95ea 100755 (executable)
@@ -208,16 +208,15 @@ do
                if test -d "$dotest"
                then
                        move_to_original_branch
-                       rm -r "$dotest"
                elif test -d .dotest
                then
                        dotest=.dotest
                        move_to_original_branch
-                       rm -r .dotest
                else
                        die "No rebase in progress?"
                fi
-               git reset --hard ORIG_HEAD
+               git reset --hard $(cat $dotest/orig-head)
+               rm -r "$dotest"
                exit
                ;;
        --onto)
@@ -377,7 +376,7 @@ fi
 if test -z "$do_merge"
 then
        git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
-       git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" &&
+       git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
        move_to_original_branch
        ret=$?
        test 0 != $ret -a -d .dotest &&
index 5cd69513cf84111d1152d07f8cda77b201ffc416..b30ed734e7102b6a5d88c01364fa7d18db8f8bfe 100755 (executable)
@@ -7,10 +7,10 @@
 sub add_remote_config {
        my ($hash, $name, $what, $value) = @_;
        if ($what eq 'url') {
-               if (exists $hash->{$name}{'URL'}) {
-                       print STDERR "Warning: more than one remote.$name.url\n";
+               # Having more than one is Ok -- it is used for push.
+               if (! exists $hash->{'URL'}) {
+                       $hash->{$name}{'URL'} = $value;
                }
-               $hash->{$name}{'URL'} = $value;
        }
        elsif ($what eq 'fetch') {
                $hash->{$name}{'FETCH'} ||= [];
index 29b1105c4c3c83258ebdf53c8885f767baa0ace0..be4a20d7cd562e9b00d93fe71313aed70afe50af 100755 (executable)
@@ -317,7 +317,7 @@ sub read_config {
 
 # If explicit old-style ones are specified, they trump --suppress-cc.
 $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
-$suppress_cc{'sob'} = $signed_off_cc if defined $signed_off_cc;
+$suppress_cc{'sob'} = !$signed_off_cc if defined $signed_off_cc;
 
 # Debugging, print out the suppressions.
 if (0) {
@@ -855,6 +855,7 @@ sub send_message
                        $message .=  $_;
                        if (/^(Signed-off-by|Cc): (.*)$/i) {
                                next if ($suppress_cc{'sob'});
+                               chomp;
                                my $c = $2;
                                chomp $c;
                                next if ($c eq $sender and $suppress_cc{'self'});
index b00f8881693ff09516ea058816dc96697ca56483..c2b68205a2b69eb91b2403e11238093fc65c04ef 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Copyright (c) 2007, Nanako Shiraishi
 
-USAGE='[  | save | list | show | apply | clear | create ]'
+USAGE='[  | save | list | show | apply | clear | drop | pop | create ]'
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -196,6 +196,28 @@ apply_stash () {
        fi
 }
 
+drop_stash () {
+       have_stash || die 'No stash entries to drop'
+
+       if test $# = 0
+       then
+               set x "$ref_stash@{0}"
+               shift
+       fi
+       # Verify supplied argument looks like a stash entry
+       s=$(git rev-parse --revs-only --no-flags "$@") &&
+       git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
+       git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
+       git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
+               die "$*: not a valid stashed state"
+
+       git reflog delete --updateref --rewrite "$@" &&
+               echo "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+
+       # clear_stash if we just dropped the last stash entry
+       git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
+}
+
 # Main command set
 case "$1" in
 list)
@@ -230,6 +252,18 @@ create)
        fi
        create_stash "$*" && echo "$w_commit"
        ;;
+drop)
+       shift
+       drop_stash "$@"
+       ;;
+pop)
+       shift
+       if apply_stash "$@"
+       then
+               test -z "$unstash_index" || shift
+               drop_stash "$@"
+       fi
+       ;;
 *)
        if test $# -eq 0
        then
index a6aaf40b0ac5b477b73684b5a2b8b7417adaad81..7171cb60ab4e6a49b84500bde43c04ea40501d6c 100755 (executable)
@@ -153,20 +153,6 @@ cmd_add()
                usage
        fi
 
-       case "$repo" in
-       ./*|../*)
-               # dereference source url relative to parent's url
-               realrepo="$(resolve_relative_url $repo)" ;;
-       *)
-               # Turn the source into an absolute path if
-               # it is local
-               if base=$(get_repo_base "$repo"); then
-                       repo="$base"
-               fi
-               realrepo=$repo
-               ;;
-       esac
-
        # Guess path from repo if not specified or strip trailing slashes
        if test -z "$path"; then
                path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
@@ -174,15 +160,39 @@ cmd_add()
                path=$(echo "$path" | sed -e 's|/*$||')
        fi
 
-       test -e "$path" &&
-       die "'$path' already exists"
-
        git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
        die "'$path' already exists in the index"
 
-       module_clone "$path" "$realrepo" || exit
-       (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
-       die "Unable to checkout submodule '$path'"
+       # perhaps the path exists and is already a git repo, else clone it
+       if test -e "$path"
+       then
+               if test -d "$path/.git" &&
+               test "$(unset GIT_DIR; cd $path; git rev-parse --git-dir)" = ".git"
+               then
+                       echo "Adding existing repo at '$path' to the index"
+               else
+                       die "'$path' already exists and is not a valid git repo"
+               fi
+       else
+               case "$repo" in
+               ./*|../*)
+                       # dereference source url relative to parent's url
+                       realrepo="$(resolve_relative_url $repo)" ;;
+               *)
+                       # Turn the source into an absolute path if
+                       # it is local
+                       if base=$(get_repo_base "$repo"); then
+                               repo="$base"
+                       fi
+                       realrepo=$repo
+                       ;;
+               esac
+
+               module_clone "$path" "$realrepo" || exit
+               (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
+               die "Unable to checkout submodule '$path'"
+       fi
+
        git add "$path" ||
        die "Failed to add submodule '$path'"
 
@@ -362,7 +372,7 @@ cmd_status()
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
-               if test -z "url" || ! test -d "$path"/.git
+               if test -z "$url" || ! test -d "$path"/.git
                then
                        say "-$sha1 $path"
                        continue;
index 9e2faf90aa1676974234882a2f557c17f7bc59f4..1195569529401fd1c62414c8683a6b577721c1c7 100755 (executable)
@@ -1540,9 +1540,14 @@ sub find_by_url { # repos_root and, path are optional
                                            $remotes->{$repo_id}->{$_});
                }
                my $p = $path;
+               my $rwr = rewrite_root({repo_id => $repo_id});
                unless (defined $p) {
                        $p = $full_url;
-                       $p =~ s#^\Q$u\E(?:/|$)## or next;
+                       my $z = $u;
+                       if ($rwr) {
+                               $z = $rwr;
+                       }
+                       $p =~ s#^\Q$z\E(?:/|$)## or next;
                }
                foreach my $f (keys %$fetch) {
                        next if $f ne $p;
index fc95e2ca855fed2998b97733f9458d4ed9857cf2..ec73cb1256ba99a990648c548348ecdc3340a592 100755 (executable)
@@ -472,13 +472,15 @@ sub filter_snapshot_fmts {
        }
 }
 
+our $search_use_regexp = $cgi->param('sr');
+
 our $searchtext = $cgi->param('s');
 our $search_regexp;
 if (defined $searchtext) {
        if (length($searchtext) < 2) {
                die_error(undef, "At least two characters are required for search parameter");
        }
-       $search_regexp = quotemeta $searchtext;
+       $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
 }
 
 # now read PATH_INFO and use it as alternative to parameters
@@ -608,6 +610,7 @@ (%)
                searchtype => "st",
                snapshot_format => "sf",
                extra_options => "opt",
+               search_use_regexp => "sr",
        );
        my %mapping = @mapping;
 
@@ -2079,7 +2082,7 @@ sub parse_commit {
 }
 
 sub parse_commits {
-       my ($commit_id, $maxcount, $skip, $arg, $filename) = @_;
+       my ($commit_id, $maxcount, $skip, $filename, @args) = @_;
        my @cos;
 
        $maxcount ||= 1;
@@ -2089,7 +2092,7 @@ sub parse_commits {
 
        open my $fd, "-|", git_cmd(), "rev-list",
                "--header",
-               ($arg ? ($arg) : ()),
+               @args,
                ("--max-count=" . $maxcount),
                ("--skip=" . $skip),
                @extra_options,
@@ -2584,6 +2587,10 @@ sub git_header_html {
                      $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
                      " search:\n",
                      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+                     "<span title=\"Extended regular expression\">" .
+                     $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+                                    -checked => $search_use_regexp) .
+                     "</span>" .
                      "</div>" .
                      $cgi->end_form() . "\n";
        }
@@ -3830,7 +3837,7 @@ sub git_search_grep_body {
                              chop_and_escape_str($co{'title'}, 50) . "<br/>");
                my $comment = $co{'comment'};
                foreach my $line (@$comment) {
-                       if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
+                       if ($line =~ m/^(.*?)($search_regexp)(.*)$/i) {
                                my ($lead, $match, $trail) = ($1, $2, $3);
                                $match = chop_str($match, 70, 5, 'center');
                                my $contextlen = int((80 - length($match))/2);
@@ -5172,7 +5179,7 @@ sub git_history {
                $ftype = git_get_type($hash);
        }
 
-       my @commitlist = parse_commits($hash_base, 101, (100 * $page), "--full-history", $file_name);
+       my @commitlist = parse_commits($hash_base, 101, (100 * $page), $file_name, "--full-history");
 
        my $paging_nav = '';
        if ($page > 0) {
@@ -5254,14 +5261,17 @@ sub git_search {
                } elsif ($searchtype eq 'committer') {
                        $greptype = "--committer=";
                }
-               $greptype .= $search_regexp;
-               my @commitlist = parse_commits($hash, 101, (100 * $page), $greptype);
+               $greptype .= $searchtext;
+               my @commitlist = parse_commits($hash, 101, (100 * $page), undef,
+                                              $greptype, '--regexp-ignore-case',
+                                              $search_use_regexp ? '--extended-regexp' : '--fixed-strings');
 
                my $paging_nav = '';
                if ($page > 0) {
                        $paging_nav .=
                                $cgi->a({-href => href(action=>"search", hash=>$hash,
-                                                      searchtext=>$searchtext, searchtype=>$searchtype)},
+                                                      searchtext=>$searchtext,
+                                                      searchtype=>$searchtype)},
                                        "first");
                        $paging_nav .= " &sdot; " .
                                $cgi->a({-href => href(-replay=>1, page=>$page-1),
@@ -5295,50 +5305,19 @@ sub git_search {
                print "<table class=\"pickaxe search\">\n";
                my $alternate = 1;
                $/ = "\n";
-               my $git_command = git_cmd_str();
-               my $searchqtext = $searchtext;
-               $searchqtext =~ s/'/'\\''/;
-               open my $fd, "-|", "$git_command rev-list $hash | " .
-                       "$git_command diff-tree -r --stdin -S\'$searchqtext\'";
+               open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
+                       '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
+                       ($search_use_regexp ? '--pickaxe-regex' : ());
                undef %co;
                my @files;
                while (my $line = <$fd>) {
-                       if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
-                               my %set;
-                               $set{'file'} = $6;
-                               $set{'from_id'} = $3;
-                               $set{'to_id'} = $4;
-                               $set{'id'} = $set{'to_id'};
-                               if ($set{'id'} =~ m/0{40}/) {
-                                       $set{'id'} = $set{'from_id'};
-                               }
-                               if ($set{'id'} =~ m/0{40}/) {
-                                       next;
-                               }
-                               push @files, \%set;
-                       } elsif ($line =~ m/^([0-9a-fA-F]{40})$/){
+                       chomp $line;
+                       next unless $line;
+
+                       my %set = parse_difftree_raw_line($line);
+                       if (defined $set{'commit'}) {
+                               # finish previous commit
                                if (%co) {
-                                       if ($alternate) {
-                                               print "<tr class=\"dark\">\n";
-                                       } else {
-                                               print "<tr class=\"light\">\n";
-                                       }
-                                       $alternate ^= 1;
-                                       my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
-                                       print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-                                             "<td><i>" . $author . "</i></td>\n" .
-                                             "<td>" .
-                                             $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
-                                                     -class => "list subject"},
-                                                     chop_and_escape_str($co{'title'}, 50) . "<br/>");
-                                       while (my $setref = shift @files) {
-                                               my %set = %$setref;
-                                               print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
-                                                                            hash=>$set{'id'}, file_name=>$set{'file'}),
-                                                             -class => "list"},
-                                                             "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
-                                                     "<br/>\n";
-                                       }
                                        print "</td>\n" .
                                              "<td class=\"link\">" .
                                              $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
@@ -5347,11 +5326,44 @@ sub git_search {
                                        print "</td>\n" .
                                              "</tr>\n";
                                }
-                               %co = parse_commit($1);
+
+                               if ($alternate) {
+                                       print "<tr class=\"dark\">\n";
+                               } else {
+                                       print "<tr class=\"light\">\n";
+                               }
+                               $alternate ^= 1;
+                               %co = parse_commit($set{'commit'});
+                               my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
+                               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+                                     "<td><i>$author</i></td>\n" .
+                                     "<td>" .
+                                     $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+                                             -class => "list subject"},
+                                             chop_and_escape_str($co{'title'}, 50) . "<br/>");
+                       } elsif (defined $set{'to_id'}) {
+                               next if ($set{'to_id'} =~ m/^0{40}$/);
+
+                               print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
+                                                            hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
+                                             -class => "list"},
+                                             "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
+                                     "<br/>\n";
                        }
                }
                close $fd;
 
+               # finish last commit (warning: repetition!)
+               if (%co) {
+                       print "</td>\n" .
+                             "<td class=\"link\">" .
+                             $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
+                             " | " .
+                             $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
+                       print "</td>\n" .
+                             "</tr>\n";
+               }
+
                print "</table>\n";
        }
 
@@ -5363,7 +5375,9 @@ sub git_search {
                my $alternate = 1;
                my $matches = 0;
                $/ = "\n";
-               open my $fd, "-|", git_cmd(), 'grep', '-n', '-i', '-E', $searchtext, $co{'tree'};
+               open my $fd, "-|", git_cmd(), 'grep', '-n',
+                       $search_use_regexp ? ('-E', '-i') : '-F',
+                       $searchtext, $co{'tree'};
                my $lastfile = '';
                while (my $line = <$fd>) {
                        chomp $line;
@@ -5393,7 +5407,7 @@ sub git_search {
                                print "<div class=\"binary\">Binary file</div>\n";
                        } else {
                                $ltext = untabify($ltext);
-                               if ($ltext =~ m/^(.*)($searchtext)(.*)$/i) {
+                               if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
                                        $ltext = esc_html($1, -nbsp=>1);
                                        $ltext .= '<span class="match">';
                                        $ltext .= esc_html($2, -nbsp=>1);
@@ -5428,27 +5442,31 @@ sub git_search_help {
        git_header_html();
        git_print_page_nav('','', $hash,$hash,$hash);
        print <<EOT;
+<p><strong>Pattern</strong> is by default a normal string that is matched precisely (but without
+regard to case, except in the case of pickaxe). However, when you check the <em>re</em> checkbox,
+the pattern entered is recognized as the POSIX extended
+<a href="http://en.wikipedia.org/wiki/Regular_expression">regular expression</a> (also case
+insensitive).</p>
 <dl>
 <dt><b>commit</b></dt>
-<dd>The commit messages and authorship information will be scanned for the given string.</dd>
+<dd>The commit messages and authorship information will be scanned for the given pattern.</dd>
 EOT
        my ($have_grep) = gitweb_check_feature('grep');
        if ($have_grep) {
                print <<EOT;
 <dt><b>grep</b></dt>
 <dd>All files in the currently selected tree (HEAD unless you are explicitly browsing
-    a different one) are searched for the given
-<a href="http://en.wikipedia.org/wiki/Regular_expression">regular expression</a>
-(POSIX extended) and the matches are listed. On large
-trees, this search can take a while and put some strain on the server, so please use it with
-some consideration.</dd>
+    a different one) are searched for the given pattern. On large trees, this search can take
+a while and put some strain on the server, so please use it with some consideration. Note that
+due to git-grep peculiarity, currently if regexp mode is turned off, the matches are
+case-sensitive.</dd>
 EOT
        }
        print <<EOT;
 <dt><b>author</b></dt>
-<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given string.</dd>
+<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given pattern.</dd>
 <dt><b>committer</b></dt>
-<dd>Name and e-mail of the committer and date of commit will be scanned for the given string.</dd>
+<dd>Name and e-mail of the committer and date of commit will be scanned for the given pattern.</dd>
 EOT
        my ($have_pickaxe) = gitweb_check_feature('pickaxe');
        if ($have_pickaxe) {
@@ -5456,7 +5474,8 @@ sub git_search_help {
 <dt><b>pickaxe</b></dt>
 <dd>All commits that caused the string to appear or disappear from any file (changes that
 added, removed or "modified" the string) will be listed. This search can take a while and
-takes a lot of strain on the server, so please use it wisely.</dd>
+takes a lot of strain on the server, so please use it wisely. Note that since you may be
+interested even in changes just changing the case as well, this search is case sensitive.</dd>
 EOT
        }
        print "</dl>\n";
@@ -5507,7 +5526,7 @@ sub git_feed {
 
        # log/feed of current (HEAD) branch, log of given branch, history of file/directory
        my $head = $hash || 'HEAD';
-       my @commitlist = parse_commits($head, 150, 0, undef, $file_name);
+       my @commitlist = parse_commits($head, 150, 0, $file_name);
 
        my %latest_commit;
        my %latest_date;
index 406270f6f43d02eea1210b0363816363ed5a5816..5b230380ccd33d8b04f8dfce24050237ae9206ad 100644 (file)
@@ -2133,6 +2133,8 @@ static int delete_remote_branch(char *pattern, int force)
 
        /* Send delete request */
        fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
+       if (dry_run)
+               return 0;
        url = xmalloc(strlen(remote->url) + strlen(remote_ref->name) + 1);
        sprintf(url, "%s%s", remote->url, remote_ref->name);
        slot = get_active_slot();
@@ -2235,7 +2237,7 @@ int main(int argc, char **argv)
 
        memset(remote_dir_exists, -1, 256);
 
-       http_init();
+       http_init(NULL);
 
        no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
 
@@ -2306,6 +2308,16 @@ int main(int argc, char **argv)
 
                if (!ref->peer_ref)
                        continue;
+
+               if (is_zero_sha1(ref->peer_ref->new_sha1)) {
+                       if (delete_remote_branch(ref->name, 1) == -1) {
+                               error("Could not remove %s", ref->name);
+                               rc = -4;
+                       }
+                       new_refs++;
+                       continue;
+               }
+
                if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
                        if (push_verbosely || 1)
                                fprintf(stderr, "'%s': up-to-date\n", ref->name);
@@ -2337,11 +2349,6 @@ int main(int argc, char **argv)
                        }
                }
                hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-               if (is_zero_sha1(ref->new_sha1)) {
-                       error("cannot happen anymore");
-                       rc = -3;
-                       continue;
-               }
                new_refs++;
                strcpy(old_hex, sha1_to_hex(ref->old_sha1));
                new_hex = sha1_to_hex(ref->new_sha1);
index 2c3786870e1fbe94a5346cdbc53e6f806011052c..7bda34d91498c3959569327ea2132851230999e7 100644 (file)
@@ -902,13 +902,13 @@ static void cleanup(struct walker *walker)
        curl_slist_free_all(data->no_pragma_header);
 }
 
-struct walker *get_http_walker(const char *url)
+struct walker *get_http_walker(const char *url, struct remote *remote)
 {
        char *s;
        struct walker_data *data = xmalloc(sizeof(struct walker_data));
        struct walker *walker = xmalloc(sizeof(struct walker));
 
-       http_init();
+       http_init(remote);
 
        data->no_pragma_header = curl_slist_append(NULL, "Pragma:");
 
diff --git a/http.c b/http.c
index 5925d07478b763ee9f91d7b273f64f0ae956219b..256a5f15f40a8d9389560e1fb08e34a56e9f7140 100644 (file)
--- a/http.c
+++ b/http.c
@@ -218,13 +218,16 @@ static CURL* get_curl_handle(void)
        return result;
 }
 
-void http_init(void)
+void http_init(struct remote *remote)
 {
        char *low_speed_limit;
        char *low_speed_time;
 
        curl_global_init(CURL_GLOBAL_ALL);
 
+       if (remote && remote->http_proxy)
+               curl_http_proxy = xstrdup(remote->http_proxy);
+
        pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
 
 #ifdef USE_CURL_MULTI
@@ -281,23 +284,15 @@ void http_init(void)
 void http_cleanup(void)
 {
        struct active_request_slot *slot = active_queue_head;
-#ifdef USE_CURL_MULTI
-       char *wait_url;
-#endif
 
        while (slot != NULL) {
                struct active_request_slot *next = slot->next;
+               if (slot->curl != NULL) {
 #ifdef USE_CURL_MULTI
-               if (slot->in_use) {
-                       curl_easy_getinfo(slot->curl,
-                                         CURLINFO_EFFECTIVE_URL,
-                                         &wait_url);
-                       fprintf(stderr, "Waiting for %s\n", wait_url);
-                       run_active_slot(slot);
-               }
+                       curl_multi_remove_handle(curlm, slot->curl);
 #endif
-               if (slot->curl != NULL)
                        curl_easy_cleanup(slot->curl);
+               }
                free(slot);
                slot = next;
        }
@@ -314,6 +309,11 @@ void http_cleanup(void)
 
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
+
+       if (curl_http_proxy) {
+               free(curl_http_proxy);
+               curl_http_proxy = NULL;
+       }
 }
 
 struct active_request_slot *get_active_slot(void)
diff --git a/http.h b/http.h
index 9bab2c88210650e2aaa271a72eb7192cd2f7331b..04169d5f9c8fa4cb82ad720b9e6d371f02be83d3 100644 (file)
--- a/http.h
+++ b/http.h
@@ -7,6 +7,7 @@
 #include <curl/easy.h>
 
 #include "strbuf.h"
+#include "remote.h"
 
 /*
  * We detect based on the cURL version if multi-transfer is
@@ -83,7 +84,7 @@ extern void add_fill_function(void *data, int (*fill)(void *));
 extern void step_active_slots(void);
 #endif
 
-extern void http_init(void);
+extern void http_init(struct remote *remote);
 extern void http_cleanup(void);
 
 extern int data_received;
diff --git a/ident.c b/ident.c
index b839dcf5f085a94080e1f7891f3fb40ed151a394..ed44a5345a47d4843e8328c0f879ac925dde2b07 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -171,7 +171,7 @@ static const char au_env[] = "GIT_AUTHOR_NAME";
 static const char co_env[] = "GIT_COMMITTER_NAME";
 static const char *env_hint =
 "\n"
-"*** Your name cannot be determined from your system services (gecos).\n"
+"*** Please tell me who you are.\n"
 "\n"
 "Run\n"
 "\n"
index 9fd6982a979a40e701dcb6bcaf117eafbf6ff161..9c0c27813fd6736218d1caaed6248e4ae4d388b5 100644 (file)
@@ -7,9 +7,10 @@
 #include "tag.h"
 #include "tree.h"
 #include "progress.h"
+#include "fsck.h"
 
 static const char index_pack_usage[] =
-"git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
+"git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
 
 struct object_entry
 {
@@ -31,6 +32,9 @@ union delta_base {
  */
 #define UNION_BASE_SZ  20
 
+#define FLAG_LINK (1u<<20)
+#define FLAG_CHECKED (1u<<21)
+
 struct delta_entry
 {
        union delta_base base;
@@ -44,6 +48,7 @@ static int nr_deltas;
 static int nr_resolved_deltas;
 
 static int from_stdin;
+static int strict;
 static int verbose;
 
 static struct progress *progress;
@@ -56,6 +61,48 @@ static SHA_CTX input_ctx;
 static uint32_t input_crc32;
 static int input_fd, output_fd, pack_fd;
 
+static int mark_link(struct object *obj, int type, void *data)
+{
+       if (!obj)
+               return -1;
+
+       if (type != OBJ_ANY && obj->type != type)
+               die("object type mismatch at %s", sha1_to_hex(obj->sha1));
+
+       obj->flags |= FLAG_LINK;
+       return 0;
+}
+
+/* The content of each linked object must have been checked
+   or it must be already present in the object database */
+static void check_object(struct object *obj)
+{
+       if (!obj)
+               return;
+
+       if (!(obj->flags & FLAG_LINK))
+               return;
+
+       if (!(obj->flags & FLAG_CHECKED)) {
+               unsigned long size;
+               int type = sha1_object_info(obj->sha1, &size);
+               if (type != obj->type || type <= 0)
+                       die("object of unexpected type");
+               obj->flags |= FLAG_CHECKED;
+               return;
+       }
+}
+
+static void check_objects(void)
+{
+       unsigned i, max;
+
+       max = get_max_object_index();
+       for (i = 0; i < max; i++)
+               check_object(get_indexed_object(i));
+}
+
+
 /* Discard current buffer used content. */
 static void flush(void)
 {
@@ -341,6 +388,41 @@ static void sha1_object(const void *data, unsigned long size,
                        die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
                free(has_data);
        }
+       if (strict) {
+               if (type == OBJ_BLOB) {
+                       struct blob *blob = lookup_blob(sha1);
+                       if (blob)
+                               blob->object.flags |= FLAG_CHECKED;
+                       else
+                               die("invalid blob object %s", sha1_to_hex(sha1));
+               } else {
+                       struct object *obj;
+                       int eaten;
+                       void *buf = (void *) data;
+
+                       /*
+                        * we do not need to free the memory here, as the
+                        * buf is deleted by the caller.
+                        */
+                       obj = parse_object_buffer(sha1, type, size, buf, &eaten);
+                       if (!obj)
+                               die("invalid %s", typename(type));
+                       if (fsck_object(obj, 1, fsck_error_function))
+                               die("Error in object");
+                       if (fsck_walk(obj, mark_link, 0))
+                               die("Not all child objects of %s are reachable", sha1_to_hex(obj->sha1));
+
+                       if (obj->type == OBJ_TREE) {
+                               struct tree *item = (struct tree *) obj;
+                               item->buffer = NULL;
+                       }
+                       if (obj->type == OBJ_COMMIT) {
+                               struct commit *commit = (struct commit *) obj;
+                               commit->buffer = NULL;
+                       }
+                       obj->flags |= FLAG_CHECKED;
+               }
+       }
 }
 
 static void resolve_delta(struct object_entry *delta_obj, void *base_data,
@@ -714,6 +796,8 @@ int main(int argc, char **argv)
                                from_stdin = 1;
                        } else if (!strcmp(arg, "--fix-thin")) {
                                fix_thin_pack = 1;
+                       } else if (!strcmp(arg, "--strict")) {
+                               strict = 1;
                        } else if (!strcmp(arg, "--keep")) {
                                keep_msg = "";
                        } else if (!prefixcmp(arg, "--keep=")) {
@@ -812,6 +896,8 @@ int main(int argc, char **argv)
                            nr_deltas - nr_resolved_deltas);
        }
        free(deltas);
+       if (strict)
+               check_objects();
 
        idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
        for (i = 0; i < nr_objects; i++)
diff --git a/object-refs.c b/object-refs.c
deleted file mode 100644 (file)
index 5345671..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "cache.h"
-#include "object.h"
-#include "decorate.h"
-
-int track_object_refs = 0;
-
-static struct decoration ref_decorate;
-
-struct object_refs *lookup_object_refs(struct object *base)
-{
-       return lookup_decoration(&ref_decorate, base);
-}
-
-static void add_object_refs(struct object *obj, struct object_refs *refs)
-{
-       if (add_decoration(&ref_decorate, obj, refs))
-               die("object %s tried to add refs twice!", sha1_to_hex(obj->sha1));
-}
-
-struct object_refs *alloc_object_refs(unsigned count)
-{
-       struct object_refs *refs;
-       size_t size = sizeof(*refs) + count*sizeof(struct object *);
-
-       refs = xcalloc(1, size);
-       refs->count = count;
-       return refs;
-}
-
-static int compare_object_pointers(const void *a, const void *b)
-{
-       const struct object * const *pa = a;
-       const struct object * const *pb = b;
-       if (*pa == *pb)
-               return 0;
-       else if (*pa < *pb)
-               return -1;
-       else
-               return 1;
-}
-
-void set_object_refs(struct object *obj, struct object_refs *refs)
-{
-       unsigned int i, j;
-
-       /* Do not install empty list of references */
-       if (refs->count < 1) {
-               free(refs);
-               return;
-       }
-
-       /* Sort the list and filter out duplicates */
-       qsort(refs->ref, refs->count, sizeof(refs->ref[0]),
-             compare_object_pointers);
-       for (i = j = 1; i < refs->count; i++) {
-               if (refs->ref[i] != refs->ref[i - 1])
-                       refs->ref[j++] = refs->ref[i];
-       }
-       if (j < refs->count) {
-               /* Duplicates were found - reallocate list */
-               size_t size = sizeof(*refs) + j*sizeof(struct object *);
-               refs->count = j;
-               refs = xrealloc(refs, size);
-       }
-
-       for (i = 0; i < refs->count; i++)
-               refs->ref[i]->used = 1;
-       add_object_refs(obj, refs);
-}
-
-void mark_reachable(struct object *obj, unsigned int mask)
-{
-       const struct object_refs *refs;
-
-       if (!track_object_refs)
-               die("cannot do reachability with object refs turned off");
-       /* If we've been here already, don't bother */
-       if (obj->flags & mask)
-               return;
-       obj->flags |= mask;
-       refs = lookup_object_refs(obj);
-       if (refs) {
-               unsigned i;
-               for (i = 0; i < refs->count; i++)
-                       mark_reachable(refs->ref[i], mask);
-       }
-}
index 397bbfa090cd281214013c42a5f0b1de6063a861..036bd66fe9b6591e959e6df51160e636ab1a682e 100644 (file)
--- a/object.h
+++ b/object.h
@@ -35,14 +35,11 @@ struct object {
        unsigned char sha1[20];
 };
 
-extern int track_object_refs;
-
 extern const char *typename(unsigned int type);
 extern int type_from_string(const char *str);
 
 extern unsigned int get_max_object_index(void);
 extern struct object *get_indexed_object(unsigned int);
-extern struct object_refs *lookup_object_refs(struct object *);
 
 /** Internal only **/
 struct object *lookup_object(const unsigned char *sha1);
@@ -61,11 +58,6 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
 /** Returns the object, with potentially excess memory allocated. **/
 struct object *lookup_unknown_object(const unsigned  char *sha1);
 
-struct object_refs *alloc_object_refs(unsigned count);
-void set_object_refs(struct object *obj, struct object_refs *refs);
-
-void mark_reachable(struct object *obj, unsigned int mask);
-
 struct object_list *object_list_insert(struct object *item,
                                       struct object_list **list_p);
 
index d7dd62bb8346c4cac8dbd7334e999a450c21c5ab..0f8ad2c00f21d9a0a64b541ed95fff91650d7175 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "pack.h"
+#include "pack-revindex.h"
 
 struct idx_entry
 {
@@ -101,8 +102,10 @@ static int verify_packfile(struct packed_git *p,
 static void show_pack_info(struct packed_git *p)
 {
        uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
+
        nr_objects = p->num_objects;
        memset(chain_histogram, 0, sizeof(chain_histogram));
+       init_pack_revindex();
 
        for (i = 0; i < nr_objects; i++) {
                const unsigned char *sha1;
@@ -125,11 +128,11 @@ static void show_pack_info(struct packed_git *p)
                                                 base_sha1);
                printf("%s ", sha1_to_hex(sha1));
                if (!delta_chain_length)
-                       printf("%-6s %lu %"PRIuMAX"\n",
-                              type, size, (uintmax_t)offset);
+                       printf("%-6s %lu %lu %"PRIuMAX"\n",
+                              type, size, store_size, (uintmax_t)offset);
                else {
-                       printf("%-6s %lu %"PRIuMAX" %u %s\n",
-                              type, size, (uintmax_t)offset,
+                       printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
+                              type, size, store_size, (uintmax_t)offset,
                               delta_chain_length, sha1_to_hex(base_sha1));
                        if (delta_chain_length <= MAX_CHAIN)
                                chain_histogram[delta_chain_length]++;
diff --git a/pack-revindex.c b/pack-revindex.c
new file mode 100644 (file)
index 0000000..a8aa2cd
--- /dev/null
@@ -0,0 +1,142 @@
+#include "cache.h"
+#include "pack-revindex.h"
+
+/*
+ * Pack index for existing packs give us easy access to the offsets into
+ * corresponding pack file where each object's data starts, but the entries
+ * do not store the size of the compressed representation (uncompressed
+ * size is easily available by examining the pack entry header).  It is
+ * also rather expensive to find the sha1 for an object given its offset.
+ *
+ * We build a hashtable of existing packs (pack_revindex), and keep reverse
+ * index here -- pack index file is sorted by object name mapping to offset;
+ * this pack_revindex[].revindex array is a list of offset/index_nr pairs
+ * ordered by offset, so if you know the offset of an object, next offset
+ * is where its packed representation ends and the index_nr can be used to
+ * get the object sha1 from the main index.
+ */
+
+struct pack_revindex {
+       struct packed_git *p;
+       struct revindex_entry *revindex;
+};
+
+static struct pack_revindex *pack_revindex;
+static int pack_revindex_hashsz;
+
+static int pack_revindex_ix(struct packed_git *p)
+{
+       unsigned long ui = (unsigned long)p;
+       int i;
+
+       ui = ui ^ (ui >> 16); /* defeat structure alignment */
+       i = (int)(ui % pack_revindex_hashsz);
+       while (pack_revindex[i].p) {
+               if (pack_revindex[i].p == p)
+                       return i;
+               if (++i == pack_revindex_hashsz)
+                       i = 0;
+       }
+       return -1 - i;
+}
+
+void init_pack_revindex(void)
+{
+       int num;
+       struct packed_git *p;
+
+       for (num = 0, p = packed_git; p; p = p->next)
+               num++;
+       if (!num)
+               return;
+       pack_revindex_hashsz = num * 11;
+       pack_revindex = xcalloc(sizeof(*pack_revindex), pack_revindex_hashsz);
+       for (p = packed_git; p; p = p->next) {
+               num = pack_revindex_ix(p);
+               num = - 1 - num;
+               pack_revindex[num].p = p;
+       }
+       /* revindex elements are lazily initialized */
+}
+
+static int cmp_offset(const void *a_, const void *b_)
+{
+       const struct revindex_entry *a = a_;
+       const struct revindex_entry *b = b_;
+       return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
+}
+
+/*
+ * Ordered list of offsets of objects in the pack.
+ */
+static void create_pack_revindex(struct pack_revindex *rix)
+{
+       struct packed_git *p = rix->p;
+       int num_ent = p->num_objects;
+       int i;
+       const char *index = p->index_data;
+
+       rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
+       index += 4 * 256;
+
+       if (p->index_version > 1) {
+               const uint32_t *off_32 =
+                       (uint32_t *)(index + 8 + p->num_objects * (20 + 4));
+               const uint32_t *off_64 = off_32 + p->num_objects;
+               for (i = 0; i < num_ent; i++) {
+                       uint32_t off = ntohl(*off_32++);
+                       if (!(off & 0x80000000)) {
+                               rix->revindex[i].offset = off;
+                       } else {
+                               rix->revindex[i].offset =
+                                       ((uint64_t)ntohl(*off_64++)) << 32;
+                               rix->revindex[i].offset |=
+                                       ntohl(*off_64++);
+                       }
+                       rix->revindex[i].nr = i;
+               }
+       } else {
+               for (i = 0; i < num_ent; i++) {
+                       uint32_t hl = *((uint32_t *)(index + 24 * i));
+                       rix->revindex[i].offset = ntohl(hl);
+                       rix->revindex[i].nr = i;
+               }
+       }
+
+       /* This knows the pack format -- the 20-byte trailer
+        * follows immediately after the last object data.
+        */
+       rix->revindex[num_ent].offset = p->pack_size - 20;
+       rix->revindex[num_ent].nr = -1;
+       qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
+}
+
+struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
+{
+       int num;
+       int lo, hi;
+       struct pack_revindex *rix;
+       struct revindex_entry *revindex;
+
+       num = pack_revindex_ix(p);
+       if (num < 0)
+               die("internal error: pack revindex uninitialized");
+
+       rix = &pack_revindex[num];
+       if (!rix->revindex)
+               create_pack_revindex(rix);
+       revindex = rix->revindex;
+
+       lo = 0;
+       hi = p->num_objects + 1;
+       do {
+               int mi = (lo + hi) / 2;
+               if (revindex[mi].offset == ofs) {
+                       return revindex + mi;
+               } else if (ofs < revindex[mi].offset)
+                       hi = mi;
+               else
+                       lo = mi + 1;
+       } while (lo < hi);
+       die("internal error: pack revindex corrupt");
+}
diff --git a/pack-revindex.h b/pack-revindex.h
new file mode 100644 (file)
index 0000000..c3527a7
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef PACK_REVINDEX_H
+#define PACK_REVINDEX_H
+
+struct revindex_entry {
+       off_t offset;
+       unsigned int nr;
+};
+
+void init_pack_revindex(void);
+struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);
+
+#endif
diff --git a/path.c b/path.c
index 42609524a55ad017557d48bedf26906cc4405d0a..f4ed979997b6a968ba1987a199beae39035d5da2 100644 (file)
--- a/path.c
+++ b/path.c
@@ -283,7 +283,7 @@ int adjust_shared_perm(const char *path)
                            ? (S_IXGRP|S_IXOTH)
                            : 0));
        if (S_ISDIR(mode))
-               mode |= S_ISGID;
+               mode |= FORCE_DIR_SET_GID;
        if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
                return -2;
        return 0;
@@ -311,8 +311,10 @@ const char *make_absolute_path(const char *path)
                        if (last_slash) {
                                *last_slash = '\0';
                                last_elem = xstrdup(last_slash + 1);
-                       } else
+                       } else {
                                last_elem = xstrdup(buf);
+                               *buf = '\0';
+                       }
                }
 
                if (*buf) {
diff --git a/quote.c b/quote.c
index d061626c34f1e62c538db6e931c29751be4fdbcf..d5cf9d8f94f37fe8ff9f964998c7f0525617e5bc 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -260,6 +260,48 @@ extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
        fputc(terminator, fp);
 }
 
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+                         struct strbuf *out, const char *prefix)
+{
+       int needquote;
+
+       if (len < 0)
+               len = strlen(in);
+
+       /* "../" prefix itself does not need quoting, but "in" might. */
+       needquote = next_quote_pos(in, len) < len;
+       strbuf_setlen(out, 0);
+       strbuf_grow(out, len);
+
+       if (needquote)
+               strbuf_addch(out, '"');
+       if (prefix) {
+               int off = 0;
+               while (prefix[off] && off < len && prefix[off] == in[off])
+                       if (prefix[off] == '/') {
+                               prefix += off + 1;
+                               in += off + 1;
+                               len -= off + 1;
+                               off = 0;
+                       } else
+                               off++;
+
+               for (; *prefix; prefix++)
+                       if (*prefix == '/')
+                               strbuf_addstr(out, "../");
+       }
+
+       quote_c_style_counted (in, len, out, NULL, 1);
+
+       if (needquote)
+               strbuf_addch(out, '"');
+       if (!out->len)
+               strbuf_addstr(out, "./");
+
+       return out->buf;
+}
+
 /*
  * C-style name unquoting.
  *
@@ -288,7 +330,7 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
                switch (*quoted++) {
                  case '"':
                        if (endp)
-                               *endp = quoted + 1;
+                               *endp = quoted;
                        return 0;
                  case '\\':
                        break;
diff --git a/quote.h b/quote.h
index 4da110ec01331f346705199dde709436622967cb..c5eea6f18e2dfabd071b73e6507c34c2b7b5e39f 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -47,6 +47,10 @@ extern void write_name_quoted(const char *name, FILE *, int terminator);
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
                                  const char *name, FILE *, int terminator);
 
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+                         struct strbuf *out, const char *prefix);
+
 /* quoting as a string literal for other languages */
 extern void perl_quote_print(FILE *stream, const char *src);
 extern void python_quote_print(FILE *stream, const char *src);
index a971433db155bbac162cece038b73dc64e682ac0..f83ae87e150ff93728da989f1d35ce0ad7c10f60 100644 (file)
@@ -471,6 +471,8 @@ int main(int argc, char **argv)
        if (!dir)
                usage(receive_pack_usage);
 
+       setup_path(NULL);
+
        if (!enter_repo(dir, 0))
                die("'%s': unable to chdir or not a git archive", dir);
 
diff --git a/refs.c b/refs.c
index c979fb1d9507309785909a8d714ab28b1efd2e88..1b0050eee4e037aec8396edd0ff6c14bda5985a2 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1033,7 +1033,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        return 1;
 }
 
-static int close_ref(struct ref_lock *lock)
+int close_ref(struct ref_lock *lock)
 {
        if (close_lock_file(lock->lk))
                return -1;
@@ -1041,7 +1041,7 @@ static int close_ref(struct ref_lock *lock)
        return 0;
 }
 
-static int commit_ref(struct ref_lock *lock)
+int commit_ref(struct ref_lock *lock)
 {
        if (commit_lock_file(lock->lk))
                return -1;
diff --git a/refs.h b/refs.h
index 9cd16f82956d89f2800827046151abd7866fb9da..06abee15266cc3b234ec64cd27362c482874e54b 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -33,6 +33,12 @@ extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_
 #define REF_NODEREF    0x01
 extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
 
+/** Close the file descriptor owned by a lock and return the status */
+extern int close_ref(struct ref_lock *lock);
+
+/** Close and commit the ref locked by the lock */
+extern int commit_ref(struct ref_lock *lock);
+
 /** Release any lock taken but not written. **/
 extern void unlock_ref(struct ref_lock *lock);
 
index 0eb6faa5378ebafe2c4939cb4ec7fd2e6aaadb02..63bf2c5c2dd314cfff2f54f76c4f5fd85b3b207e 100644 (file)
@@ -772,14 +772,9 @@ static void prepare_show_merge(struct rev_info *revs)
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
        bases = get_merge_bases(head, other, 1);
-       while (bases) {
-               struct commit *it = bases->item;
-               struct commit_list *n = bases->next;
-               free(bases);
-               bases = n;
-               it->object.flags |= UNINTERESTING;
-               add_pending_object(revs, &it->object, "(merge-base)");
-       }
+       add_pending_commit_list(revs, bases, UNINTERESTING);
+       free_commit_list(bases);
+       head->object.flags |= SYMMETRIC_LEFT;
 
        if (!active_nr)
                read_cache();
@@ -798,6 +793,7 @@ static void prepare_show_merge(struct rev_info *revs)
                        i++;
        }
        revs->prune_data = prune;
+       revs->limited = 1;
 }
 
 int handle_revision_arg(const char *arg, struct rev_info *revs,
index 743757c36ec0e5667fd8af28800ac095f1581adb..44100a749b09439121073e32e7a3143b54185d31 100644 (file)
@@ -91,6 +91,13 @@ int start_command(struct child_process *cmd)
                        close(cmd->in);
                }
 
+               if (cmd->no_stderr)
+                       dup_devnull(2);
+               else if (need_err) {
+                       dup2(fderr[1], 2);
+                       close_pair(fderr);
+               }
+
                if (cmd->no_stdout)
                        dup_devnull(1);
                else if (cmd->stdout_to_stderr)
@@ -103,13 +110,6 @@ int start_command(struct child_process *cmd)
                        close(cmd->out);
                }
 
-               if (cmd->no_stderr)
-                       dup_devnull(2);
-               else if (need_err) {
-                       dup2(fderr[1], 2);
-                       close_pair(fderr);
-               }
-
                if (cmd->dir && chdir(cmd->dir))
                        die("exec %s: cd to %s failed (%s)", cmd->argv[0],
                            cmd->dir, strerror(errno));
diff --git a/setup.c b/setup.c
index 89c81e54e6d25d7ba2bec8831621283f32fd3108..41e298b8f549dcce28fca335c5f5b7fe5aea27bb 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -202,6 +202,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
                const char *p = prefix_path(prefix, prefixlen, *src);
                if (p)
                        *(dst++) = p;
+               else
+                       exit(128); /* error message already given */
                src++;
        }
        *dst = NULL;
index 1ddb96bb82b60f4da8489664d13f398d88bf5a15..445a871db31673af20017d36ff22fd106f77f510 100644 (file)
@@ -14,6 +14,7 @@
 #include "tag.h"
 #include "tree.h"
 #include "refs.h"
+#include "pack-revindex.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -1367,11 +1368,15 @@ const char *packed_object_info_detail(struct packed_git *p,
        unsigned long dummy;
        unsigned char *next_sha1;
        enum object_type type;
+       struct revindex_entry *revidx;
 
        *delta_chain_length = 0;
        curpos = obj_offset;
        type = unpack_object_header(p, &w_curs, &curpos, size);
 
+       revidx = find_pack_revindex(p, obj_offset);
+       *store_size = revidx[1].offset - obj_offset;
+
        for (;;) {
                switch (type) {
                default:
@@ -1381,14 +1386,13 @@ const char *packed_object_info_detail(struct packed_git *p,
                case OBJ_TREE:
                case OBJ_BLOB:
                case OBJ_TAG:
-                       *store_size = 0; /* notyet */
                        unuse_pack(&w_curs);
                        return typename(type);
                case OBJ_OFS_DELTA:
                        obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
                        if (*delta_chain_length == 0) {
-                               /* TODO: find base_sha1 as pointed by curpos */
-                               hashclr(base_sha1);
+                               revidx = find_pack_revindex(p, obj_offset);
+                               hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr));
                        }
                        break;
                case OBJ_REF_DELTA:
index 9d088cc2caa24ad79b927b9078771e4f0fb22a69..8358ba2069746943fcf45d35222d95fab7a8231d 100644 (file)
@@ -192,26 +192,25 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
 
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
-       int status, is_null;
+       int status, exists;
        static char hex[41];
 
-       is_null = is_null_sha1(sha1);
+       exists = has_sha1_file(sha1);
        memcpy(hex, sha1_to_hex(sha1), 40);
        if (len == 40 || !len)
                return hex;
        while (len < 40) {
                unsigned char sha1_ret[20];
                status = get_short_sha1(hex, len, sha1_ret, 1);
-               if (!status ||
-                   (is_null && status != SHORT_NAME_AMBIGUOUS)) {
+               if (exists
+                   ? !status
+                   : status == SHORT_NAME_NOT_FOUND) {
                        hex[len] = 0;
                        return hex;
                }
-               if (status != SHORT_NAME_AMBIGUOUS)
-                       return NULL;
                len++;
        }
-       return NULL;
+       return hex;
 }
 
 static int ambiguous_path(const char *path, int len)
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
new file mode 100644 (file)
index 0000000..7f206c5
--- /dev/null
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+if test -z "$GIT_TEST_HTTPD"
+then
+       say "skipping test, network testing disabled by default"
+       say "(define GIT_TEST_HTTPD to enable)"
+       test_done
+       exit
+fi
+
+LIB_HTTPD_PATH=${LIB_HTTPD_PATH-'/usr/sbin/apache2'}
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
+
+TEST_PATH="$PWD"/../lib-httpd
+HTTPD_ROOT_PATH="$PWD"/httpd
+HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
+
+if ! test -x "$LIB_HTTPD_PATH"
+then
+        say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
+        test_done
+        exit
+fi
+
+HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
+       sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q'`
+
+if test -n "$HTTPD_VERSION"
+then
+       if test -z "$LIB_HTTPD_MODULE_PATH"
+       then
+               if ! test $HTTPD_VERSION -ge 2
+               then
+                       say "skipping test, at least Apache version 2 is required"
+                       test_done
+                       exit
+               fi
+
+               LIB_HTTPD_MODULE_PATH='/usr/lib/apache2/modules'
+       fi
+else
+       error "Could not identify web server at '$LIB_HTTPD_PATH'"
+fi
+
+HTTPD_PARA="-d $HTTPD_ROOT_PATH -f $TEST_PATH/apache.conf"
+
+prepare_httpd() {
+       mkdir -p $HTTPD_DOCUMENT_ROOT_PATH
+
+       ln -s $LIB_HTTPD_MODULE_PATH $HTTPD_ROOT_PATH/modules
+
+       if test -n "$LIB_HTTPD_SSL"
+       then
+               HTTPD_URL=https://127.0.0.1:$LIB_HTTPD_PORT
+
+               RANDFILE_PATH="$HTTPD_ROOT_PATH"/.rnd openssl req \
+                       -config $TEST_PATH/ssl.cnf \
+                       -new -x509 -nodes \
+                       -out $HTTPD_ROOT_PATH/httpd.pem \
+                       -keyout $HTTPD_ROOT_PATH/httpd.pem
+               export GIT_SSL_NO_VERIFY=t
+               HTTPD_PARA="$HTTPD_PARA -DSSL"
+       else
+               HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
+       fi
+
+       if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
+       then
+               HTTPD_PARA="$HTTPD_PARA -DDAV"
+
+               if test -n "$LIB_HTTPD_SVN"
+               then
+                       HTTPD_PARA="$HTTPD_PARA -DSVN"
+                       rawsvnrepo="$HTTPD_ROOT_PATH/svnrepo"
+                       svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/svn"
+               fi
+       fi
+}
+
+start_httpd() {
+       prepare_httpd
+
+       trap 'stop_httpd; die' exit
+
+       "$LIB_HTTPD_PATH" $HTTPD_PARA \
+               -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start
+}
+
+stop_httpd() {
+       trap 'die' exit
+
+       "$LIB_HTTPD_PATH" $HTTPD_PARA -k stop
+}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
new file mode 100644 (file)
index 0000000..a447346
--- /dev/null
@@ -0,0 +1,34 @@
+PidFile httpd.pid
+DocumentRoot www
+ErrorLog error.log
+
+<IfDefine SSL>
+LoadModule ssl_module modules/mod_ssl.so
+
+SSLCertificateFile httpd.pem
+SSLCertificateKeyFile httpd.pem
+SSLRandomSeed startup file:/dev/urandom 512
+SSLRandomSeed connect file:/dev/urandom 512
+SSLSessionCache none
+SSLMutex file:ssl_mutex
+SSLEngine On
+</IfDefine>
+
+<IfDefine DAV>
+       LoadModule dav_module modules/mod_dav.so
+       LoadModule dav_fs_module modules/mod_dav_fs.so
+
+       DAVLockDB DAVLock
+       <Location />
+               Dav on
+       </Location>
+</IfDefine>
+
+<IfDefine SVN>
+       LoadModule dav_svn_module modules/mod_dav_svn.so
+
+       <Location /svn>
+               DAV svn
+               SVNPath svnrepo
+       </Location>
+</IfDefine>
diff --git a/t/lib-httpd/ssl.cnf b/t/lib-httpd/ssl.cnf
new file mode 100644 (file)
index 0000000..6dab257
--- /dev/null
@@ -0,0 +1,8 @@
+RANDFILE                = $ENV::RANDFILE_PATH
+
+[ req ]
+default_bits            = 1024
+distinguished_name      = req_distinguished_name
+prompt                  = no
+[ req_distinguished_name ]
+commonName              = 127.0.0.1
index 92de08822702f780c6895a5d2c070ee009ecfef7..27b54cbb1216beef8b5f315708574825a1c81da3 100755 (executable)
@@ -304,6 +304,8 @@ test_expect_success 'absolute path works as expected' '
        test "$dir" = "$(test-absolute-path $dir2)" &&
        file="$dir"/index &&
        test "$file" = "$(test-absolute-path $dir2/index)" &&
+       basename=blub &&
+       test "$dir/$basename" = $(cd .git && test-absolute-path $basename) &&
        ln -s ../first/file .git/syml &&
        sym="$(cd first; pwd -P)"/file &&
        test "$sym" = "$(test-absolute-path $dir2/syml)"
index f959aae84630ddbb68304868b1a025b7c2d33d10..24476bede5ce8f8720bf3d9eba7f7a944182dbf4 100755 (executable)
@@ -175,6 +175,33 @@ test_expect_success 'recover and check' '
 
 '
 
+test_expect_success 'delete' '
+       echo 1 > C &&
+       test_tick &&
+       git commit -m rat C &&
+
+       echo 2 > C &&
+       test_tick &&
+       git commit -m ox C &&
+
+       echo 3 > C &&
+       test_tick &&
+       git commit -m tiger C &&
+
+       test 5 = $(git reflog | wc -l) &&
+
+       git reflog delete master@{1} &&
+       git reflog show master > output &&
+       test 4 = $(wc -l < output) &&
+       ! grep ox < output &&
+
+       git reflog delete master@{07.04.2005.15:15:00.-0700} &&
+       git reflog show master > output &&
+       test 3 = $(wc -l < output) &&
+       ! grep dragon < output
+
+'
+
 test_expect_success 'prune --expire' '
 
        before=$(git count-objects | sed "s/ .*//") &&
index 39fe2676dcd8e22451309d2321dee45410f90963..70f9ce9d52827cb35fee7ac0f13471023291d1fa 100755 (executable)
@@ -120,7 +120,7 @@ EOF
 # having 1.txt and path3
 test_expect_success \
     'ls-tree filter odd names' \
-    'git ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
+    'git ls-tree $tree 1.txt ./1.txt .//1.txt path3/1.txt path3/./1.txt path3 path3// >current &&
      cat >expected <<\EOF &&
 100644 blob X  1.txt
 100644 blob X  path3/1.txt
index 94bdd723306c7795c8a17095bad0925b662345d4..37944c39a3be5e8d2db59faa5609d857fe902199 100755 (executable)
@@ -23,37 +23,49 @@ test_expect_success setup '
        git branch pre-rebase
 '
 
-test_expect_success 'rebase --abort' '
-       test_must_fail git rebase master &&
-       git rebase --abort &&
-       test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
-'
+testrebase() {
+       type=$1
+       dotest=$2
 
-test_expect_failure 'rebase --abort after --skip' '
-       # Clean up the state from the previous one
-       git reset --hard pre-rebase
-       rm -rf .dotest
+       test_expect_success "rebase$type --abort" '
+               # Clean up the state from the previous one
+               git reset --hard pre-rebase
+               test_must_fail git rebase'"$type"' master &&
+               test -d '$dotest' &&
+               git rebase --abort &&
+               test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+               test ! -d '$dotest'
+       '
 
-       test_must_fail git rebase master &&
-       test_must_fail git rebase --skip &&
-       test $(git rev-parse HEAD) = $(git rev-parse master) &&
-       git rebase --abort &&
-       test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
-'
+       test_expect_success "rebase$type --abort after --skip" '
+               # Clean up the state from the previous one
+               git reset --hard pre-rebase
+               test_must_fail git rebase'"$type"' master &&
+               test -d '$dotest' &&
+               test_must_fail git rebase --skip &&
+               test $(git rev-parse HEAD) = $(git rev-parse master) &&
+               git-rebase --abort &&
+               test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+               test ! -d '$dotest'
+       '
 
-test_expect_success 'rebase --abort after --continue' '
-       # Clean up the state from the previous one
-       git reset --hard pre-rebase
-       rm -rf .dotest
+       test_expect_success "rebase$type --abort after --continue" '
+               # Clean up the state from the previous one
+               git reset --hard pre-rebase
+               test_must_fail git rebase'"$type"' master &&
+               test -d '$dotest' &&
+               echo c > a &&
+               echo d >> a &&
+               git add a &&
+               test_must_fail git rebase --continue &&
+               test $(git rev-parse HEAD) != $(git rev-parse master) &&
+               git rebase --abort &&
+               test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+               test ! -d '$dotest'
+       '
+}
 
-       test_must_fail git rebase master &&
-       echo c > a &&
-       echo d >> a &&
-       git add a &&
-       test_must_fail git rebase --continue &&
-       test $(git rev-parse HEAD) != $(git rev-parse master) &&
-       git rebase --abort &&
-       test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
-'
+testrebase "" .dotest
+testrebase " --merge" .git/.dotest-merge
 
 test_done
index 2dbe04fb20aea9a18e6fe751539bcb48e5e7b4d3..6da212825a447866364979c2fb10778b6bbc02d5 100755 (executable)
@@ -59,4 +59,13 @@ test_expect_success 'revert after renaming branch' '
 
 '
 
+test_expect_success 'revert forbidden on dirty working tree' '
+
+       echo content >extra_file &&
+       git add extra_file &&
+       test_must_fail git revert HEAD 2>errors &&
+       grep "Dirty index" errors
+
+'
+
 test_done
index 9a9a250d2c6bad80745b7ebd2df7ac947d30b521..aa282e1bc1e17f08dd12fc1980187c786f1de99b 100755 (executable)
@@ -40,8 +40,8 @@ test_expect_success 'parents of stash' '
 test_expect_success 'apply needs clean working directory' '
        echo 4 > other-file &&
        git add other-file &&
-       echo 5 > other-file
-       ! git stash apply
+       echo 5 > other-file &&
+       test_must_fail git stash apply
 '
 
 test_expect_success 'apply stashed changes' '
@@ -70,7 +70,51 @@ test_expect_success 'unstashing in a subdirectory' '
        git reset --hard HEAD &&
        mkdir subdir &&
        cd subdir &&
-       git stash apply
+       git stash apply &&
+       cd ..
+'
+
+test_expect_success 'drop top stash' '
+       git reset --hard &&
+       git stash list > stashlist1 &&
+       echo 7 > file &&
+       git stash &&
+       git stash drop &&
+       git stash list > stashlist2 &&
+       diff stashlist1 stashlist2 &&
+       git stash apply &&
+       test 3 = $(cat file) &&
+       test 1 = $(git show :file) &&
+       test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'drop middle stash' '
+       git reset --hard &&
+       echo 8 > file &&
+       git stash &&
+       echo 9 > file &&
+       git stash &&
+       git stash drop stash@{1} &&
+       test 2 = $(git stash list | wc -l) &&
+       git stash apply &&
+       test 9 = $(cat file) &&
+       test 1 = $(git show :file) &&
+       test 1 = $(git show HEAD:file) &&
+       git reset --hard &&
+       git stash drop &&
+       git stash apply &&
+       test 3 = $(cat file) &&
+       test 1 = $(git show :file) &&
+       test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'stash pop' '
+       git reset --hard &&
+       git stash pop &&
+       test 3 = $(cat file) &&
+       test 1 = $(git show :file) &&
+       test 1 = $(git show HEAD:file) &&
+       test 0 = $(git stash list | wc -l)
 '
 
 test_done
index 0151453b73c3f57f8594d550b3118616b423b4d2..8dab4bf93ebd5f3e5ee6e899890d6e53af9ab967 100644 (file)
@@ -7,8 +7,8 @@ Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***
 *** BLURB HERE ***
 
 A U Thor (2):
-      Second
-      Third
+  Second
+  Third
 
  dir/sub |    4 ++++
  file0   |    3 +++
index 16aa99dc0dbe1fe7e09aa046dfc2dc1ec652963c..b2b7a8db859fe7f5d8c74fb27b77e4eba583ebc4 100755 (executable)
@@ -147,7 +147,7 @@ test_expect_success 'thread' '
        for i in patches/0002-* patches/0003-*
        do
          grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i
+         grep "In-Reply-To: $FIRST_MID" $i || break
        done
 '
 
@@ -160,7 +160,7 @@ test_expect_success 'thread in-reply-to' '
        for i in patches/*
        do
          grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i
+         grep "In-Reply-To: $FIRST_MID" $i || break
        done
 '
 
@@ -173,7 +173,7 @@ test_expect_success 'thread cover-letter' '
        for i in patches/0001-* patches/0002-* patches/0003-* 
        do
          grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i
+         grep "In-Reply-To: $FIRST_MID" $i || break
        done
 '
 
@@ -186,7 +186,7 @@ test_expect_success 'thread cover-letter in-reply-to' '
        for i in patches/*
        do
          grep "References: $FIRST_MID" $i &&
-         grep "In-Reply-To: $FIRST_MID" $i
+         grep "In-Reply-To: $FIRST_MID" $i || break
        done
 '
 
@@ -201,4 +201,33 @@ test_expect_success 'excessive subject' '
        ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
 '
 
+test_expect_success 'cover-letter inherits diff options' '
+
+       git mv file foo &&
+       git commit -m foo &&
+       git format-patch --cover-letter -1 &&
+       ! grep "file => foo .* 0 *$" 0000-cover-letter.patch &&
+       git format-patch --cover-letter -1 -M &&
+       grep "file => foo .* 0 *$" 0000-cover-letter.patch
+
+'
+
+cat > expect << EOF
+  This is an excessively long subject line for a message due to the
+    habit some projects have of not having a short, one-line subject at
+    the start of the commit message, but rather sticking a whole
+    paragraph right at the start as the only thing in the commit
+    message. It had better not become the filename for the patch.
+  foo
+
+EOF
+
+test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
+
+       git format-patch --cover-letter -2 &&
+       sed -e "1,/A U Thor/d" -e "/^$/q" < 0000-cover-letter.patch > output &&
+       git diff expect output
+
+'
+
 test_done
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
new file mode 100755 (executable)
index 0000000..3d2d081
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='difference in submodules'
+
+. ./test-lib.sh
+. ../diff-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+test_expect_success setup '
+       test_tick &&
+       test_create_repo sub &&
+       (
+               cd sub &&
+               echo hello >world &&
+               git add world &&
+               git commit -m submodule
+       ) &&
+
+       test_tick &&
+       echo frotz >nitfol &&
+       git add nitfol sub &&
+       git commit -m superproject &&
+
+       (
+               cd sub &&
+               echo goodbye >world &&
+               git add world &&
+               git commit -m "submodule #2"
+       ) &&
+
+       set x $(
+               cd sub &&
+               git rev-list HEAD
+       ) &&
+       echo ":160000 160000 $3 $_z40 M sub" >expect
+'
+
+test_expect_success 'git diff --raw HEAD' '
+       git diff --raw --abbrev=40 HEAD >actual &&
+       diff -u expect actual
+'
+
+test_expect_success 'git diff-index --raw HEAD' '
+       git diff-index --raw HEAD >actual.index &&
+       diff -u expect actual.index
+'
+
+test_expect_success 'git diff-files --raw' '
+       git diff-files --raw >actual.files &&
+       diff -u expect actual.files
+'
+
+test_done
diff --git a/t/t4150-am-subdir.sh b/t/t4150-am-subdir.sh
new file mode 100755 (executable)
index 0000000..929d2cb
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='git am running from a subdirectory'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo hello >world &&
+       git add world &&
+       test_tick &&
+       git commit -m initial &&
+       git tag initial &&
+       echo goodbye >world &&
+       git add world &&
+       test_tick &&
+       git commit -m second &&
+       git format-patch --stdout HEAD^ >patchfile &&
+       : >expect
+'
+
+test_expect_success 'am regularly from stdin' '
+       git checkout initial &&
+       git am <patchfile &&
+       git diff master >actual &&
+       diff -u expect actual
+'
+
+test_expect_success 'am regularly from file' '
+       git checkout initial &&
+       git am patchfile &&
+       git diff master >actual &&
+       diff -u expect actual
+'
+
+test_expect_success 'am regularly from stdin in subdirectory' '
+       rm -fr subdir &&
+       git checkout initial &&
+       (
+               mkdir -p subdir &&
+               cd subdir &&
+               git am <../patchfile
+       ) &&
+       git diff master>actual &&
+       diff -u expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory' '
+       rm -fr subdir &&
+       git checkout initial &&
+       (
+               mkdir -p subdir &&
+               cd subdir &&
+               git am ../patchfile
+       ) &&
+       git diff master >actual &&
+       diff -u expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory with full path' '
+       rm -fr subdir &&
+       git checkout initial &&
+       P=$(pwd) &&
+       (
+               mkdir -p subdir &&
+               cd subdir &&
+               git am "$P/patchfile"
+       ) &&
+       git diff master >actual &&
+       diff -u expect actual
+'
+
+test_done
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
new file mode 100755 (executable)
index 0000000..0db2754
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='git-pack-object --include-tag'
+. ./test-lib.sh
+
+TRASH=`pwd`
+
+test_expect_success setup '
+       echo c >d &&
+       git update-index --add d &&
+       tree=`git write-tree` &&
+       commit=`git commit-tree $tree </dev/null` &&
+       echo "object $commit" >sig &&
+       echo "type commit" >>sig &&
+       echo "tag mytag" >>sig &&
+       echo "tagger $(git var GIT_COMMITTER_IDENT)" >>sig &&
+       echo >>sig &&
+       echo "our test tag" >>sig &&
+       tag=`git mktag <sig` &&
+       rm d sig &&
+       git update-ref refs/tags/mytag $tag && {
+               echo $tree &&
+               echo $commit &&
+               git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\)        .*/\\1/"
+       } >obj-list
+'
+
+rm -rf clone.git
+test_expect_success 'pack without --include-tag' '
+       packname_1=$(git pack-objects \
+               --window=0 \
+               test-1 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+       (
+               GIT_DIR=clone.git &&
+               export GIT_DIR &&
+               git init &&
+               git unpack-objects -n <test-1-${packname_1}.pack &&
+               git unpack-objects <test-1-${packname_1}.pack
+       )
+'
+
+test_expect_success 'check unpacked result (have commit, no tag)' '
+       git rev-list --objects $commit >list.expect &&
+       (
+               GIT_DIR=clone.git &&
+               export GIT_DIR &&
+               test_must_fail git cat-file -e $tag &&
+               git rev-list --objects $commit
+       ) >list.actual &&
+       git diff list.expect list.actual
+'
+
+rm -rf clone.git
+test_expect_success 'pack with --include-tag' '
+       packname_1=$(git pack-objects \
+               --window=0 \
+               --include-tag \
+               test-2 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+       (
+               GIT_DIR=clone.git &&
+               export GIT_DIR &&
+               git init &&
+               git unpack-objects -n <test-2-${packname_1}.pack &&
+               git unpack-objects <test-2-${packname_1}.pack
+       )
+'
+
+test_expect_success 'check unpacked result (have commit, have tag)' '
+       git rev-list --objects mytag >list.expect &&
+       (
+               GIT_DIR=clone.git &&
+               export GIT_DIR &&
+               git rev-list --objects $tag
+       ) >list.actual &&
+       git diff list.expect list.actual
+'
+
+test_done
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
new file mode 100755 (executable)
index 0000000..86e5b9b
--- /dev/null
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+test_description='test automatic tag following'
+
+. ./test-lib.sh
+
+# End state of the repository:
+#
+#         T - tag1          S - tag2
+#        /                 /
+#   L - A ------ O ------ B
+#    \   \                 \
+#     \   C - origin/cat    \
+#      origin/master         master
+
+test_expect_success setup '
+       test_tick &&
+       echo ichi >file &&
+       git add file &&
+       git commit -m L &&
+       L=$(git rev-parse --verify HEAD) &&
+
+       (
+               mkdir cloned &&
+               cd cloned &&
+               git init-db &&
+               git remote add -f origin ..
+       ) &&
+
+       test_tick &&
+       echo A >file &&
+       git add file &&
+       git commit -m A &&
+       A=$(git rev-parse --verify HEAD)
+'
+
+U=UPLOAD_LOG
+
+cat - <<EOF >expect
+#S
+want $A
+#E
+EOF
+test_expect_success 'fetch A (new commit : 1 connection)' '
+       rm -f $U
+       (
+               cd cloned &&
+               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               test $A = $(git rev-parse --verify origin/master)
+       ) &&
+       test -s $U &&
+       cut -d" " -f1,2 $U >actual &&
+       git diff expect actual
+'
+
+test_expect_success "create tag T on A, create C on branch cat" '
+       git tag -a -m tag1 tag1 $A &&
+       T=$(git rev-parse --verify tag1) &&
+
+       git checkout -b cat &&
+       echo C >file &&
+       git add file &&
+       git commit -m C &&
+       C=$(git rev-parse --verify HEAD) &&
+       git checkout master
+'
+
+cat - <<EOF >expect
+#S
+want $C
+want $T
+#E
+EOF
+test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
+       rm -f $U
+       (
+               cd cloned &&
+               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               test $C = $(git rev-parse --verify origin/cat) &&
+               test $T = $(git rev-parse --verify tag1) &&
+               test $A = $(git rev-parse --verify tag1^0)
+       ) &&
+       test -s $U &&
+       cut -d" " -f1,2 $U >actual &&
+       git diff expect actual
+'
+
+test_expect_success "create commits O, B, tag S on B" '
+       test_tick &&
+       echo O >file &&
+       git add file &&
+       git commit -m O &&
+
+       test_tick &&
+       echo B >file &&
+       git add file &&
+       git commit -m B &&
+       B=$(git rev-parse --verify HEAD) &&
+
+       git tag -a -m tag2 tag2 $B &&
+       S=$(git rev-parse --verify tag2)
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
+       rm -f $U
+       (
+               cd cloned &&
+               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               test $B = $(git rev-parse --verify origin/master) &&
+               test $B = $(git rev-parse --verify tag2^0) &&
+               test $S = $(git rev-parse --verify tag2)
+       ) &&
+       test -s $U &&
+       cut -d" " -f1,2 $U >actual &&
+       git diff expect actual
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'new clone fetch master and tags' '
+       git branch -D cat
+       rm -f $U
+       (
+               mkdir clone2 &&
+               cd clone2 &&
+               git init &&
+               git remote add origin .. &&
+               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               test $B = $(git rev-parse --verify origin/master) &&
+               test $S = $(git rev-parse --verify tag2) &&
+               test $B = $(git rev-parse --verify tag2^0) &&
+               test $T = $(git rev-parse --verify tag1) &&
+               test $A = $(git rev-parse --verify tag1^0)
+       ) &&
+       test -s $U &&
+       cut -d" " -f1,2 $U >actual &&
+       git diff expect actual
+'
+
+test_done
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
new file mode 100755 (executable)
index 0000000..7372439
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test http-push
+
+This test runs various sanity checks on http-push.'
+
+. ./test-lib.sh
+
+ROOT_PATH="$PWD"
+LIB_HTTPD_DAV=t
+
+. ../lib-httpd.sh
+
+if ! start_httpd >&3 2>&4
+then
+       say "skipping test, web server setup failed"
+       test_done
+       exit
+fi
+
+test_expect_success 'setup remote repository' '
+       cd "$ROOT_PATH" &&
+       mkdir test_repo &&
+       cd test_repo &&
+       git init &&
+       : >path1 &&
+       git add path1 &&
+       test_tick &&
+       git commit -m initial &&
+       cd - &&
+       git clone --bare test_repo test_repo.git &&
+       cd test_repo.git &&
+       git --bare update-server-info &&
+       chmod +x hooks/post-update &&
+       cd - &&
+       mv test_repo.git $HTTPD_DOCUMENT_ROOT_PATH
+'
+       
+test_expect_success 'clone remote repository' '
+       cd "$ROOT_PATH" &&
+       git clone $HTTPD_URL/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       : >path2 &&
+       git add path2 &&
+       test_tick &&
+       git commit -m path2 &&
+       git push
+'
+
+test_expect_success 'create and delete remote branch' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       git checkout -b dev &&
+       : >path3 &&
+       git add path3 &&
+       test_tick &&
+       git commit -m dev &&
+       git push origin dev &&
+       git fetch &&
+       git push origin :dev &&
+       git branch -d -r origin/dev &&
+       git fetch &&
+       ! git show-ref --verify refs/remotes/origin/dev
+'
+
+stop_httpd
+
+test_done
index ae8ee11183833fd4610adb1f83763eba381f7850..56bbd8519d69a1fc31293f30eb3c8a7b20a285cc 100755 (executable)
@@ -15,8 +15,11 @@ test_description='test describe
 check_describe () {
        expect="$1"
        shift
-       R=$(git describe "$@") &&
+       R=$(git describe "$@" 2>err.actual)
+       S=$?
+       cat err.actual >&3
        test_expect_success "describe $*" '
+       test $S = 0 &&
        case "$R" in
        $expect)        echo happy ;;
        *)      echo "Oops - $R is not $expect";
@@ -94,4 +97,24 @@ check_describe D-* --tags HEAD^^
 check_describe A-* --tags HEAD^^2
 check_describe B --tags HEAD^^2^
 
+check_describe B-0-* --long HEAD^^2^
+check_describe A-3-* --long HEAD^^2
+
+test_expect_success 'rename tag A to Q locally' '
+       mv .git/refs/tags/A .git/refs/tags/Q
+'
+cat - >err.expect <<EOF
+warning: tag 'A' is really 'Q' here
+EOF
+check_describe A-* HEAD
+test_expect_success 'warning was displayed for Q' '
+       git diff err.expect err.actual
+'
+test_expect_success 'rename tag Q back to A' '
+       mv .git/refs/tags/Q .git/refs/tags/A
+'
+
+test_expect_success 'pack tag refs' 'git pack-refs'
+check_describe A-* HEAD
+
 test_done
index 868babc4b2350526192a8df53f6cbec3f81c644e..6e14bf1c7f0991bbaab217e8a08bae0cacfe1ab1 100755 (executable)
@@ -179,4 +179,28 @@ test_expect_success 'Name needing quotes' '
 
 '
 
+test_expect_success 'Subdirectory filter with disappearing trees' '
+       git reset --hard &&
+       git checkout master &&
+
+       mkdir foo &&
+       touch foo/bar &&
+       git add foo &&
+       test_tick &&
+       git commit -m "Adding foo" &&
+
+       git rm -r foo &&
+       test_tick &&
+       git commit -m "Removing foo" &&
+
+       mkdir foo &&
+       touch foo/bar &&
+       git add foo &&
+       test_tick &&
+       git commit -m "Re-adding foo" &&
+
+       git filter-branch -f --subdirectory-filter foo &&
+       test $(git rev-list master | wc -l) = 3
+'
+
 test_done
index e809e0e2c9ff491cdb288386baff6b1c016493ef..bc8ab6a61913169325de3b04fee17d04033b54e1 100755 (executable)
@@ -142,15 +142,16 @@ test_expect_success 'setup deeper work tree' '
 test_expect_success 'add a directory outside the work tree' '(
        cd tester &&
        d1="$(cd .. ; pwd)" &&
-       git add "$d1"
+       test_must_fail git add "$d1"
 )'
 
+
 test_expect_success 'add a file outside the work tree, nasty case 1' '(
        cd tester &&
        f="$(pwd)x" &&
        echo "$f" &&
        touch "$f" &&
-       git add "$f"
+       test_must_fail git add "$f"
 )'
 
 test_expect_success 'add a file outside the work tree, nasty case 2' '(
@@ -158,7 +159,7 @@ test_expect_success 'add a file outside the work tree, nasty case 2' '(
        f="$(pwd | sed "s/.$//")x" &&
        echo "$f" &&
        touch "$f" &&
-       git add "$f"
+       test_must_fail git add "$f"
 )'
 
 test_done
index 38403643a6271ae4e6bb5a9c7d768ae4b50df34d..afccfc9973ba864167387b1e1caf6fe8b12f68f9 100755 (executable)
@@ -89,6 +89,58 @@ test_expect_success 'git-clean with prefix' '
        test -f build/lib.so
 
 '
+
+test_expect_success 'git-clean with relative prefix' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       would_clean=$(
+               cd docs &&
+               git clean -n ../src |
+               sed -n -e "s|^Would remove ||p"
+       ) &&
+       test "$would_clean" = ../src/part3.c || {
+               echo "OOps <$would_clean>"
+               false
+       }
+'
+
+test_expect_success 'git-clean with absolute path' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       would_clean=$(
+               cd docs &&
+               git clean -n $(pwd)/../src |
+               sed -n -e "s|^Would remove ||p"
+       ) &&
+       test "$would_clean" = ../src/part3.c || {
+               echo "OOps <$would_clean>"
+               false
+       }
+'
+
+test_expect_success 'git-clean with out of work tree relative path' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       (
+               cd docs &&
+               test_must_fail git clean -n ../..
+       )
+'
+
+test_expect_success 'git-clean with out of work tree absolute path' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       dd=$(cd .. && pwd) &&
+       (
+               cd docs &&
+               test_must_fail git clean -n $dd
+       )
+'
+
 test_expect_success 'git-clean -d with prefix and path' '
 
        mkdir -p build docs src/feature &&
@@ -320,8 +372,9 @@ test_expect_success 'removal failure' '
 
        mkdir foo &&
        touch foo/bar &&
+       exec <foo/bar &&
        chmod 0 foo &&
-       ! git clean -f -d
+       test_must_fail git clean -f -d
 
 '
 chmod 755 foo
index 50c51c82facdf30fffa4517763c84ce862aa8c27..5d166280cbb93ef4e22f04090d510bfd5b09fb7a 100755 (executable)
@@ -419,6 +419,7 @@ test_debug 'gitk --all'
 
 test_expect_success 'merge c0 with c1 (no-ff)' '
        git reset --hard c0 &&
+       git config branch.master.mergeoptions "" &&
        test_tick &&
        git merge --no-ff c1 &&
        verify_merge file result.1 &&
@@ -427,6 +428,11 @@ test_expect_success 'merge c0 with c1 (no-ff)' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'combining --squash and --no-ff is refused' '
+       test_must_fail git merge --squash --no-ff c1 &&
+       test_must_fail git merge --no-ff --squash c1
+'
+
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
        git reset --hard c0 &&
        git config branch.master.mergeoptions "--no-ff" &&
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
new file mode 100644 (file)
index 0000000..6b0483f
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Charles Bailey
+#
+
+test_description='git-mergetool
+
+Testing basic merge tool invocation'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+    echo master >file1 &&
+    git add file1 &&
+    git commit -m "added file1" &&
+    git checkout -b branch1 master &&
+    echo branch1 change >file1 &&
+    echo branch1 newfile >file2 &&
+    git add file1 file2 &&
+    git commit -m "branch1 changes" &&
+    git checkout -b branch2 master &&
+    echo branch2 change >file1 &&
+    echo branch2 newfile >file2 &&
+    git add file1 file2 &&
+    git commit -m "branch2 changes" &&
+    git checkout master &&
+    echo master updated >file1 &&
+    echo master new >file2 &&
+    git add file1 file2 &&
+    git commit -m "master updates"
+'
+
+test_expect_success 'custom mergetool' '
+    git config merge.tool mytool &&
+    git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+    git config mergetool.mytool.trustExitCode true &&
+       git checkout branch1 &&
+    ! git merge master >/dev/null 2>&1 &&
+    ( yes "" | git mergetool file1>/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool file2>/dev/null 2>&1 ) &&
+    test "$(cat file1)" = "master updated" &&
+    test "$(cat file2)" = "master new" &&
+       git commit -m "branch1 resolved with mergetool"
+'
+
+test_done
index cceedbb2b7efc20b79155889ed20c5a7866fed9c..c4f4465dc687d30138a9111ae2efbc0e3aaf42b6 100755 (executable)
@@ -869,6 +869,8 @@ zcommits
 COMMIT
 reset refs/tags/O3-2nd
 from :5
+reset refs/tags/O3-3rd
+from :5
 INPUT_END
 
 cat >expect <<INPUT_END
index 68efda44928fdd119a6e052557a9833cf3d2c13a..6aea0ea0a579973c9baddff608ea92445ef9fcbb 100644 (file)
@@ -3,12 +3,16 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
+# Keep the original TERM for say_color
+ORIGINAL_TERM=$TERM
+
 # For repeatability, reset the environment to known value.
 LANG=C
 LC_ALL=C
 PAGER=cat
 TZ=UTC
-export LANG LC_ALL PAGER TZ
+TERM=dumb
+export LANG LC_ALL PAGER TERM TZ
 EDITOR=:
 VISUAL=:
 unset GIT_EDITOR
@@ -58,12 +62,14 @@ esac
 # This test checks if command xyzzy does the right thing...
 # '
 # . ./test-lib.sh
-
-[ "x$TERM" != "xdumb" ] &&
-       [ -t 1 ] &&
-       tput bold >/dev/null 2>&1 &&
-       tput setaf 1 >/dev/null 2>&1 &&
-       tput sgr0 >/dev/null 2>&1 &&
+[ "x$ORIGINAL_TERM" != "xdumb" ] && (
+               TERM=$ORIGINAL_TERM &&
+               export TERM &&
+               [ -t 1 ] &&
+               tput bold >/dev/null 2>&1 &&
+               tput setaf 1 >/dev/null 2>&1 &&
+               tput sgr0 >/dev/null 2>&1
+       ) &&
        color=t
 
 while test "$#" -ne 0
@@ -80,7 +86,7 @@ do
        -q|--q|--qu|--qui|--quie|--quiet)
                quiet=t; shift ;;
        --no-color)
-           color=; shift ;;
+               color=; shift ;;
        --no-python)
                # noop now...
                shift ;;
@@ -91,6 +97,9 @@ done
 
 if test -n "$color"; then
        say_color () {
+               (
+               TERM=$ORIGINAL_TERM
+               export TERM
                case "$1" in
                        error) tput bold; tput setaf 1;; # bold red
                        skip)  tput bold; tput setaf 2;; # bold green
@@ -101,6 +110,7 @@ if test -n "$color"; then
                shift
                echo "* $*"
                tput sgr0
+               )
        }
 else
        say_color() {
@@ -142,7 +152,12 @@ test_count=0
 test_fixed=0
 test_broken=0
 
-trap 'echo >&5 "FATAL: Unexpected exit with code $?"; exit 1' exit
+die () {
+       echo >&5 "FATAL: Unexpected exit with code $?"
+       exit 1
+}
+
+trap 'die' exit
 
 test_tick () {
        if test -z "${test_tick+set}"
diff --git a/tag.c b/tag.c
index 990134fe7ab14043d40b97230571ffea109b9129..4470d2bf78e1fbb00d00e487f41daa4373cf48e1 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -87,12 +87,6 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
                item->tagged = NULL;
        }
 
-       if (item->tagged && track_object_refs) {
-               struct object_refs *refs = alloc_object_refs(1);
-               refs->ref[0] = item->tagged;
-               set_object_refs(&item->object, refs);
-       }
-
        return 0;
 }
 
index 0a5cf0a9c29c34a7f0fd0d53a5f6472ab2f2fa4f..393e0e8fe233ca51dc4c5db94c55e81ee53baa52 100644 (file)
@@ -442,7 +442,8 @@ static struct ref *get_refs_via_curl(struct transport *transport)
        struct ref *last_ref = NULL;
 
        if (!transport->data)
-               transport->data = get_http_walker(transport->url);
+               transport->data = get_http_walker(transport->url,
+                                               transport->remote);
 
        refs_url = xmalloc(strlen(transport->url) + 11);
        sprintf(refs_url, "%s/info/refs", transport->url);
@@ -453,9 +454,6 @@ static struct ref *get_refs_via_curl(struct transport *transport)
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
-       if (transport->remote->http_proxy)
-               curl_easy_setopt(slot->curl, CURLOPT_PROXY,
-                                transport->remote->http_proxy);
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
@@ -509,7 +507,8 @@ static int fetch_objs_via_curl(struct transport *transport,
                                 int nr_objs, struct ref **to_fetch)
 {
        if (!transport->data)
-               transport->data = get_http_walker(transport->url);
+               transport->data = get_http_walker(transport->url,
+                                               transport->remote);
        return fetch_objs_via_walker(transport, nr_objs, to_fetch);
 }
 
@@ -561,6 +560,7 @@ static int close_bundle(struct transport *transport)
 struct git_transport_data {
        unsigned thin : 1;
        unsigned keep : 1;
+       unsigned followtags : 1;
        int depth;
        struct child_process *conn;
        int fd[2];
@@ -581,6 +581,9 @@ static int set_git_option(struct transport *connection,
        } else if (!strcmp(name, TRANS_OPT_THIN)) {
                data->thin = !!value;
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
+               data->followtags = !!value;
+               return 0;
        } else if (!strcmp(name, TRANS_OPT_KEEP)) {
                data->keep = !!value;
                return 0;
@@ -629,6 +632,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.keep_pack = data->keep;
        args.lock_pack = 1;
        args.use_thin_pack = data->thin;
+       args.include_tag = data->followtags;
        args.verbose = transport->verbose > 0;
        args.depth = data->depth;
 
index 6fb4526cda5fc1ee1178fdf1d0840c90b88aa26c..8abfc0ae60c18165b44ef93ed9115972b991e2dc 100644 (file)
@@ -53,6 +53,9 @@ struct transport *transport_get(struct remote *, const char *);
 /* Limit the depth of the fetch if not null */
 #define TRANS_OPT_DEPTH "depth"
 
+/* Aggressively fetch annotated tags if possible */
+#define TRANS_OPT_FOLLOWTAGS "followtags"
+
 /**
  * Returns 0 if the option was used, non-zero otherwise. Prints a
  * message to stderr if the option is not used.
diff --git a/tree.c b/tree.c
index 87708ef420aaa328c9a8dfcefbcf4aa7ba6d4b72..4b1825c2adac94ad806d729862c90cf8dfa37e9a 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -202,52 +202,6 @@ struct tree *lookup_tree(const unsigned char *sha1)
        return (struct tree *) obj;
 }
 
-/*
- * NOTE! Tree refs to external git repositories
- * (ie gitlinks) do not count as real references.
- *
- * You don't have to have those repositories
- * available at all, much less have the objects
- * accessible from the current repository.
- */
-static void track_tree_refs(struct tree *item)
-{
-       int n_refs = 0, i;
-       struct object_refs *refs;
-       struct tree_desc desc;
-       struct name_entry entry;
-
-       /* Count how many entries there are.. */
-       init_tree_desc(&desc, item->buffer, item->size);
-       while (tree_entry(&desc, &entry)) {
-               if (S_ISGITLINK(entry.mode))
-                       continue;
-               n_refs++;
-       }
-
-       /* Allocate object refs and walk it again.. */
-       i = 0;
-       refs = alloc_object_refs(n_refs);
-       init_tree_desc(&desc, item->buffer, item->size);
-       while (tree_entry(&desc, &entry)) {
-               struct object *obj;
-
-               if (S_ISGITLINK(entry.mode))
-                       continue;
-               if (S_ISDIR(entry.mode))
-                       obj = &lookup_tree(entry.sha1)->object;
-               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
-                       obj = &lookup_blob(entry.sha1)->object;
-               else {
-                       warning("in tree %s: entry %s has bad mode %.6o\n",
-                            sha1_to_hex(item->object.sha1), entry.path, entry.mode);
-                       obj = lookup_unknown_object(entry.sha1);
-               }
-               refs->ref[i++] = obj;
-       }
-       set_object_refs(&item->object, refs);
-}
-
 int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
 {
        if (item->object.parsed)
@@ -256,8 +210,6 @@ int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
        item->buffer = buffer;
        item->size = size;
 
-       if (track_object_refs)
-               track_tree_refs(item);
        return 0;
 }
 
index b26d05331d7eeab4efea01594ecf196bc5fb50ed..b46dd365ea289c6d397dc6fc994b6ba4227886fc 100644 (file)
@@ -27,7 +27,8 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
 static unsigned long oldest_have;
 
 static int multi_ack, nr_our_refs;
-static int use_thin_pack, use_ofs_delta, no_progress;
+static int use_thin_pack, use_ofs_delta, use_include_tag;
+static int no_progress;
 static struct object_array have_obj;
 static struct object_array want_obj;
 static unsigned int timeout;
@@ -35,6 +36,7 @@ static unsigned int timeout;
  * otherwise maximum packet size (up to 65520 bytes).
  */
 static int use_sideband;
+static int debug_fd;
 
 static void reset_timeout(void)
 {
@@ -161,6 +163,8 @@ static void create_pack_file(void)
                argv[arg++] = "--progress";
        if (use_ofs_delta)
                argv[arg++] = "--delta-base-offset";
+       if (use_include_tag)
+               argv[arg++] = "--include-tag";
        argv[arg++] = NULL;
 
        memset(&pack_objects, 0, sizeof(pack_objects));
@@ -393,7 +397,6 @@ static int get_common_commits(void)
        char hex[41], last_hex[41];
        int len;
 
-       track_object_refs = 0;
        save_commit_buffer = 0;
 
        for(;;) {
@@ -445,6 +448,8 @@ static void receive_needs(void)
        static char line[1000];
        int len, depth = 0;
 
+       if (debug_fd)
+               write_in_full(debug_fd, "#S\n", 3);
        for (;;) {
                struct object *o;
                unsigned char sha1_buf[20];
@@ -452,6 +457,8 @@ static void receive_needs(void)
                reset_timeout();
                if (!len)
                        break;
+               if (debug_fd)
+                       write_in_full(debug_fd, line, len);
 
                if (!prefixcmp(line, "shallow ")) {
                        unsigned char sha1[20];
@@ -490,6 +497,8 @@ static void receive_needs(void)
                        use_sideband = DEFAULT_PACKET_MAX;
                if (strstr(line+45, "no-progress"))
                        no_progress = 1;
+               if (strstr(line+45, "include-tag"))
+                       use_include_tag = 1;
 
                /* We have sent all our refs already, and the other end
                 * should have chosen out of them; otherwise they are
@@ -507,6 +516,8 @@ static void receive_needs(void)
                        add_object_array(o, NULL, &want_obj);
                }
        }
+       if (debug_fd)
+               write_in_full(debug_fd, "#E\n", 3);
        if (depth == 0 && shallows.nr == 0)
                return;
        if (depth > 0) {
@@ -559,7 +570,8 @@ static void receive_needs(void)
 static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        static const char *capabilities = "multi_ack thin-pack side-band"
-               " side-band-64k ofs-delta shallow no-progress";
+               " side-band-64k ofs-delta shallow no-progress"
+               " include-tag";
        struct object *o = parse_object(sha1);
 
        if (!o)
@@ -632,6 +644,8 @@ int main(int argc, char **argv)
                die("'%s': unable to chdir or not a git archive", dir);
        if (is_repository_shallow())
                die("attempt to fetch/clone from a shallow repository");
+       if (getenv("GIT_DEBUG_SEND_PACK"))
+               debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
        upload_pack();
        return 0;
 }
index adc3e80ce14ab60b585e295d029261f5b53acb51..c10eca88261eed6b0e5c3fc2fdc19b8332c4ac6b 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -256,7 +256,6 @@ int walker_fetch(struct walker *walker, int targets, char **target,
        int i;
 
        save_commit_buffer = 0;
-       track_object_refs = 0;
 
        for (i = 0; i < targets; i++) {
                if (!write_ref || !write_ref[i])
index ea2c363f4ee4fe548dc405afe547d6dd71d76714..e1d40deaffa5965b1edd381a610ab225e027e3b2 100644 (file)
--- a/walker.h
+++ b/walker.h
@@ -1,6 +1,8 @@
 #ifndef WALKER_H
 #define WALKER_H
 
+#include "remote.h"
+
 struct walker {
        void *data;
        int (*fetch_ref)(struct walker *, char *ref, unsigned char *sha1);
@@ -32,6 +34,6 @@ int walker_fetch(struct walker *impl, int targets, char **target,
 
 void walker_free(struct walker *walker);
 
-struct walker *get_http_walker(const char *url);
+struct walker *get_http_walker(const char *url, struct remote *remote);
 
 #endif /* WALKER_H */
index 32d780af1e4f6e701f75d9759dfd4ebec56e6425..701d13da7c7b2d3de7ca67fbd663d6e9da813151 100644 (file)
@@ -7,6 +7,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "diffcore.h"
+#include "quote.h"
 
 int wt_status_relative_paths = 1;
 int wt_status_use_color = -1;
@@ -82,51 +83,7 @@ static void wt_status_print_trailer(struct wt_status *s)
        color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
 }
 
-static char *quote_path(const char *in, int len,
-                       struct strbuf *out, const char *prefix)
-{
-       if (len < 0)
-               len = strlen(in);
-
-       strbuf_grow(out, len);
-       strbuf_setlen(out, 0);
-       if (prefix) {
-               int off = 0;
-               while (prefix[off] && off < len && prefix[off] == in[off])
-                       if (prefix[off] == '/') {
-                               prefix += off + 1;
-                               in += off + 1;
-                               len -= off + 1;
-                               off = 0;
-                       } else
-                               off++;
-
-               for (; *prefix; prefix++)
-                       if (*prefix == '/')
-                               strbuf_addstr(out, "../");
-       }
-
-       for ( ; len > 0; in++, len--) {
-               int ch = *in;
-
-               switch (ch) {
-               case '\n':
-                       strbuf_addstr(out, "\\n");
-                       break;
-               case '\r':
-                       strbuf_addstr(out, "\\r");
-                       break;
-               default:
-                       strbuf_addch(out, ch);
-                       continue;
-               }
-       }
-
-       if (!out->len)
-               strbuf_addstr(out, "./");
-
-       return out->buf;
-}
+#define quote_path quote_path_relative
 
 static void wt_status_print_filepair(struct wt_status *s,
                                     int t, struct diff_filepair *p)