Merge branch 'py/submodule'
authorJunio C Hamano <gitster@pobox.com>
Sat, 15 Mar 2008 08:10:44 +0000 (01:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 15 Mar 2008 08:10:44 +0000 (01:10 -0700)
* py/submodule:
git-submodule summary: fix that some "wc" flavors produce leading spaces
git-submodule summary: test
git-submodule summary: documentation
git-submodule summary: limit summary size
git-submodule summary: show commit summary
git-submodule summary: code framework

158 files changed:
Documentation/RelNotes-1.5.4.4.txt
Documentation/RelNotes-1.5.5.txt
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-fetch-pack.txt
Documentation/git-gc.txt
Documentation/git-help.txt
Documentation/git-merge.txt
Documentation/git-mergetool.txt
Documentation/git-pack-objects.txt
Documentation/git-pull.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-rev-parse.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-unpack-objects.txt
Documentation/git-web--browse.txt
Documentation/git-whatchanged.txt
Documentation/git.txt
Documentation/manpage-1.72.xsl
Documentation/pretty-options.txt
Documentation/technical/api-run-command.txt
Makefile
builtin-add.c
builtin-checkout.c
builtin-clean.c
builtin-commit.c
builtin-describe.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-gc.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-merge-file.c
builtin-merge-recursive.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-read-tree.c
builtin-reflog.c
builtin-remote.c [new file with mode: 0644]
builtin-reset.c
builtin-rev-parse.c
builtin-shortlog.c
builtin-tag.c
builtin-unpack-objects.c
builtin.h
cache.h
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/examples/git-remote.perl [new file with mode: 0755]
contrib/fast-import/git-p4
contrib/hooks/post-receive-email
contrib/workdir/git-new-workdir
diff-lib.c
diff.c
diff.h
fast-import.c
fetch-pack.h
fsck.c
git-am.sh
git-compat-util.h
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/browser.tcl
git-gui/lib/choose_font.tcl
git-gui/lib/console.tcl
git-gui/lib/error.tcl
git-gui/lib/option.tcl
git-gui/po/zh_cn.po
git-merge.sh
git-mergetool.sh
git-quiltimport.sh
git-rebase.sh
git-remote.perl [deleted file]
git-send-email.perl
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
gitk-git/Makefile
gitk-git/gitk
gitk-git/po/it.po [new file with mode: 0644]
gitweb/gitweb.perl
hash.c
hash.h
help.c
http.c
ident.c
ll-merge.c [new file with mode: 0644]
ll-merge.h [new file with mode: 0644]
log-tree.c
log-tree.h
merge-tree.c
parse-options.c
parse-options.h
path-list.c
path-list.h
path.c
pretty.c
quote.c
quote.h
read-cache.c
receive-pack.c
refs.c
refs.h
remote.c
remote.h
run-command.c
setup.c
sha1_name.c
t/t0021-conversion.sh
t/t0040-parse-options.sh
t/t1005-read-tree-reset.sh [new file with mode: 0755]
t/t1410-reflog.sh
t/t3101-ls-tree-dirname.sh
t/t3407-rebase-abort.sh
t/t3903-stash.sh
t/t4021-format-patch-signer-mime.sh
t/t4028-format-patch-mime-headers.sh [new file with mode: 0755]
t/t4150-am-subdir.sh [new file with mode: 0755]
t/t4201-shortlog.sh
t/t5304-prune.sh
t/t5305-include-tag.sh [new file with mode: 0755]
t/t5503-tagfollow.sh [new file with mode: 0755]
t/t5505-remote.sh
t/t6031-merge-recursive.sh [new file with mode: 0755]
t/t6120-describe.sh
t/t7003-filter-branch.sh
t/t7005-editor.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
test-parse-options.c
transport.c
transport.h
tree-walk.c
tree-walk.h
unpack-trees.c
unpack-trees.h
upload-pack.c
wt-status.c
xdiff-interface.c
index 5635977c93b68b59f95c78058fecd8bd234b2693..89fa6d03bc038d6210e94d7fe9fbbffdbc84d883 100644 (file)
@@ -37,10 +37,30 @@ Fixes since v1.5.4.3
  * "git revert" did not properly fail when attempting to run with a
    dirty index.
 
-Also included are a handful documentation updates.
+ * "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).
+
+ * "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.
 
----
-exec >/var/tmp/1
-echo O=$(git describe maint)
-O=v1.5.4.3-32-g0f2d447
-git shortlog --no-merges $O..maint
+ * "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 b57fa1eb1a5bfc1d03e59c90222e0b6ed96fe261..874dad9a4f04b2ce11c9a7449c60b322aa041a37 100644 (file)
@@ -160,13 +160,9 @@ Fixes since v1.5.4
 All of the fixes in v1.5.4 maintenance series are included in
 this release, unless otherwise noted.
 
- * "git-daemon" did not send early errors to syslog.
-
  * "git-http-push" did not allow deletion of remote ref with the usual
    "push <remote> :<branch>" syntax.
 
- * "git-log --merge" did not well work with --left-right option.
-
  * "git-rebase --abort" did not go back to the right location if
    "git-reset" was run during the "git-rebase" session.
 
index 2091caa111a5a25ee983198437fd25aa52999b51..0865f4e01a7d37c102dced01120798b79af4f4b1 100644 (file)
@@ -420,6 +420,11 @@ branch.<name>.rebase::
        it unless you understand the implications (see linkgit:git-rebase[1]
        for details).
 
+browser.<tool>.cmd::
+       Specify the command to invoke the specified browser. The
+       specified command is evaluated in shell with the URLs passed
+       as arguments. (See linkgit:git-web--browse[1].)
+
 browser.<tool>.path::
        Override the path for the given tool that may be used to
        browse HTML help (see '-w' option in linkgit:git-help[1]) or a
@@ -556,6 +561,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
@@ -585,6 +595,10 @@ gc.packrefs::
        at some stage, and setting this to `false` will continue to
        prevent `git pack-refs` from being run from `git gc`.
 
+gc.pruneexpire::
+       When `git gc` is run, it will call `prune --expire 2.weeks.ago`.
+       Override the grace period with this config variable.
+
 gc.reflogexpire::
        `git reflog expire` removes reflog entries older than
        this time; defaults to 90 days.
@@ -743,14 +757,20 @@ log.showroot::
        Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
        normally hide the root commit will now show it. True by default.
 
+man.viewer::
+       Specify the programs that may be used to display help in the
+       'man' format. See linkgit:git-help[1].
+
 merge.summary::
        Whether to include summaries of merged commits in newly created
        merge commit messages. False by default.
 
 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 +797,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.
@@ -828,7 +873,7 @@ pack.indexVersion::
        whenever the corresponding pack is larger than 2 GB.  Otherwise
        the default is 1.
 
-pack.packSizeLimit:
+pack.packSizeLimit::
        The default maximum size of a pack.  This setting only affects
        packing to a file, i.e. the git:// protocol is unaffected.  It
        can be overridden by the `\--max-pack-size` option of
@@ -864,15 +909,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
@@ -939,12 +984,6 @@ imap::
        The configuration variables in the 'imap' section are described
        in linkgit:git-imap-send[1].
 
-receive.fsckObjects::
-       If it is set to true, git-receive-pack will check all received
-       objects. It will abort in the case of a malformed object or a
-       broken link. The result of an abort are only dangling objects.
-       The default value is true.
-
 receive.unpackLimit::
        If the number of objects received in a push is below this
        limit then the objects will be unpacked into loose object
index 47799097ce97da34fac60eafabd25576e077e362..c751a17d079dc2a6c5d3160573a42a7b6be3d5e2 100644 (file)
@@ -207,16 +207,14 @@ patch::
   and the working tree file and asks you if you want to stage
   the change of each hunk.  You can say:
 
-       y - add the change from that hunk to index
-       n - do not add the change from that hunk to index
-       a - add the change from that hunk and all the rest to index
-       d - do not the change from that hunk nor any of the rest to index
-       j - do not decide on this hunk now, and view the next
-           undecided hunk
-       J - do not decide on this hunk now, and view the next hunk
-       k - do not decide on this hunk now, and view the previous
-           undecided hunk
-       K - do not decide on this hunk now, and view the previous hunk
+       y - stage this hunk
+       n - do not stage this hunk
+       a - stage this and all the remaining hunks in the file
+       d - do not stage this hunk nor any of the remaining hunks in the file
+       j - leave this hunk undecided, see next undecided hunk
+       J - leave this hunk undecided, see next hunk
+       k - leave this hunk undecided, see previous undecided hunk
+       K - leave this hunk undecided, see previous hunk
        s - split the current hunk into smaller hunks
        ? - print help
 +
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 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 2e7be916aa5dede578b181e06457189551c16240..229a7c9b30482eb2d8729b581004bc776fa10edf 100644 (file)
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
 
 SYNOPSIS
 --------
-'git-gc' [--prune] [--aggressive] [--auto] [--quiet]
+'git-gc' [--aggressive] [--auto] [--quiet]
 
 DESCRIPTION
 -----------
@@ -25,17 +25,6 @@ operating performance. Some git commands may automatically run
 OPTIONS
 -------
 
---prune::
-       Usually `git-gc` packs refs, expires old reflog entries,
-       packs loose objects,
-       and removes old 'rerere' records.  Removal
-       of unreferenced loose objects is an unsafe operation
-       while other git operations are in progress, so it is not
-       done by default.  Pass this option if you want it, and only
-       when you know nobody else is creating new objects in the
-       repository at the same time (e.g. never use this option
-       in a cron script).
-
 --aggressive::
        Usually 'git-gc' runs very quickly while providing good disk
        space utilization and performance.  This option will cause
@@ -104,6 +93,10 @@ the value, the more time is spent optimizing the delta compression.  See
 the documentation for the --window' option in linkgit:git-repack[1] for
 more details.  This defaults to 10.
 
+The optional configuration variable 'gc.pruneExpire' controls how old
+the unreferenced loose objects have to be before they are pruned.  The
+default is "2 weeks ago".
+
 See Also
 --------
 linkgit:git-prune[1]
index 0926dc12bab23285256b8cd7906b79b7e5f5616f..be2ae53b90610d6c89fdf281634d06609562101b 100644 (file)
@@ -33,17 +33,21 @@ OPTIONS
        option supersedes any other option.
 
 -i|--info::
-       Use the 'info' program to display the manual page, instead of
-       the 'man' program that is used by default.
+       Display manual page for the command in the 'info' format. The
+       'info' program will be used for that purpose.
 
 -m|--man::
-       Use the 'man' program to display the manual page. This may be
-       used to override a value set in the 'help.format'
-       configuration variable.
+       Display manual page for the command in the 'man' format. This
+       option may be used to override a value set in the
+       'help.format' configuration variable.
++
+By default the 'man' program will be used to display the manual page,
+but the 'man.viewer' configuration variable may be used to choose
+other display programs (see below).
 
 -w|--web::
-       Use a web browser to display the HTML manual page, instead of
-       the 'man' program that is used by default.
+       Display manual page for the command in the 'web' (HTML)
+       format. A web browser will be used for that purpose.
 +
 The web browser can be specified using the configuration variable
 'help.browser', or 'web.browser' if the former is not set. If none of
@@ -54,6 +58,9 @@ linkgit:git-web--browse[1] for more information about this.
 CONFIGURATION VARIABLES
 -----------------------
 
+help.format
+~~~~~~~~~~~
+
 If no command line option is passed, the 'help.format' configuration
 variable will be checked. The following values are supported for this
 variable; they make 'git-help' behave as their corresponding command
@@ -61,15 +68,47 @@ line option:
 
 * "man" corresponds to '-m|--man',
 * "info" corresponds to '-i|--info',
-* "web" or "html" correspond to '-w|--web',
+* "web" or "html" correspond to '-w|--web'.
+
+help.browser, web.browser and browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
 be checked if the 'web' format is chosen (either by command line
 option or configuration variable). See '-w|--web' in the OPTIONS
 section above and linkgit:git-web--browse[1].
 
-Note that these configuration variables should probably be set using
-the '--global' flag, for example like this:
+man.viewer
+~~~~~~~~~~
+
+The 'man.viewer' config variable will be checked if the 'man' format
+is chosen. Only the following values are currently supported:
+
+* "man": use the 'man' program as usual,
+* "woman": use 'emacsclient' to launch the "woman" mode in emacs
+(this only works starting with emacsclient versions 22),
+* "konqueror": use a man KIO slave in konqueror.
+
+Multiple values may be given to this configuration variable. Their
+corresponding programs will be tried in the order listed in the
+configuration file.
+
+For example, this configuration:
+
+       [man]
+               viewer = konqueror
+               viewer = woman
+
+will try to use konqueror first. But this may fail (for example if
+DISPLAY is not set) and in that case emacs' woman mode will be tried.
+
+If everything fails the 'man' program will be tried anyway.
+
+Note about git config --global
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Note that all these configuration variables should probably be set
+using the '--global' flag, for example like this:
 
 ------------------------------------------------
 $ git config --global help.format web
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 737894390d8033bf470b3bfe3aeade858f38717e..3405ca09e85e9c81b9d48c753a2f117e7edca4eb 100644 (file)
@@ -21,6 +21,8 @@ Note that you can use `.` (current directory) as the
 <repository> to pull from the local repository -- this is useful
 when merging local branches into the current branch.
 
+Also note that options meant for `git-pull` itself and underlying
+`git-merge` must be given before the options meant for `git-fetch`.
 
 OPTIONS
 -------
index 4b10304740f5035095c19be05859ae0b9205e9a0..e0412e0866db8f80e8bbc8f33caaa659461d90aa 100644 (file)
@@ -262,8 +262,7 @@ hook if one exists.  You can use this hook to do sanity checks and
 reject the rebase if it isn't appropriate.  Please see the template
 pre-rebase hook script for an example.
 
-You must be in the top directory of your project to start (or continue)
-a rebase.  Upon completion, <branch> will be the current branch.
+Upon completion, <branch> will be the current branch.
 
 INTERACTIVE MODE
 ----------------
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 f02f6bbb49182c0dffce205b24a4199d824dc776..6513c2efe17668d5fc25a21385ed117ec76b40b5 100644 (file)
@@ -325,7 +325,7 @@ The lines after the separator describe the options.
 Each line of options has this format:
 
 ------------
-<opt_spec><arg_spec>? SP+ help LF
+<opt_spec><flags>* SP+ help LF
 ------------
 
 `<opt_spec>`::
@@ -334,10 +334,17 @@ Each line of options has this format:
        is necessary. `h,help`, `dry-run` and `f` are all three correct
        `<opt_spec>`.
 
-`<arg_spec>`::
-       an `<arg_spec>` tells the option parser if the option has an argument
-       (`=`), an optional one (`?` though its use is discouraged) or none
-       (no `<arg_spec>` in that case).
+`<flags>`::
+       `<flags>` are of `*`, `=`, `?` or `!`.
+       * Use `=` if the option takes an argument.
+
+       * Use `?` to mean that the option is optional (though its use is discouraged).
+
+       * Use `*` to mean that this option should not be listed in the usage
+         generated for the `-h` argument. It's shown for `--help-all` as
+         documented in linkgit:gitcli[5].
+
+       * Use `!` to not make the corresponding negated long option available.
 
 The remainder of the line, after stripping the spaces, is used
 as the help associated to the option.
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 e96bf365213c3321e9721dc2baa87ad278dfcb26..41f9f635665b82806350c87de5117e1cfa806c1a 100644 (file)
@@ -19,8 +19,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 3697896a06b460d9d29f962240dc46388487990a..b79be3fd4ced9dac416fb0fb7f97ab2d10775d01 100644 (file)
@@ -40,9 +40,6 @@ OPTIONS
        and make the best effort to recover as many objects as
        possible.
 
---strict::
-       Don't write objects with broken content or links.
-
 
 Author
 ------
index df57d010e5d999930ceaee1384a769bd1a8b2157..ddbae5b194790d7f76b53885e3d4ea642fda034a 100644 (file)
@@ -27,6 +27,8 @@ The following browsers (or commands) are currently supported:
 * dillo
 * open (this is the default under Mac OS X GUI)
 
+Custom commands may also be specified.
+
 OPTIONS
 -------
 -b BROWSER|--browser=BROWSER::
@@ -43,16 +45,35 @@ OPTIONS
 CONFIGURATION VARIABLES
 -----------------------
 
+CONF.VAR (from -c option) and web.browser
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 The web browser can be specified using a configuration variable passed
 with the -c (or --config) command line option, or the 'web.browser'
 configuration variable if the former is not used.
 
+browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~
+
 You can explicitly provide a full path to your preferred browser by
 setting the configuration variable 'browser.<tool>.path'. For example,
 you can configure the absolute path to firefox by setting
 'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool
 is available in PATH.
 
+browser.<tool>.cmd
+~~~~~~~~~~~~~~~~~~
+
+When the browser, specified by options or configuration variables, is
+not among the supported ones, then the corresponding
+'browser.<tool>.cmd' configuration variable will be looked up. If this
+variable exists then "git web--browse" will treat the specified tool
+as a custom command and will use a shell eval to run the command with
+the URLs passed as arguments.
+
+Note about git config --global
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 Note that these configuration variables should probably be set using
 the '--global' flag, for example like this:
 
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 fe3cd72d6f6bfde6e5071c68814e2e985cba8c6c..4065a3a27a38be73132b9f509e1d63546b3fddef 100644 (file)
@@ -1,5 +1,9 @@
-<!-- callout.xsl: converts asciidoc callouts to man page format -->
+<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL -->
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<xsl:param name="man.output.quietly" select="1"/>
+<xsl:param name="refentry.meta.get.quietly" select="1"/>
+
 <xsl:template match="co">
        <xsl:value-of select="concat('&#x2593;fB(',substring-after(@id,'-'),')&#x2593;fR')"/>
 </xsl:template>
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 fde3b453211030b7256ccedd100970ef712338bd..c364a22c8f98f6fbb71d4059f7458ed0fa78ed43 100644 (file)
@@ -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:
index ca5aad963ca7b5565338f49ce1705c451216c8be..e3eaa6a543e7c3eed8a72c00f8b5541524fc9508 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.
 #
@@ -243,7 +247,7 @@ SCRIPT_SH = \
 SCRIPT_PERL = \
        git-add--interactive.perl \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
-       git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
+       git-cvsserver.perl git-cvsexportcommit.perl \
        git-send-email.perl git-svn.perl
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
@@ -304,7 +308,7 @@ LIB_H = \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
        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 fsck.h \
+       mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h ll-merge.h fsck.h \
        pack-revindex.h
 
 DIFF_OBJS = \
@@ -329,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 fsck.o pack-revindex.o
+       ll-merge.o alias.o fsck.o pack-revindex.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
@@ -381,6 +385,7 @@ BUILTIN_OBJS = \
        builtin-push.o \
        builtin-read-tree.o \
        builtin-reflog.o \
+       builtin-remote.o \
        builtin-send-pack.o \
        builtin-config.o \
        builtin-rerere.o \
@@ -478,6 +483,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
@@ -629,6 +635,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
@@ -747,6 +757,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 6b08016228d4d50cfd6863a11f33b2c13084a6eb..7deb504837d252dd43fa9746c5d12342e93a5923 100644 (file)
@@ -152,6 +152,7 @@ static int reset_to_new(struct tree *tree, int quiet)
 {
        struct unpack_trees_options opts;
        struct tree_desc tree_desc;
+
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = -1;
        opts.update = 1;
@@ -159,6 +160,8 @@ static int reset_to_new(struct tree *tree, int quiet)
        opts.merge = 1;
        opts.fn = oneway_merge;
        opts.verbose_update = !quiet;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
        parse_tree(tree);
        init_tree_desc(&tree_desc, tree->buffer, tree->size);
        if (unpack_trees(1, &tree_desc, &opts))
@@ -170,6 +173,7 @@ static void reset_clean_to_new(struct tree *tree, int quiet)
 {
        struct unpack_trees_options opts;
        struct tree_desc tree_desc;
+
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = -1;
        opts.skip_unmerged = 1;
@@ -177,6 +181,8 @@ static void reset_clean_to_new(struct tree *tree, int quiet)
        opts.merge = 1;
        opts.fn = oneway_merge;
        opts.verbose_update = !quiet;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
        parse_tree(tree);
        init_tree_desc(&tree_desc, tree->buffer, tree->size);
        if (unpack_trees(1, &tree_desc, &opts))
@@ -224,8 +230,11 @@ static int merge_working_tree(struct checkout_opts *opts,
                struct tree_desc trees[2];
                struct tree *tree;
                struct unpack_trees_options topts;
+
                memset(&topts, 0, sizeof(topts));
                topts.head_idx = -1;
+               topts.src_index = &the_index;
+               topts.dst_index = &the_index;
 
                refresh_cache(REFRESH_QUIET);
 
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 f49c22e64255225e492614bb628c1d1776521424..660a3458f7f4ef24dfa4fd5bdf902174da1eefb4 100644 (file)
@@ -198,6 +198,8 @@ static void create_base_index(void)
        opts.head_idx = 1;
        opts.index_only = 1;
        opts.merge = 1;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
 
        opts.fn = oneway_merge;
        tree = parse_tree_indirect(head_sha1);
index 2f1e7ba150098dfa85c8ffe009b318c22bedcbbd..df554b30af3ddcb4d0141838c06f82d433cbf3a4 100644 (file)
@@ -21,6 +21,7 @@ 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;
@@ -156,7 +157,7 @@ static void display_name(struct commit_name *n)
 {
        if (n->prio == 2 && !n->tag) {
                n->tag = lookup_tag(n->sha1);
-               if (!n->tag || !n->tag->tag)
+               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);
@@ -166,9 +167,11 @@ static void display_name(struct commit_name *n)
                printf("%s", n->tag->tag);
        else
                printf("%s", n->path);
-       if (longformat)
-               printf("-0-g%s",
-                      find_unique_abbrev(n->tag->tagged->sha1, abbrev));
+}
+
+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)
@@ -195,7 +198,12 @@ static void describe(const char *arg, int last_one)
 
        n = cmit->util;
        if (n) {
+               /*
+                * Exact match to an existing ref.
+                */
                display_name(n);
+               if (longformat)
+                       show_suffix(0, n->tag->tagged->sha1);
                printf("\n");
                return;
        }
@@ -250,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);
 
@@ -281,8 +295,7 @@ static void describe(const char *arg, int last_one)
 
        display_name(all_matches[0].name);
        if (abbrev)
-               printf("-%d-g%s", all_matches[0].depth,
-                      find_unique_abbrev(cmit->object.sha1, abbrev));
+               show_suffix(all_matches[0].depth, cmit->object.sha1);
        printf("\n");
 
        if (!last_one)
@@ -305,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(),
        };
 
@@ -320,11 +335,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                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 b23e886d757664208f22bdfebbc21e4af103f69e..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;
@@ -680,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..b2b9935ed65cfa80daf9f9071a3dd5d381ef3426 100644 (file)
@@ -40,6 +40,8 @@ static struct option builtin_fetch_options[] = {
                    "force overwrite of local branch"),
        OPT_SET_INT('t', "tags", &tags,
                    "fetch all tags and associated objects", TAGS_SET),
+       OPT_SET_INT('n', NULL, &tags,
+                   "do not fetch all tags (--no-tags)", TAGS_UNSET),
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
@@ -101,6 +103,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 +163,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 +461,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 +508,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 +518,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 +557,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 045bf0e487a73e98d8d78990fcf85cc25ad5f96d..95917d74a873065bf9f928da37aa40e9e0d1f688 100644 (file)
@@ -26,12 +26,13 @@ static int pack_refs = 1;
 static int aggressive_window = -1;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 20;
+static char *prune_expire = "2.weeks.ago";
 
 #define MAX_ADD 10
 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
 static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
 static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
-static const char *argv_prune[] = {"prune", NULL};
+static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
 static const char *argv_rerere[] = {"rerere", "gc", NULL};
 
 static int gc_config(const char *var, const char *value)
@@ -55,6 +56,17 @@ static int gc_config(const char *var, const char *value)
                gc_auto_pack_limit = git_config_int(var, value);
                return 0;
        }
+       if (!strcmp(var, "gc.pruneexpire")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               if (strcmp(value, "now")) {
+                       unsigned long now = approxidate("now");
+                       if (approxidate(value) >= now)
+                               return error("Invalid %s: '%s'", var, value);
+               }
+               prune_expire = xstrdup(value);
+               return 0;
+       }
        return git_default_config(var, value);
 }
 
@@ -234,7 +246,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_repack[0]);
 
-       if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
+       argv_prune[2] = prune_expire;
+       if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_prune[0]);
 
        if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
index fe8fc6f22a176a017c65ac6177f0bab890d8fce5..5c00725f030460ba34c702b9ad31e577ca70d361 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);
@@ -657,6 +662,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        int i;
        const char *encoding = "utf-8";
        struct diff_options opts;
+       int need_8bit_cte = 0;
 
        if (rev->commit_format != CMIT_FMT_EMAIL)
                die("Cover letter needs email format");
@@ -667,7 +673,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
 
        head_sha1 = sha1_to_hex(head->object.sha1);
 
-       log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers);
+       log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
+                               &need_8bit_cte);
 
        committer = git_committer_info(0);
 
@@ -676,7 +683,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
                     encoding);
        pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
-                     encoding, 0);
+                     encoding, need_8bit_cte);
        pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
        printf("%s\n", sb.buf);
 
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 adce6d4635a4153428368073677cd74a9bafc045..3605960c2d9692514a6df0f344f3c3269cf1de3c 100644 (file)
@@ -57,7 +57,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 
                if (!f)
                        ret = error("Could not open %s for writing", filename);
-               else if (fwrite(result.ptr, result.size, 1, f) != 1)
+               else if (result.size &&
+                        fwrite(result.ptr, result.size, 1, f) != 1)
                        ret = error("Could not write to %s", filename);
                else if (fclose(f))
                        ret = error("Could not close %s", filename);
index 6fe4102c0cfb29031f7fdce68ca4d1937e2fefd7..910c0d20e7ba1128c705a49bfd9966212c5420b2 100644 (file)
 #include "tree-walk.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "run-command.h"
 #include "tag.h"
 #include "unpack-trees.h"
 #include "path-list.h"
 #include "xdiff-interface.h"
+#include "ll-merge.h"
 #include "interpolate.h"
 #include "attr.h"
 #include "merge-recursive.h"
@@ -213,6 +213,8 @@ static int git_merge_trees(int index_only,
        opts.merge = 1;
        opts.head_idx = 2;
        opts.fn = threeway_merge;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
 
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@ -615,364 +617,16 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
        mm->size = size;
 }
 
-/*
- * Customizable low-level merge drivers support.
- */
-
-struct ll_merge_driver;
-typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
-                          const char *path,
-                          mmfile_t *orig,
-                          mmfile_t *src1, const char *name1,
-                          mmfile_t *src2, const char *name2,
-                          mmbuffer_t *result);
-
-struct ll_merge_driver {
-       const char *name;
-       const char *description;
-       ll_merge_fn fn;
-       const char *recursive;
-       struct ll_merge_driver *next;
-       char *cmdline;
-};
-
-/*
- * Built-in low-levels
- */
-static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
-                          const char *path_unused,
-                          mmfile_t *orig,
-                          mmfile_t *src1, const char *name1,
-                          mmfile_t *src2, const char *name2,
-                          mmbuffer_t *result)
-{
-       /*
-        * The tentative merge result is "ours" for the final round,
-        * or common ancestor for an internal merge.  Still return
-        * "conflicted merge" status.
-        */
-       mmfile_t *stolen = index_only ? orig : src1;
-
-       result->ptr = stolen->ptr;
-       result->size = stolen->size;
-       stolen->ptr = NULL;
-       return 1;
-}
-
-static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
-                       const char *path_unused,
-                       mmfile_t *orig,
-                       mmfile_t *src1, const char *name1,
-                       mmfile_t *src2, const char *name2,
-                       mmbuffer_t *result)
-{
-       xpparam_t xpp;
-
-       if (buffer_is_binary(orig->ptr, orig->size) ||
-           buffer_is_binary(src1->ptr, src1->size) ||
-           buffer_is_binary(src2->ptr, src2->size)) {
-               warning("Cannot merge binary files: %s vs. %s\n",
-                       name1, name2);
-               return ll_binary_merge(drv_unused, path_unused,
-                                      orig, src1, name1,
-                                      src2, name2,
-                                      result);
-       }
-
-       memset(&xpp, 0, sizeof(xpp));
-       return xdl_merge(orig,
-                        src1, name1,
-                        src2, name2,
-                        &xpp, XDL_MERGE_ZEALOUS,
-                        result);
-}
-
-static int ll_union_merge(const struct ll_merge_driver *drv_unused,
-                         const char *path_unused,
-                         mmfile_t *orig,
-                         mmfile_t *src1, const char *name1,
-                         mmfile_t *src2, const char *name2,
-                         mmbuffer_t *result)
-{
-       char *src, *dst;
-       long size;
-       const int marker_size = 7;
-
-       int status = ll_xdl_merge(drv_unused, path_unused,
-                                 orig, src1, NULL, src2, NULL, result);
-       if (status <= 0)
-               return status;
-       size = result->size;
-       src = dst = result->ptr;
-       while (size) {
-               char ch;
-               if ((marker_size < size) &&
-                   (*src == '<' || *src == '=' || *src == '>')) {
-                       int i;
-                       ch = *src;
-                       for (i = 0; i < marker_size; i++)
-                               if (src[i] != ch)
-                                       goto not_a_marker;
-                       if (src[marker_size] != '\n')
-                               goto not_a_marker;
-                       src += marker_size + 1;
-                       size -= marker_size + 1;
-                       continue;
-               }
-       not_a_marker:
-               do {
-                       ch = *src++;
-                       *dst++ = ch;
-                       size--;
-               } while (ch != '\n' && size);
-       }
-       result->size = dst - result->ptr;
-       return 0;
-}
-
-#define LL_BINARY_MERGE 0
-#define LL_TEXT_MERGE 1
-#define LL_UNION_MERGE 2
-static struct ll_merge_driver ll_merge_drv[] = {
-       { "binary", "built-in binary merge", ll_binary_merge },
-       { "text", "built-in 3-way text merge", ll_xdl_merge },
-       { "union", "built-in union merge", ll_union_merge },
-};
-
-static void create_temp(mmfile_t *src, char *path)
-{
-       int fd;
-
-       strcpy(path, ".merge_file_XXXXXX");
-       fd = xmkstemp(path);
-       if (write_in_full(fd, src->ptr, src->size) != src->size)
-               die("unable to write temp-file");
-       close(fd);
-}
-
-/*
- * User defined low-level merge driver support.
- */
-static int ll_ext_merge(const struct ll_merge_driver *fn,
-                       const char *path,
-                       mmfile_t *orig,
-                       mmfile_t *src1, const char *name1,
-                       mmfile_t *src2, const char *name2,
-                       mmbuffer_t *result)
-{
-       char temp[3][50];
-       char cmdbuf[2048];
-       struct interp table[] = {
-               { "%O" },
-               { "%A" },
-               { "%B" },
-       };
-       struct child_process child;
-       const char *args[20];
-       int status, fd, i;
-       struct stat st;
-
-       if (fn->cmdline == NULL)
-               die("custom merge driver %s lacks command line.", fn->name);
-
-       result->ptr = NULL;
-       result->size = 0;
-       create_temp(orig, temp[0]);
-       create_temp(src1, temp[1]);
-       create_temp(src2, temp[2]);
-
-       interp_set_entry(table, 0, temp[0]);
-       interp_set_entry(table, 1, temp[1]);
-       interp_set_entry(table, 2, temp[2]);
-
-       output(1, "merging %s using %s", path,
-              fn->description ? fn->description : fn->name);
-
-       interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
-
-       memset(&child, 0, sizeof(child));
-       child.argv = args;
-       args[0] = "sh";
-       args[1] = "-c";
-       args[2] = cmdbuf;
-       args[3] = NULL;
-
-       status = run_command(&child);
-       if (status < -ERR_RUN_COMMAND_FORK)
-               ; /* failure in run-command */
-       else
-               status = -status;
-       fd = open(temp[1], O_RDONLY);
-       if (fd < 0)
-               goto bad;
-       if (fstat(fd, &st))
-               goto close_bad;
-       result->size = st.st_size;
-       result->ptr = xmalloc(result->size + 1);
-       if (read_in_full(fd, result->ptr, result->size) != result->size) {
-               free(result->ptr);
-               result->ptr = NULL;
-               result->size = 0;
-       }
- close_bad:
-       close(fd);
- bad:
-       for (i = 0; i < 3; i++)
-               unlink(temp[i]);
-       return status;
-}
-
-/*
- * merge.default and merge.driver configuration items
- */
-static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
-static const char *default_ll_merge;
-
-static int read_merge_config(const char *var, const char *value)
-{
-       struct ll_merge_driver *fn;
-       const char *ep, *name;
-       int namelen;
-
-       if (!strcmp(var, "merge.default")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               default_ll_merge = strdup(value);
-               return 0;
-       }
-
-       /*
-        * We are not interested in anything but "merge.<name>.variable";
-        * especially, we do not want to look at variables such as
-        * "merge.summary", "merge.tool", and "merge.verbosity".
-        */
-       if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
-               return 0;
-
-       /*
-        * Find existing one as we might be processing merge.<name>.var2
-        * after seeing merge.<name>.var1.
-        */
-       name = var + 6;
-       namelen = ep - name;
-       for (fn = ll_user_merge; fn; fn = fn->next)
-               if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
-                       break;
-       if (!fn) {
-               fn = xcalloc(1, sizeof(struct ll_merge_driver));
-               fn->name = xmemdupz(name, namelen);
-               fn->fn = ll_ext_merge;
-               *ll_user_merge_tail = fn;
-               ll_user_merge_tail = &(fn->next);
-       }
-
-       ep++;
-
-       if (!strcmp("name", ep)) {
-               if (!value)
-                       return config_error_nonbool(var);
-               fn->description = strdup(value);
-               return 0;
-       }
-
-       if (!strcmp("driver", ep)) {
-               if (!value)
-                       return config_error_nonbool(var);
-               /*
-                * merge.<name>.driver specifies the command line:
-                *
-                *      command-line
-                *
-                * The command-line will be interpolated with the following
-                * tokens and is given to the shell:
-                *
-                *    %O - temporary file name for the merge base.
-                *    %A - temporary file name for our version.
-                *    %B - temporary file name for the other branches' version.
-                *
-                * The external merge driver should write the results in the
-                * file named by %A, and signal that it has done with zero exit
-                * status.
-                */
-               fn->cmdline = strdup(value);
-               return 0;
-       }
-
-       if (!strcmp("recursive", ep)) {
-               if (!value)
-                       return config_error_nonbool(var);
-               fn->recursive = strdup(value);
-               return 0;
-       }
-
-       return 0;
-}
-
-static void initialize_ll_merge(void)
-{
-       if (ll_user_merge_tail)
-               return;
-       ll_user_merge_tail = &ll_user_merge;
-       git_config(read_merge_config);
-}
-
-static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
-{
-       struct ll_merge_driver *fn;
-       const char *name;
-       int i;
-
-       initialize_ll_merge();
-
-       if (ATTR_TRUE(merge_attr))
-               return &ll_merge_drv[LL_TEXT_MERGE];
-       else if (ATTR_FALSE(merge_attr))
-               return &ll_merge_drv[LL_BINARY_MERGE];
-       else if (ATTR_UNSET(merge_attr)) {
-               if (!default_ll_merge)
-                       return &ll_merge_drv[LL_TEXT_MERGE];
-               else
-                       name = default_ll_merge;
-       }
-       else
-               name = merge_attr;
-
-       for (fn = ll_user_merge; fn; fn = fn->next)
-               if (!strcmp(fn->name, name))
-                       return fn;
-
-       for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
-               if (!strcmp(ll_merge_drv[i].name, name))
-                       return &ll_merge_drv[i];
-
-       /* default to the 3-way */
-       return &ll_merge_drv[LL_TEXT_MERGE];
-}
-
-static const char *git_path_check_merge(const char *path)
-{
-       static struct git_attr_check attr_merge_check;
-
-       if (!attr_merge_check.attr)
-               attr_merge_check.attr = git_attr("merge", 5);
-
-       if (git_checkattr(path, 1, &attr_merge_check))
-               return NULL;
-       return attr_merge_check.value;
-}
-
-static int ll_merge(mmbuffer_t *result_buf,
-                   struct diff_filespec *o,
-                   struct diff_filespec *a,
-                   struct diff_filespec *b,
-                   const char *branch1,
-                   const char *branch2)
+static int merge_3way(mmbuffer_t *result_buf,
+                     struct diff_filespec *o,
+                     struct diff_filespec *a,
+                     struct diff_filespec *b,
+                     const char *branch1,
+                     const char *branch2)
 {
        mmfile_t orig, src1, src2;
        char *name1, *name2;
        int merge_status;
-       const char *ll_driver_name;
-       const struct ll_merge_driver *driver;
 
        name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
        name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
@@ -981,14 +635,9 @@ static int ll_merge(mmbuffer_t *result_buf,
        fill_mm(a->sha1, &src1);
        fill_mm(b->sha1, &src2);
 
-       ll_driver_name = git_path_check_merge(a->path);
-       driver = find_ll_merge_driver(ll_driver_name);
-
-       if (index_only && driver->recursive)
-               driver = find_ll_merge_driver(driver->recursive);
-       merge_status = driver->fn(driver, a->path,
-                                 &orig, &src1, name1, &src2, name2,
-                                 result_buf);
+       merge_status = ll_merge(result_buf, a->path, &orig,
+                               &src1, name1, &src2, name2,
+                               index_only);
 
        free(name1);
        free(name2);
@@ -1019,9 +668,20 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
                if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
                        result.merge = 1;
 
-               result.mode = a->mode == o->mode ? b->mode: a->mode;
+               /*
+                * Merge modes
+                */
+               if (a->mode == b->mode || a->mode == o->mode)
+                       result.mode = b->mode;
+               else {
+                       result.mode = a->mode;
+                       if (b->mode != o->mode) {
+                               result.clean = 0;
+                               result.merge = 1;
+                       }
+               }
 
-               if (sha_eq(a->sha1, o->sha1))
+               if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, o->sha1))
                        hashcpy(result.sha, b->sha1);
                else if (sha_eq(b->sha1, o->sha1))
                        hashcpy(result.sha, a->sha1);
@@ -1029,8 +689,8 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
                        mmbuffer_t result_buf;
                        int merge_status;
 
-                       merge_status = ll_merge(&result_buf, o, a, b,
-                                               branch1, branch2);
+                       merge_status = merge_3way(&result_buf, o, a, b,
+                                                 branch1, branch2);
 
                        if ((merge_status < 0) || !result_buf.ptr)
                                die("Failed to execute internal merge");
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 2799e6833849eea9a4cb35213c1115c44bf702ed..777f272668c39931b330be555f41399adfbce397 100644 (file)
@@ -15,6 +15,7 @@
 #include "revision.h"
 #include "list-objects.h"
 #include "progress.h"
+#include "refs.h"
 
 #ifdef THREADED_DELTA_SEARCH
 #include "thread-utils.h"
@@ -27,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;
@@ -63,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;
@@ -452,6 +454,7 @@ static void write_pack_file(void)
        struct pack_header hdr;
        int do_progress = progress >> pack_to_stdout;
        uint32_t nr_remaining = nr_result;
+       time_t last_mtime = 0;
 
        if (do_progress)
                progress_state = start_progress("Writing objects", nr_result);
@@ -502,6 +505,7 @@ static void write_pack_file(void)
 
                if (!pack_to_stdout) {
                        mode_t mode = umask(0);
+                       struct stat st;
                        char *idx_tmp_name, tmpname[PATH_MAX];
 
                        umask(mode);
@@ -509,6 +513,7 @@ static void write_pack_file(void)
 
                        idx_tmp_name = write_idx_file(NULL, written_list,
                                                      nr_written, sha1);
+
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
                                 base_name, sha1_to_hex(sha1));
                        if (adjust_perm(pack_tmp_name, mode))
@@ -517,6 +522,28 @@ static void write_pack_file(void)
                        if (rename(pack_tmp_name, tmpname))
                                die("unable to rename temporary pack file: %s",
                                    strerror(errno));
+
+                       /*
+                        * Packs are runtime accessed in their mtime
+                        * order since newer packs are more likely to contain
+                        * younger objects.  So if we are creating multiple
+                        * packs then we should modify the mtime of later ones
+                        * to preserve this property.
+                        */
+                       if (stat(tmpname, &st) < 0) {
+                               warning("failed to stat %s: %s",
+                                       tmpname, strerror(errno));
+                       } else if (!last_mtime) {
+                               last_mtime = st.st_mtime;
+                       } else {
+                               struct utimbuf utb;
+                               utb.actime = st.st_atime;
+                               utb.modtime = --last_mtime;
+                               if (utime(tmpname, &utb) < 0)
+                                       warning("failed utime() on %s: %s",
+                                               tmpname, strerror(errno));
+                       }
+
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
                                 base_name, sha1_to_hex(sha1));
                        if (adjust_perm(idx_tmp_name, mode))
@@ -525,6 +552,7 @@ static void write_pack_file(void)
                        if (rename(idx_tmp_name, tmpname))
                                die("unable to rename temporary index file: %s",
                                    strerror(errno));
+
                        free(idx_tmp_name);
                        free(pack_tmp_name);
                        puts(sha1_to_hex(sha1));
@@ -1630,6 +1658,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;
@@ -2033,6 +2073,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) ||
@@ -2109,6 +2153,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 0138f5a9172034b2ce34222ff077a975f8998005..e9cfd2bbc5539ee0c9c048798383b837ff63991b 100644 (file)
 #include "dir.h"
 #include "builtin.h"
 
-#define MAX_TREES 8
 static int nr_trees;
-static struct tree *trees[MAX_TREES];
+static struct tree *trees[MAX_UNPACK_TREES];
 
 static int list_tree(unsigned char *sha1)
 {
        struct tree *tree;
 
-       if (nr_trees >= MAX_TREES)
-               die("I cannot read more than %d trees", MAX_TREES);
+       if (nr_trees >= MAX_UNPACK_TREES)
+               die("I cannot read more than %d trees", MAX_UNPACK_TREES);
        tree = parse_tree_indirect(sha1);
        if (!tree)
                return -1;
@@ -97,11 +96,13 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 {
        int i, newfd, stage = 0;
        unsigned char sha1[20];
-       struct tree_desc t[MAX_TREES];
+       struct tree_desc t[MAX_UNPACK_TREES];
        struct unpack_trees_options opts;
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = -1;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
 
        git_config(git_default_config);
 
@@ -220,27 +221,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        if ((opts.dir && !opts.update))
                die("--exclude-per-directory is meaningless unless -u");
 
-       if (opts.prefix) {
-               int pfxlen = strlen(opts.prefix);
-               int pos;
-               if (opts.prefix[pfxlen-1] != '/')
-                       die("prefix must end with /");
-               if (stage != 2)
-                       die("binding merge takes only one tree");
-               pos = cache_name_pos(opts.prefix, pfxlen);
-               if (0 <= pos)
-                       die("corrupt index file");
-               pos = -pos-1;
-               if (pos < active_nr &&
-                   !strncmp(active_cache[pos]->name, opts.prefix, pfxlen))
-                       die("subdirectory '%s' already exists.", opts.prefix);
-               pos = cache_name_pos(opts.prefix, pfxlen-1);
-               if (0 <= pos)
-                       die("file '%.*s' already exists.",
-                                       pfxlen-1, opts.prefix);
-               opts.pos = -1 - pos;
-       }
-
        if (opts.merge) {
                if (stage < 2)
                        die("just how do you expect me to merge %d trees?", stage-1);
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);
 }
diff --git a/builtin-remote.c b/builtin-remote.c
new file mode 100644 (file)
index 0000000..24e6929
--- /dev/null
@@ -0,0 +1,605 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "transport.h"
+#include "remote.h"
+#include "path-list.h"
+#include "strbuf.h"
+#include "run-command.h"
+#include "refs.h"
+
+static const char * const builtin_remote_usage[] = {
+       "git remote",
+       "git remote add <name> <url>",
+       "git remote rm <name>",
+       "git remote show <name>",
+       "git remote prune <name>",
+       "git remote update [group]",
+       NULL
+};
+
+static int verbose;
+
+static inline int postfixcmp(const char *string, const char *postfix)
+{
+       int len1 = strlen(string), len2 = strlen(postfix);
+       if (len1 < len2)
+               return 1;
+       return strcmp(string + len1 - len2, postfix);
+}
+
+static inline const char *skip_prefix(const char *name, const char *prefix)
+{
+       return !name ? "" :
+               prefixcmp(name, prefix) ?  name : name + strlen(prefix);
+}
+
+static int opt_parse_track(const struct option *opt, const char *arg, int not)
+{
+       struct path_list *list = opt->value;
+       if (not)
+               path_list_clear(list, 0);
+       else
+               path_list_append(arg, list);
+       return 0;
+}
+
+static int fetch_remote(const char *name)
+{
+       const char *argv[] = { "fetch", name, NULL };
+       printf("Updating %s\n", name);
+       if (run_command_v_opt(argv, RUN_GIT_CMD))
+               return error("Could not fetch %s", name);
+       return 0;
+}
+
+static int add(int argc, const char **argv)
+{
+       int fetch = 0, mirror = 0;
+       struct path_list track = { NULL, 0, 0 };
+       const char *master = NULL;
+       struct remote *remote;
+       struct strbuf buf, buf2;
+       const char *name, *url;
+       int i;
+
+       struct option options[] = {
+               OPT_GROUP("add specific options"),
+               OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+               OPT_CALLBACK('t', "track", &track, "branch",
+                       "branch(es) to track", opt_parse_track),
+               OPT_STRING('m', "master", &master, "branch", "master branch"),
+               OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+
+       if (argc < 2)
+               usage_with_options(builtin_remote_usage, options);
+
+       name = argv[0];
+       url = argv[1];
+
+       remote = remote_get(name);
+       if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
+                       remote->fetch_refspec_nr))
+               die("remote %s already exists.", name);
+
+       strbuf_init(&buf, 0);
+       strbuf_init(&buf2, 0);
+
+       strbuf_addf(&buf, "remote.%s.url", name);
+       if (git_config_set(buf.buf, url))
+               return 1;
+
+       if (track.nr == 0)
+               path_list_append("*", &track);
+       for (i = 0; i < track.nr; i++) {
+               struct path_list_item *item = track.items + i;
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "remote.%s.fetch", name);
+
+               strbuf_reset(&buf2);
+               if (mirror)
+                       strbuf_addf(&buf2, "refs/%s:refs/%s",
+                                       item->path, item->path);
+               else
+                       strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
+                                       item->path, name, item->path);
+               if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+                       return 1;
+       }
+
+       if (fetch && fetch_remote(name))
+               return 1;
+
+       if (master) {
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
+
+               strbuf_reset(&buf2);
+               strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
+
+               if (create_symref(buf.buf, buf2.buf, "remote add"))
+                       return error("Could not setup master '%s'", master);
+       }
+
+       strbuf_release(&buf);
+       strbuf_release(&buf2);
+       path_list_clear(&track, 0);
+
+       return 0;
+}
+
+struct branch_info {
+       char *remote;
+       struct path_list merge;
+};
+
+static struct path_list branch_list;
+
+static int config_read_branches(const char *key, const char *value)
+{
+       if (!prefixcmp(key, "branch.")) {
+               char *name;
+               struct path_list_item *item;
+               struct branch_info *info;
+               enum { REMOTE, MERGE } type;
+
+               key += 7;
+               if (!postfixcmp(key, ".remote")) {
+                       name = xstrndup(key, strlen(key) - 7);
+                       type = REMOTE;
+               } else if (!postfixcmp(key, ".merge")) {
+                       name = xstrndup(key, strlen(key) - 6);
+                       type = MERGE;
+               } else
+                       return 0;
+
+               item = path_list_insert(name, &branch_list);
+
+               if (!item->util)
+                       item->util = xcalloc(sizeof(struct branch_info), 1);
+               info = item->util;
+               if (type == REMOTE) {
+                       if (info->remote)
+                               warning("more than one branch.%s", key);
+                       info->remote = xstrdup(value);
+               } else {
+                       char *space = strchr(value, ' ');
+                       value = skip_prefix(value, "refs/heads/");
+                       while (space) {
+                               char *merge;
+                               merge = xstrndup(value, space - value);
+                               path_list_append(merge, &info->merge);
+                               value = skip_prefix(space + 1, "refs/heads/");
+                               space = strchr(value, ' ');
+                       }
+                       path_list_append(xstrdup(value), &info->merge);
+               }
+       }
+       return 0;
+}
+
+static void read_branches(void)
+{
+       if (branch_list.nr)
+               return;
+       git_config(config_read_branches);
+       sort_path_list(&branch_list);
+}
+
+struct ref_states {
+       struct remote *remote;
+       struct strbuf remote_prefix;
+       struct path_list new, stale, tracked;
+};
+
+static int handle_one_branch(const char *refname,
+       const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct ref_states *states = cb_data;
+       struct refspec refspec;
+
+       memset(&refspec, 0, sizeof(refspec));
+       refspec.dst = (char *)refname;
+       if (!remote_find_tracking(states->remote, &refspec)) {
+               struct path_list_item *item;
+               const char *name = skip_prefix(refspec.src, "refs/heads/");
+               if (unsorted_path_list_has_path(&states->tracked, name) ||
+                               unsorted_path_list_has_path(&states->new,
+                                       name))
+                       return 0;
+               item = path_list_append(name, &states->stale);
+               item->util = xstrdup(refname);
+       }
+       return 0;
+}
+
+static int get_ref_states(const struct ref *ref, struct ref_states *states)
+{
+       struct ref *fetch_map = NULL, **tail = &fetch_map;
+       int i;
+
+       for (i = 0; i < states->remote->fetch_refspec_nr; i++)
+               if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
+                       die("Could not get fetch map for refspec %s",
+                               states->remote->fetch_refspec[i]);
+
+       states->new.strdup_paths = states->tracked.strdup_paths = 1;
+       for (ref = fetch_map; ref; ref = ref->next) {
+               struct path_list *target = &states->tracked;
+               unsigned char sha1[20];
+               void *util = NULL;
+
+               if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
+                       target = &states->new;
+               else {
+                       target = &states->tracked;
+                       if (hashcmp(sha1, ref->new_sha1))
+                               util = &states;
+               }
+               path_list_append(skip_prefix(ref->name, "refs/heads/"),
+                               target)->util = util;
+       }
+       free_refs(fetch_map);
+
+       strbuf_addf(&states->remote_prefix,
+               "refs/remotes/%s/", states->remote->name);
+       for_each_ref(handle_one_branch, states);
+       sort_path_list(&states->stale);
+
+       return 0;
+}
+
+struct branches_for_remote {
+       const char *prefix;
+       struct path_list *branches;
+};
+
+static int add_branch_for_removal(const char *refname,
+       const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct branches_for_remote *branches = cb_data;
+
+       if (!prefixcmp(refname, branches->prefix)) {
+               struct path_list_item *item;
+
+               /* make sure that symrefs are deleted */
+               if (flags & REF_ISSYMREF)
+                       return unlink(git_path(refname));
+
+               item = path_list_append(refname, branches->branches);
+               item->util = xmalloc(20);
+               hashcpy(item->util, sha1);
+       }
+
+       return 0;
+}
+
+static int remove_branches(struct path_list *branches)
+{
+       int i, result = 0;
+       for (i = 0; i < branches->nr; i++) {
+               struct path_list_item *item = branches->items + i;
+               const char *refname = item->path;
+               unsigned char *sha1 = item->util;
+
+               if (delete_ref(refname, sha1))
+                       result |= error("Could not remove branch %s", refname);
+       }
+       return result;
+}
+
+static int rm(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       struct remote *remote;
+       struct strbuf buf;
+       struct path_list branches = { NULL, 0, 0, 1 };
+       struct branches_for_remote cb_data = { NULL, &branches };
+       int i;
+
+       if (argc != 2)
+               usage_with_options(builtin_remote_usage, options);
+
+       remote = remote_get(argv[1]);
+       if (!remote)
+               die("No such remote: %s", argv[1]);
+
+       strbuf_init(&buf, 0);
+       strbuf_addf(&buf, "remote.%s", remote->name);
+       if (git_config_rename_section(buf.buf, NULL) < 1)
+               return error("Could not remove config section '%s'", buf.buf);
+
+       read_branches();
+       for (i = 0; i < branch_list.nr; i++) {
+               struct path_list_item *item = branch_list.items + i;
+               struct branch_info *info = item->util;
+               if (info->remote && !strcmp(info->remote, remote->name)) {
+                       const char *keys[] = { "remote", "merge", NULL }, **k;
+                       for (k = keys; *k; k++) {
+                               strbuf_reset(&buf);
+                               strbuf_addf(&buf, "branch.%s.%s",
+                                               item->path, *k);
+                               if (git_config_set(buf.buf, NULL)) {
+                                       strbuf_release(&buf);
+                                       return -1;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * We cannot just pass a function to for_each_ref() which deletes
+        * the branches one by one, since for_each_ref() relies on cached
+        * refs, which are invalidated when deleting a branch.
+        */
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "refs/remotes/%s/", remote->name);
+       cb_data.prefix = buf.buf;
+       i = for_each_ref(add_branch_for_removal, &cb_data);
+       strbuf_release(&buf);
+
+       if (!i)
+               i = remove_branches(&branches);
+       path_list_clear(&branches, 1);
+
+       return i;
+}
+
+static void show_list(const char *title, struct path_list *list)
+{
+       int i;
+
+       if (!list->nr)
+               return;
+
+       printf(title, list->nr > 1 ? "es" : "");
+       printf("\n    ");
+       for (i = 0; i < list->nr; i++)
+               printf("%s%s", i ? " " : "", list->items[i].path);
+       printf("\n");
+}
+
+static int show_or_prune(int argc, const char **argv, int prune)
+{
+       int dry_run = 0, result = 0;
+       struct option options[] = {
+               OPT_GROUP("show specific options"),
+               OPT__DRY_RUN(&dry_run),
+               OPT_END()
+       };
+       struct ref_states states;
+
+       argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+
+       if (argc < 1)
+               usage_with_options(builtin_remote_usage, options);
+
+       memset(&states, 0, sizeof(states));
+       for (; argc; argc--, argv++) {
+               struct transport *transport;
+               const struct ref *ref;
+               struct strbuf buf;
+               int i, got_states;
+
+               states.remote = remote_get(*argv);
+               if (!states.remote)
+                       return error("No such remote: %s", *argv);
+               transport = transport_get(NULL, states.remote->url_nr > 0 ?
+                       states.remote->url[0] : NULL);
+               ref = transport_get_remote_refs(transport);
+               transport_disconnect(transport);
+
+               read_branches();
+               got_states = get_ref_states(ref, &states);
+               if (got_states)
+                       result = error("Error getting local info for '%s'",
+                                       states.remote->name);
+
+               if (prune) {
+                       struct strbuf buf;
+                       int prefix_len;
+
+                       strbuf_init(&buf, 0);
+                       if (states.remote->fetch_refspec_nr == 1 &&
+                                       states.remote->fetch->pattern &&
+                                       !strcmp(states.remote->fetch->src,
+                                               states.remote->fetch->dst))
+                               /* handle --mirror remote */
+                               strbuf_addstr(&buf, "refs/heads/");
+                       else
+                               strbuf_addf(&buf, "refs/remotes/%s/", *argv);
+                       prefix_len = buf.len;
+
+                       for (i = 0; i < states.stale.nr; i++) {
+                               strbuf_setlen(&buf, prefix_len);
+                               strbuf_addstr(&buf, states.stale.items[i].path);
+                               result |= delete_ref(buf.buf, NULL);
+                       }
+
+                       strbuf_release(&buf);
+                       goto cleanup_states;
+               }
+
+               printf("* remote %s\n  URL: %s\n", *argv,
+                       states.remote->url_nr > 0 ?
+                               states.remote->url[0] : "(no URL)");
+
+               for (i = 0; i < branch_list.nr; i++) {
+                       struct path_list_item *branch = branch_list.items + i;
+                       struct branch_info *info = branch->util;
+                       int j;
+
+                       if (!info->merge.nr || strcmp(*argv, info->remote))
+                               continue;
+                       printf("  Remote branch%s merged with 'git pull' "
+                               "while on branch %s\n   ",
+                               info->merge.nr > 1 ? "es" : "",
+                               branch->path);
+                       for (j = 0; j < info->merge.nr; j++)
+                               printf(" %s", info->merge.items[j].path);
+                       printf("\n");
+               }
+
+               if (got_states)
+                       continue;
+               strbuf_init(&buf, 0);
+               strbuf_addf(&buf, "  New remote branch%%s (next fetch will "
+                       "store in remotes/%s)", states.remote->name);
+               show_list(buf.buf, &states.new);
+               strbuf_release(&buf);
+               show_list("  Stale tracking branch%s (use 'git remote prune')",
+                               &states.stale);
+               show_list("  Tracked remote branch%s",
+                               &states.tracked);
+
+               if (states.remote->push_refspec_nr) {
+                       printf("  Local branch%s pushed with 'git push'\n   ",
+                               states.remote->push_refspec_nr > 1 ?
+                                       "es" : "");
+                       for (i = 0; i < states.remote->push_refspec_nr; i++) {
+                               struct refspec *spec = states.remote->push + i;
+                               printf(" %s%s%s%s", spec->force ? "+" : "",
+                                       skip_prefix(spec->src, "refs/heads/"),
+                                       spec->dst ? ":" : "",
+                                       skip_prefix(spec->dst, "refs/heads/"));
+                       }
+               }
+cleanup_states:
+               /* NEEDSWORK: free remote */
+               path_list_clear(&states.new, 0);
+               path_list_clear(&states.stale, 0);
+               path_list_clear(&states.tracked, 0);
+       }
+
+       return result;
+}
+
+static int get_one_remote_for_update(struct remote *remote, void *priv)
+{
+       struct path_list *list = priv;
+       if (!remote->skip_default_update)
+               path_list_append(xstrdup(remote->name), list);
+       return 0;
+}
+
+struct remote_group {
+       const char *name;
+       struct path_list *list;
+} remote_group;
+
+static int get_remote_group(const char *key, const char *value)
+{
+       if (!prefixcmp(key, "remotes.") &&
+                       !strcmp(key + 8, remote_group.name)) {
+               /* split list by white space */
+               int space = strcspn(value, " \t\n");
+               while (*value) {
+                       if (space > 1)
+                               path_list_append(xstrndup(value, space),
+                                               remote_group.list);
+                       value += space + (value[space] != '\0');
+                       space = strcspn(value, " \t\n");
+               }
+       }
+
+       return 0;
+}
+
+static int update(int argc, const char **argv)
+{
+       int i, result = 0;
+       struct path_list list = { NULL, 0, 0, 0 };
+       static const char *default_argv[] = { NULL, "default", NULL };
+
+       if (argc < 2) {
+               argc = 2;
+               argv = default_argv;
+       }
+
+       remote_group.list = &list;
+       for (i = 1; i < argc; i++) {
+               remote_group.name = argv[i];
+               result = git_config(get_remote_group);
+       }
+
+       if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
+               result = for_each_remote(get_one_remote_for_update, &list);
+
+       for (i = 0; i < list.nr; i++)
+               result |= fetch_remote(list.items[i].path);
+
+       /* all names were strdup()ed or strndup()ed */
+       list.strdup_paths = 1;
+       path_list_clear(&list, 0);
+
+       return result;
+}
+
+static int get_one_entry(struct remote *remote, void *priv)
+{
+       struct path_list *list = priv;
+
+       path_list_append(remote->name, list)->util = remote->url_nr ?
+               (void *)remote->url[0] : NULL;
+       if (remote->url_nr > 1)
+               warning("Remote %s has more than one URL", remote->name);
+
+       return 0;
+}
+
+static int show_all(void)
+{
+       struct path_list list = { NULL, 0, 0 };
+       int result = for_each_remote(get_one_entry, &list);
+
+       if (!result) {
+               int i;
+
+               sort_path_list(&list);
+               for (i = 0; i < list.nr; i++) {
+                       struct path_list_item *item = list.items + i;
+                       printf("%s%s%s\n", item->path,
+                               verbose ? "\t" : "",
+                               verbose && item->util ?
+                                       (const char *)item->util : "");
+               }
+       }
+       return result;
+}
+
+int cmd_remote(int argc, const char **argv, const char *prefix)
+{
+       struct option options[] = {
+               OPT__VERBOSE(&verbose),
+               OPT_END()
+       };
+       int result;
+
+       argc = parse_options(argc, argv, options, builtin_remote_usage,
+               PARSE_OPT_STOP_AT_NON_OPTION);
+
+       if (argc < 1)
+               result = show_all();
+       else if (!strcmp(argv[0], "add"))
+               result = add(argc, argv);
+       else if (!strcmp(argv[0], "rm"))
+               result = rm(argc, argv);
+       else if (!strcmp(argv[0], "show"))
+               result = show_or_prune(argc, argv, 0);
+       else if (!strcmp(argv[0], "prune"))
+               result = show_or_prune(argc, argv, 1);
+       else if (!strcmp(argv[0], "update"))
+               result = update(argc, argv);
+       else {
+               error("Unknown subcommand: %s", argv[0]);
+               usage_with_options(builtin_remote_usage, options);
+       }
+
+       return result ? 1 : 0;
+}
index bb3e19240a8921126600cddd70ac46b3b1bada29..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)
 {
@@ -165,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))
@@ -211,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 90dbb9d7c1a28fd463ba827cb8da2eab42e489df..0351d54435b566d5030f3a83df57fb140fffc143 100644 (file)
@@ -322,18 +322,24 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
                o->type = OPTION_CALLBACK;
                o->help = xstrdup(skipspaces(s));
                o->value = &parsed;
+               o->flags = PARSE_OPT_NOARG;
                o->callback = &parseopt_dump;
-               switch (s[-1]) {
-               case '=':
-                       s--;
-                       break;
-               case '?':
-                       o->flags = PARSE_OPT_OPTARG;
-                       s--;
-                       break;
-               default:
-                       o->flags = PARSE_OPT_NOARG;
-                       break;
+               while (s > sb.buf && strchr("*=?!", s[-1])) {
+                       switch (*--s) {
+                       case '=':
+                               o->flags &= ~PARSE_OPT_NOARG;
+                               break;
+                       case '?':
+                               o->flags &= ~PARSE_OPT_NOARG;
+                               o->flags |= PARSE_OPT_OPTARG;
+                               break;
+                       case '!':
+                               o->flags |= PARSE_OPT_NONEG;
+                               break;
+                       case '*':
+                               o->flags |= PARSE_OPT_HIDDEN;
+                               break;
+                       }
                }
 
                if (s - sb.buf == 1) /* short option only */
index af31abaaf862bb85a72e8052cf28a874d34c714a..bd795b1db7a4054a0218d1ec96794ad25d656896 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))
@@ -228,7 +229,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 {
        struct shortlog log;
        struct rev_info rev;
+       int nongit;
 
+       prefix = setup_git_directory_gently(&nongit);
        shortlog_init(&log);
 
        /* since -n is a shadowed rev argument, parse our args first */
@@ -258,7 +261,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
                die ("unrecognized argument: %s", argv[1]);
 
        /* assume HEAD if from a tty */
-       if (!rev.pending.nr && isatty(0))
+       if (!nongit && !rev.pending.nr && isatty(0))
                add_head_to_pending(&rev);
        if (rev.pending.nr == 0) {
                read_from_stdin(&log);
index 28c36fdcd1658968ff7c3e2f1d6ba6f364f99592..8dd959fe1c74507023f8e82c7f4682c1588ebac6 100644 (file)
@@ -50,12 +50,15 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
                size_t len = strlen(editor);
                int i = 0;
                const char *args[6];
+               struct strbuf arg0;
 
+               strbuf_init(&arg0, 0);
                if (strcspn(editor, "$ \t'") != len) {
                        /* there are specials */
+                       strbuf_addf(&arg0, "%s \"$@\"", editor);
                        args[i++] = "sh";
                        args[i++] = "-c";
-                       args[i++] = "$0 \"$@\"";
+                       args[i++] = arg0.buf;
                }
                args[i++] = editor;
                args[i++] = path;
@@ -63,6 +66,7 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
 
                if (run_command_v_opt_cd_env(args, 0, NULL, env))
                        die("There was a problem with the editor %s.", editor);
+               strbuf_release(&arg0);
        }
 
        if (!buffer)
index 9d2a8549509658d32f25a1bc6af0552dc8380988..50e07faa12f495b3270e1446573b88a255336877 100644 (file)
@@ -7,13 +7,11 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
-#include "tree-walk.h"
 #include "progress.h"
 #include "decorate.h"
-#include "fsck.h"
 
-static int dry_run, quiet, recover, has_errors, strict;
-static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
+static int dry_run, quiet, recover, has_errors;
+static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
 
 /* We always read in 4kB chunks. */
 static unsigned char buffer[4096];
@@ -33,16 +31,6 @@ static struct obj_buffer *lookup_object_buffer(struct object *base)
        return lookup_decoration(&obj_decorate, base);
 }
 
-static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
-{
-       struct obj_buffer *obj;
-       obj = xcalloc(1, sizeof(struct obj_buffer));
-       obj->buffer = buffer;
-       obj->size = size;
-       if (add_decoration(&obj_decorate, object, obj))
-               die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
-}
-
 /*
  * Make sure at least "min" bytes are available in the buffer, and
  * return the pointer to the buffer.
@@ -146,58 +134,9 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
 struct obj_info {
        off_t offset;
        unsigned char sha1[20];
-       struct object *obj;
 };
 
-#define FLAG_OPEN (1u<<20)
-#define FLAG_WRITTEN (1u<<21)
-
 static struct obj_info *obj_list;
-unsigned nr_objects;
-
-static void write_cached_object(struct object *obj)
-{
-       unsigned char sha1[20];
-       struct obj_buffer *obj_buf = lookup_object_buffer(obj);
-       if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
-               die("failed to write object %s", sha1_to_hex(obj->sha1));
-       obj->flags |= FLAG_WRITTEN;
-}
-
-static int check_object(struct object *obj, int type, void *data)
-{
-       if (!obj)
-               return 0;
-
-       if (obj->flags & FLAG_WRITTEN)
-               return 1;
-
-       if (type != OBJ_ANY && obj->type != type)
-               die("object type mismatch");
-
-       if (!(obj->flags & FLAG_OPEN)) {
-               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_WRITTEN;
-               return 1;
-       }
-
-       if (fsck_object(obj, 1, fsck_error_function))
-               die("Error in object");
-       if (!fsck_walk(obj, check_object, 0))
-               die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
-       write_cached_object(obj);
-       return 1;
-}
-
-static void write_rest(void)
-{
-       unsigned i;
-       for (i = 0; i < nr_objects; i++)
-               check_object(obj_list[i].obj, OBJ_ANY, 0);
-}
 
 static void added_object(unsigned nr, enum object_type type,
                         void *data, unsigned long size);
@@ -205,36 +144,9 @@ static void added_object(unsigned nr, enum object_type type,
 static void write_object(unsigned nr, enum object_type type,
                         void *buf, unsigned long size)
 {
+       if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+               die("failed to write object");
        added_object(nr, type, buf, size);
-       if (!strict) {
-               if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
-                       die("failed to write object");
-               free(buf);
-               obj_list[nr].obj = 0;
-       } else if (type == OBJ_BLOB) {
-               struct blob *blob;
-               if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
-                       die("failed to write object");
-               free(buf);
-
-               blob = lookup_blob(obj_list[nr].sha1);
-               if (blob)
-                       blob->object.flags |= FLAG_WRITTEN;
-               else
-                       die("invalid blob object");
-               obj_list[nr].obj = 0;
-       } else {
-               struct object *obj;
-               int eaten;
-               hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
-               obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
-               if (!obj)
-                       die("invalid %s", typename(type));
-               /* buf is stored via add_object_buffer and in obj, if its a tree or commit */
-               add_object_buffer(obj, buf, size);
-               obj->flags |= FLAG_OPEN;
-               obj_list[nr].obj = obj;
-       }
 }
 
 static void resolve_delta(unsigned nr, enum object_type type,
@@ -251,6 +163,7 @@ static void resolve_delta(unsigned nr, enum object_type type,
                die("failed to apply delta");
        free(delta);
        write_object(nr, type, result, result_size);
+       free(result);
 }
 
 static void added_object(unsigned nr, enum object_type type,
@@ -280,8 +193,7 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
 
        if (!dry_run && buf)
                write_object(nr, type, buf, size);
-       else
-               free(buf);
+       free(buf);
 }
 
 static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
@@ -424,8 +336,7 @@ static void unpack_all(void)
        int i;
        struct progress *progress = NULL;
        struct pack_header *hdr = fill(sizeof(struct pack_header));
-
-       nr_objects = ntohl(hdr->hdr_entries);
+       unsigned nr_objects = ntohl(hdr->hdr_entries);
 
        if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
                die("bad pack file");
@@ -436,7 +347,6 @@ static void unpack_all(void)
        if (!quiet)
                progress = start_progress("Unpacking objects", nr_objects);
        obj_list = xmalloc(nr_objects * sizeof(*obj_list));
-       memset(obj_list, 0, nr_objects * sizeof(*obj_list));
        for (i = 0; i < nr_objects; i++) {
                unpack_one(i);
                display_progress(progress, i + 1);
@@ -472,10 +382,6 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
                                recover = 1;
                                continue;
                        }
-                       if (!strcmp(arg, "--strict")) {
-                               strict = 1;
-                               continue;
-                       }
                        if (!prefixcmp(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
@@ -501,8 +407,6 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
        unpack_all();
        SHA1_Update(&ctx, buffer, offset);
        SHA1_Final(sha1, &ctx);
-       if (strict)
-               write_rest();
        if (hashcmp(fill(20), sha1))
                die("final sha1 did not match");
        use(20);
index 674c8a141faf808883c9de283d10da01b3f9c2d5..95126fd0c12149034372afd8eb37b944be684957 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -67,6 +67,7 @@ extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
+extern int cmd_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index e23030262328761669f4704a3e3731a5e93d5b0c..2a1e7ec6b2bf712af80813936b5aed434d02090e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -346,12 +346,12 @@ extern void verify_non_filename(const char *prefix, const char *name);
 /* Initialize and use the cache information */
 extern int read_index(struct index_state *);
 extern int read_index_from(struct index_state *, const char *path);
-extern int write_index(struct index_state *, int newfd);
+extern int write_index(const struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
-extern int unmerged_index(struct index_state *);
+extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
 extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
-extern int index_name_pos(struct index_state *, const char *name, int namelen);
+extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
 #define ADD_CACHE_SKIP_DFCHECK 4       /* Ok to skip DF conflict checks */
@@ -368,8 +368,8 @@ extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 #define CE_MATCH_IGNORE_VALID          01
 /* do not check the contents but report dirty on racily-clean entries */
 #define CE_MATCH_RACY_IS_DIRTY 02
-extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
@@ -536,6 +536,7 @@ extern int create_symref(const char *ref, const char *refs_heads_master, const c
 extern int validate_headref(const char *ref);
 
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
 
 extern void *read_object_with_reference(const unsigned char *sha1,
@@ -543,6 +544,9 @@ extern void *read_object_with_reference(const unsigned char *sha1,
                                        unsigned long *size,
                                        unsigned char *sha1_ret);
 
+extern struct object *peel_to_type(const char *name, int namelen,
+                                  struct object *o, enum object_type);
+
 enum date_mode {
        DATE_NORMAL = 0,
        DATE_RELATIVE,
index a1e95914263355e2021f54be4837ed25f1210757..2f63bc8b2fac0d748e13d4435d550d61002092b3 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -70,7 +70,7 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
                                 struct strbuf *,
                                 int abbrev, const char *subject,
                                 const char *after_subject, enum date_mode,
-                               int non_ascii_present);
+                               int need_8bit_cte);
 void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
                   const char *line, enum date_mode dmode,
                   const char *encoding);
@@ -80,7 +80,7 @@ void pp_title_line(enum cmit_fmt fmt,
                   const char *subject,
                   const char *after_subject,
                   const char *encoding,
-                  int plain_non_ascii);
+                  int need_8bit_cte);
 void pp_remainder(enum cmit_fmt fmt,
                  const char **msg_p,
                  struct strbuf *sb,
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..7868dfd93a8dab01927c3f5907733baf556e9b59 100644 (file)
@@ -46,3 +46,5 @@ NO_MKDTEMP=@NO_MKDTEMP@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
+SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
index 85d7ef570d390d56d3ec3ba8af406eee30d700ea..82584e91532264790bac7efd953f9f5da09576c1 100644 (file)
@@ -326,6 +326,60 @@ else
        NO_C99_FORMAT=
 fi
 AC_SUBST(NO_C99_FORMAT)
+#
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+AC_CACHE_CHECK([whether system succeeds to read fopen'ed directory],
+ [ac_cv_fread_reads_directories],
+[
+AC_RUN_IFELSE(
+       [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+               [[char c;
+               FILE *f = fopen(".", "r");
+               return f && fread(&c, 1, 1, f)]])],
+       [ac_cv_fread_reads_directories=no],
+       [ac_cv_fread_reads_directories=yes])
+])
+if test $ac_cv_fread_reads_directories = yes; then
+       FREAD_READS_DIRECTORIES=UnfortunatelyYes
+else
+       FREAD_READS_DIRECTORIES=
+fi
+AC_SUBST(FREAD_READS_DIRECTORIES)
+#
+# 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..5046f699349aa87bedbc79a997dfcb9ea54e7618 100755 (executable)
@@ -70,22 +70,30 @@ __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
                        r="|REBASE-i"
-                       b="$(cat $g/.dotest-merge/head-name)"
+                       b="$(cat "$g/.dotest-merge/head-name")"
                elif [ -d "$g/.dotest-merge" ]
                then
                        r="|REBASE-m"
-                       b="$(cat $g/.dotest-merge/head-name)"
+                       b="$(cat "$g/.dotest-merge/head-name")"
                elif [ -f "$g/MERGE_HEAD" ]
                then
                        r="|MERGING"
                        b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
-                       if [ -f $g/BISECT_LOG ]
+                       if [ -f "$g/BISECT_LOG" ]
                        then
                                r="|BISECTING"
                        fi
@@ -93,7 +101,7 @@ __git_ps1 ()
                        then
                                if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
                                then
-                                       b="$(cut -c1-7 $g/HEAD)..."
+                                       b="$(cut -c1-7 "$g/HEAD")..."
                                fi
                        fi
                fi
@@ -113,13 +121,21 @@ __gitcomp ()
        if [ $# -gt 2 ]; then
                cur="$3"
        fi
-       for c in $1; do
-               case "$c$4" in
-               --*=*) all="$all$c$4$s" ;;
-               *.)    all="$all$c$4$s" ;;
-               *)     all="$all$c$4 $s" ;;
-               esac
-       done
+       case "$cur" in
+       --*=)
+               COMPREPLY=()
+               return
+               ;;
+       *)
+               for c in $1; do
+                       case "$c$4" in
+                       --*=*) all="$all$c$4$s" ;;
+                       *.)    all="$all$c$4$s" ;;
+                       *)     all="$all$c$4 $s" ;;
+                       esac
+               done
+               ;;
+       esac
        IFS=$s
        COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur"))
        return
@@ -376,7 +392,6 @@ __git_commands ()
                show-index)       : plumbing;;
                ssh-*)            : transport;;
                stripspace)       : plumbing;;
-               svn)              : import export;;
                symbolic-ref)     : plumbing;;
                tar-tree)         : deprecated;;
                unpack-file)      : plumbing;;
@@ -420,6 +435,22 @@ __git_aliased_command ()
        done
 }
 
+__git_find_subcommand ()
+{
+       local word subcommand c=1
+
+       while [ $c -lt $COMP_CWORD ]; do
+               word="${COMP_WORDS[c]}"
+               for subcommand in $1; do
+                       if [ "$subcommand" = "$word" ]; then
+                               echo "$subcommand"
+                               return
+                       fi
+               done
+               c=$((++c))
+       done
+}
+
 __git_whitespacelist="nowarn warn error error-all strip"
 
 _git_am ()
@@ -477,24 +508,14 @@ _git_add ()
 
 _git_bisect ()
 {
-       local i c=1 command
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
-               case "$i" in
-               start|bad|good|reset|visualize|replay|log)
-                       command="$i"
-                       break
-                       ;;
-               esac
-               c=$((++c))
-       done
-
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
-               __gitcomp "start bad good reset visualize replay log"
+       local subcommands="start bad good reset visualize replay log"
+       local subcommand="$(__git_find_subcommand "$subcommands")"
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
                return
        fi
 
-       case "$command" in
+       case "$subcommand" in
        bad|good|reset)
                __gitcomp "$(__git_refs)"
                ;;
@@ -506,7 +527,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 ()
@@ -802,8 +849,8 @@ _git_push ()
 
 _git_rebase ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
-       if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then
+       local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+       if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then
                __gitcomp "--continue --skip --abort"
                return
        fi
@@ -922,7 +969,6 @@ _git_config ()
                core.sharedRepository
                core.warnAmbiguousRefs
                core.compression
-               core.legacyHeaders
                core.packedGitWindowSize
                core.packedGitLimit
                clean.requireForce
@@ -999,21 +1045,13 @@ _git_config ()
 
 _git_remote ()
 {
-       local i c=1 command
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
-               case "$i" in
-               add|rm|show|prune|update) command="$i"; break ;;
-               esac
-               c=$((++c))
-       done
-
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
-               __gitcomp "add rm show prune update"
+       local subcommands="add rm show prune update"
+       local subcommand="$(__git_find_subcommand "$subcommands")"
+       if [ -z "$subcommand" ]; then
                return
        fi
 
-       case "$command" in
+       case "$subcommand" in
        rm|show|prune)
                __gitcomp "$(__git_remotes)"
                ;;
@@ -1087,34 +1125,107 @@ _git_show ()
 
 _git_stash ()
 {
-       __gitcomp 'list show apply clear'
+       local subcommands='save list show apply clear drop pop create'
+       if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
+               __gitcomp "$subcommands"
+       fi
 }
 
 _git_submodule ()
 {
-       local i c=1 command
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
-               case "$i" in
-               add|status|init|update) command="$i"; break ;;
-               esac
-               c=$((++c))
-       done
-
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+       local subcommands="add status init update"
+       if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
                local cur="${COMP_WORDS[COMP_CWORD]}"
                case "$cur" in
                --*)
                        __gitcomp "--quiet --cached"
                        ;;
                *)
-                       __gitcomp "add status init update"
+                       __gitcomp "$subcommands"
                        ;;
                esac
                return
        fi
 }
 
+_git_svn ()
+{
+       local subcommands="
+               init fetch clone rebase dcommit log find-rev
+               set-tree commit-diff info create-ignore propget
+               proplist show-ignore show-externals
+               "
+       local subcommand="$(__git_find_subcommand "$subcommands")"
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
+       else
+               local remote_opts="--username= --config-dir= --no-auth-cache"
+               local fc_opts="
+                       --follow-parent --authors-file= --repack=
+                       --no-metadata --use-svm-props --use-svnsync-props
+                       --log-window-size= --no-checkout --quiet
+                       --repack-flags --user-log-author $remote_opts
+                       "
+               local init_opts="
+                       --template= --shared= --trunk= --tags=
+                       --branches= --stdlayout --minimize-url
+                       --no-metadata --use-svm-props --use-svnsync-props
+                       --rewrite-root= $remote_opts
+                       "
+               local cmt_opts="
+                       --edit --rmdir --find-copies-harder --copy-similarity=
+                       "
+
+               local cur="${COMP_WORDS[COMP_CWORD]}"
+               case "$subcommand,$cur" in
+               fetch,--*)
+                       __gitcomp "--revision= --fetch-all $fc_opts"
+                       ;;
+               clone,--*)
+                       __gitcomp "--revision= $fc_opts $init_opts"
+                       ;;
+               init,--*)
+                       __gitcomp "$init_opts"
+                       ;;
+               dcommit,--*)
+                       __gitcomp "
+                               --merge --strategy= --verbose --dry-run
+                               --fetch-all --no-rebase $cmt_opts $fc_opts
+                               "
+                       ;;
+               set-tree,--*)
+                       __gitcomp "--stdin $cmt_opts $fc_opts"
+                       ;;
+               create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
+               show-externals,--*)
+                       __gitcomp "--revision="
+                       ;;
+               log,--*)
+                       __gitcomp "
+                               --limit= --revision= --verbose --incremental
+                               --oneline --show-commit --non-recursive
+                               --authors-file=
+                               "
+                       ;;
+               rebase,--*)
+                       __gitcomp "
+                               --merge --verbose --strategy= --local
+                               --fetch-all $fc_opts
+                               "
+                       ;;
+               commit-diff,--*)
+                       __gitcomp "--message= --file= --revision= $cmt_opts"
+                       ;;
+               info,--*)
+                       __gitcomp "--url"
+                       ;;
+               *)
+                       COMPREPLY=()
+                       ;;
+               esac
+       fi
+}
+
 _git_tag ()
 {
        local i c=1 f=0
@@ -1164,15 +1275,18 @@ _git ()
                c=$((++c))
        done
 
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+       if [ -z "$command" ]; then
                case "${COMP_WORDS[COMP_CWORD]}" in
                --*=*) COMPREPLY=() ;;
                --*)   __gitcomp "
+                       --paginate
                        --no-pager
                        --git-dir=
                        --bare
                        --version
                        --exec-path
+                       --work-tree=
+                       --help
                        "
                        ;;
                *)     __gitcomp "$(__git_commands) $(__git_aliases)" ;;
@@ -1216,6 +1330,7 @@ _git ()
        show-branch) _git_log ;;
        stash)       _git_stash ;;
        submodule)   _git_submodule ;;
+       svn)         _git_svn ;;
        tag)         _git_tag ;;
        whatchanged) _git_log ;;
        *)           COMPREPLY=() ;;
@@ -1266,6 +1381,7 @@ complete -o default -o nospace -F _git_shortlog git-shortlog
 complete -o default -o nospace -F _git_show git-show
 complete -o default -o nospace -F _git_stash git-stash
 complete -o default -o nospace -F _git_submodule git-submodule
+complete -o default -o nospace -F _git_svn git-svn
 complete -o default -o nospace -F _git_log git-show-branch
 complete -o default -o nospace -F _git_tag git-tag
 complete -o default -o nospace -F _git_log git-whatchanged
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))
diff --git a/contrib/examples/git-remote.perl b/contrib/examples/git-remote.perl
new file mode 100755 (executable)
index 0000000..b30ed73
--- /dev/null
@@ -0,0 +1,477 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Git;
+my $git = Git->repository();
+
+sub add_remote_config {
+       my ($hash, $name, $what, $value) = @_;
+       if ($what eq 'url') {
+               # Having more than one is Ok -- it is used for push.
+               if (! exists $hash->{'URL'}) {
+                       $hash->{$name}{'URL'} = $value;
+               }
+       }
+       elsif ($what eq 'fetch') {
+               $hash->{$name}{'FETCH'} ||= [];
+               push @{$hash->{$name}{'FETCH'}}, $value;
+       }
+       elsif ($what eq 'push') {
+               $hash->{$name}{'PUSH'} ||= [];
+               push @{$hash->{$name}{'PUSH'}}, $value;
+       }
+       if (!exists $hash->{$name}{'SOURCE'}) {
+               $hash->{$name}{'SOURCE'} = 'config';
+       }
+}
+
+sub add_remote_remotes {
+       my ($hash, $file, $name) = @_;
+
+       if (exists $hash->{$name}) {
+               $hash->{$name}{'WARNING'} = 'ignored due to config';
+               return;
+       }
+
+       my $fh;
+       if (!open($fh, '<', $file)) {
+               print STDERR "Warning: cannot open $file\n";
+               return;
+       }
+       my $it = { 'SOURCE' => 'remotes' };
+       $hash->{$name} = $it;
+       while (<$fh>) {
+               chomp;
+               if (/^URL:\s*(.*)$/) {
+                       # Having more than one is Ok -- it is used for push.
+                       if (! exists $it->{'URL'}) {
+                               $it->{'URL'} = $1;
+                       }
+               }
+               elsif (/^Push:\s*(.*)$/) {
+                       $it->{'PUSH'} ||= [];
+                       push @{$it->{'PUSH'}}, $1;
+               }
+               elsif (/^Pull:\s*(.*)$/) {
+                       $it->{'FETCH'} ||= [];
+                       push @{$it->{'FETCH'}}, $1;
+               }
+               elsif (/^\#/) {
+                       ; # ignore
+               }
+               else {
+                       print STDERR "Warning: funny line in $file: $_\n";
+               }
+       }
+       close($fh);
+}
+
+sub list_remote {
+       my ($git) = @_;
+       my %seen = ();
+       my @remotes = eval {
+               $git->command(qw(config --get-regexp), '^remote\.');
+       };
+       for (@remotes) {
+               if (/^remote\.(\S+?)\.([^.\s]+)\s+(.*)$/) {
+                       add_remote_config(\%seen, $1, $2, $3);
+               }
+       }
+
+       my $dir = $git->repo_path() . "/remotes";
+       if (opendir(my $dh, $dir)) {
+               local $_;
+               while ($_ = readdir($dh)) {
+                       chomp;
+                       next if (! -f "$dir/$_" || ! -r _);
+                       add_remote_remotes(\%seen, "$dir/$_", $_);
+               }
+       }
+
+       return \%seen;
+}
+
+sub add_branch_config {
+       my ($hash, $name, $what, $value) = @_;
+       if ($what eq 'remote') {
+               if (exists $hash->{$name}{'REMOTE'}) {
+                       print STDERR "Warning: more than one branch.$name.remote\n";
+               }
+               $hash->{$name}{'REMOTE'} = $value;
+       }
+       elsif ($what eq 'merge') {
+               $hash->{$name}{'MERGE'} ||= [];
+               push @{$hash->{$name}{'MERGE'}}, $value;
+       }
+}
+
+sub list_branch {
+       my ($git) = @_;
+       my %seen = ();
+       my @branches = eval {
+               $git->command(qw(config --get-regexp), '^branch\.');
+       };
+       for (@branches) {
+               if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) {
+                       add_branch_config(\%seen, $1, $2, $3);
+               }
+       }
+
+       return \%seen;
+}
+
+my $remote = list_remote($git);
+my $branch = list_branch($git);
+
+sub update_ls_remote {
+       my ($harder, $info) = @_;
+
+       return if (($harder == 0) ||
+                  (($harder == 1) && exists $info->{'LS_REMOTE'}));
+
+       my @ref = map {
+               s|^[0-9a-f]{40}\s+refs/heads/||;
+               $_;
+       } $git->command(qw(ls-remote --heads), $info->{'URL'});
+       $info->{'LS_REMOTE'} = \@ref;
+}
+
+sub list_wildcard_mapping {
+       my ($forced, $ours, $ls) = @_;
+       my %refs;
+       for (@$ls) {
+               $refs{$_} = 01; # bit #0 to say "they have"
+       }
+       for ($git->command('for-each-ref', "refs/remotes/$ours")) {
+               chomp;
+               next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||);
+               next if ($_ eq 'HEAD');
+               $refs{$_} ||= 0;
+               $refs{$_} |= 02; # bit #1 to say "we have"
+       }
+       my (@new, @stale, @tracked);
+       for (sort keys %refs) {
+               my $have = $refs{$_};
+               if ($have == 1) {
+                       push @new, $_;
+               }
+               elsif ($have == 2) {
+                       push @stale, $_;
+               }
+               elsif ($have == 3) {
+                       push @tracked, $_;
+               }
+       }
+       return \@new, \@stale, \@tracked;
+}
+
+sub list_mapping {
+       my ($name, $info) = @_;
+       my $fetch = $info->{'FETCH'};
+       my $ls = $info->{'LS_REMOTE'};
+       my (@new, @stale, @tracked);
+
+       for (@$fetch) {
+               next unless (/(\+)?([^:]+):(.*)/);
+               my ($forced, $theirs, $ours) = ($1, $2, $3);
+               if ($theirs eq 'refs/heads/*' &&
+                   $ours =~ /^refs\/remotes\/(.*)\/\*$/) {
+                       # wildcard mapping
+                       my ($w_new, $w_stale, $w_tracked)
+                               = list_wildcard_mapping($forced, $1, $ls);
+                       push @new, @$w_new;
+                       push @stale, @$w_stale;
+                       push @tracked, @$w_tracked;
+               }
+               elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
+                       print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
+               }
+               elsif ($theirs =~ s|^refs/heads/||) {
+                       if (!grep { $_ eq $theirs } @$ls) {
+                               push @stale, $theirs;
+                       }
+                       elsif ($ours ne '') {
+                               push @tracked, $theirs;
+                       }
+               }
+       }
+       return \@new, \@stale, \@tracked;
+}
+
+sub show_mapping {
+       my ($name, $info) = @_;
+       my ($new, $stale, $tracked) = list_mapping($name, $info);
+       if (@$new) {
+               print "  New remote branches (next fetch will store in remotes/$name)\n";
+               print "    @$new\n";
+       }
+       if (@$stale) {
+               print "  Stale tracking branches in remotes/$name (use 'git remote prune')\n";
+               print "    @$stale\n";
+       }
+       if (@$tracked) {
+               print "  Tracked remote branches\n";
+               print "    @$tracked\n";
+       }
+}
+
+sub prune_remote {
+       my ($name, $ls_remote) = @_;
+       if (!exists $remote->{$name}) {
+               print STDERR "No such remote $name\n";
+               return 1;
+       }
+       my $info = $remote->{$name};
+       update_ls_remote($ls_remote, $info);
+
+       my ($new, $stale, $tracked) = list_mapping($name, $info);
+       my $prefix = "refs/remotes/$name";
+       foreach my $to_prune (@$stale) {
+               my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune");
+               $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]);
+       }
+       return 0;
+}
+
+sub show_remote {
+       my ($name, $ls_remote) = @_;
+       if (!exists $remote->{$name}) {
+               print STDERR "No such remote $name\n";
+               return 1;
+       }
+       my $info = $remote->{$name};
+       update_ls_remote($ls_remote, $info);
+
+       print "* remote $name\n";
+       print "  URL: $info->{'URL'}\n";
+       for my $branchname (sort keys %$branch) {
+               next unless (defined $branch->{$branchname}{'REMOTE'} &&
+                            $branch->{$branchname}{'REMOTE'} eq $name);
+               my @merged = map {
+                       s|^refs/heads/||;
+                       $_;
+               } split(' ',"@{$branch->{$branchname}{'MERGE'}}");
+               next unless (@merged);
+               print "  Remote branch(es) merged with 'git pull' while on branch $branchname\n";
+               print "    @merged\n";
+       }
+       if ($info->{'LS_REMOTE'}) {
+               show_mapping($name, $info);
+       }
+       if ($info->{'PUSH'}) {
+               my @pushed = map {
+                       s|^refs/heads/||;
+                       s|^\+refs/heads/|+|;
+                       s|:refs/heads/|:|;
+                       $_;
+               } @{$info->{'PUSH'}};
+               print "  Local branch(es) pushed with 'git push'\n";
+               print "    @pushed\n";
+       }
+       return 0;
+}
+
+sub add_remote {
+       my ($name, $url, $opts) = @_;
+       if (exists $remote->{$name}) {
+               print STDERR "remote $name already exists.\n";
+               exit(1);
+       }
+       $git->command('config', "remote.$name.url", $url);
+       my $track = $opts->{'track'} || ["*"];
+
+       for (@$track) {
+               $git->command('config', '--add', "remote.$name.fetch",
+                               $opts->{'mirror'} ?
+                               "+refs/$_:refs/$_" :
+                               "+refs/heads/$_:refs/remotes/$name/$_");
+       }
+       if ($opts->{'fetch'}) {
+               $git->command('fetch', $name);
+       }
+       if (exists $opts->{'master'}) {
+               $git->command('symbolic-ref', "refs/remotes/$name/HEAD",
+                             "refs/remotes/$name/$opts->{'master'}");
+       }
+}
+
+sub update_remote {
+       my ($name) = @_;
+       my @remotes;
+
+        my $conf = $git->config("remotes." . $name);
+       if (defined($conf)) {
+               @remotes = split(' ', $conf);
+       } elsif ($name eq 'default') {
+               @remotes = ();
+               for (sort keys %$remote) {
+                       my $do_fetch = $git->config_bool("remote." . $_ .
+                                                   ".skipDefaultUpdate");
+                       unless ($do_fetch) {
+                               push @remotes, $_;
+                       }
+               }
+       } else {
+               print STDERR "Remote group $name does not exists.\n";
+               exit(1);
+       }
+       for (@remotes) {
+               print "Updating $_\n";
+               $git->command('fetch', "$_");
+       }
+}
+
+sub rm_remote {
+       my ($name) = @_;
+       if (!exists $remote->{$name}) {
+               print STDERR "No such remote $name\n";
+               return 1;
+       }
+
+       $git->command('config', '--remove-section', "remote.$name");
+
+       eval {
+           my @trackers = $git->command('config', '--get-regexp',
+                       'branch.*.remote', $name);
+               for (@trackers) {
+                       /^branch\.(.*)?\.remote/;
+                       $git->config('--unset', "branch.$1.remote");
+                       $git->config('--unset', "branch.$1.merge");
+               }
+       };
+
+       my @refs = $git->command('for-each-ref',
+               '--format=%(refname) %(objectname)', "refs/remotes/$name");
+       for (@refs) {
+               my ($ref, $object) = split;
+               $git->command(qw(update-ref -d), $ref, $object);
+       }
+       return 0;
+}
+
+sub add_usage {
+       print STDERR "Usage: git remote add [-f] [-t track]* [-m master] <name> <url>\n";
+       exit(1);
+}
+
+my $VERBOSE = 0;
+@ARGV = grep {
+       if ($_ eq '-v' or $_ eq '--verbose') {
+               $VERBOSE=1;
+               0
+       } else {
+               1
+       }
+} @ARGV;
+
+if (!@ARGV) {
+       for (sort keys %$remote) {
+               print "$_";
+               print "\t$remote->{$_}->{URL}" if $VERBOSE;
+               print "\n";
+       }
+}
+elsif ($ARGV[0] eq 'show') {
+       my $ls_remote = 1;
+       my $i;
+       for ($i = 1; $i < @ARGV; $i++) {
+               if ($ARGV[$i] eq '-n') {
+                       $ls_remote = 0;
+               }
+               else {
+                       last;
+               }
+       }
+       if ($i >= @ARGV) {
+               print STDERR "Usage: git remote show <remote>\n";
+               exit(1);
+       }
+       my $status = 0;
+       for (; $i < @ARGV; $i++) {
+               $status |= show_remote($ARGV[$i], $ls_remote);
+       }
+       exit($status);
+}
+elsif ($ARGV[0] eq 'update') {
+       if (@ARGV <= 1) {
+               update_remote("default");
+               exit(1);
+       }
+       for (my $i = 1; $i < @ARGV; $i++) {
+               update_remote($ARGV[$i]);
+       }
+}
+elsif ($ARGV[0] eq 'prune') {
+       my $ls_remote = 1;
+       my $i;
+       for ($i = 1; $i < @ARGV; $i++) {
+               if ($ARGV[$i] eq '-n') {
+                       $ls_remote = 0;
+               }
+               else {
+                       last;
+               }
+       }
+       if ($i >= @ARGV) {
+               print STDERR "Usage: git remote prune <remote>\n";
+               exit(1);
+       }
+       my $status = 0;
+       for (; $i < @ARGV; $i++) {
+               $status |= prune_remote($ARGV[$i], $ls_remote);
+       }
+        exit($status);
+}
+elsif ($ARGV[0] eq 'add') {
+       my %opts = ();
+       while (1 < @ARGV && $ARGV[1] =~ /^-/) {
+               my $opt = $ARGV[1];
+               shift @ARGV;
+               if ($opt eq '-f' || $opt eq '--fetch') {
+                       $opts{'fetch'} = 1;
+                       next;
+               }
+               if ($opt eq '-t' || $opt eq '--track') {
+                       if (@ARGV < 1) {
+                               add_usage();
+                       }
+                       $opts{'track'} ||= [];
+                       push @{$opts{'track'}}, $ARGV[1];
+                       shift @ARGV;
+                       next;
+               }
+               if ($opt eq '-m' || $opt eq '--master') {
+                       if ((@ARGV < 1) || exists $opts{'master'}) {
+                               add_usage();
+                       }
+                       $opts{'master'} = $ARGV[1];
+                       shift @ARGV;
+                       next;
+               }
+               if ($opt eq '--mirror') {
+                       $opts{'mirror'} = 1;
+                       next;
+               }
+               add_usage();
+       }
+       if (@ARGV != 3) {
+               add_usage();
+       }
+       add_remote($ARGV[1], $ARGV[2], \%opts);
+}
+elsif ($ARGV[0] eq 'rm') {
+       if (@ARGV <= 1) {
+               print STDERR "Usage: git remote rm <remote>\n";
+               exit(1);
+       }
+       exit(rm_remote($ARGV[1]));
+}
+else {
+       print STDERR "Usage: git remote\n";
+       print STDERR "       git remote add <name> <url>\n";
+       print STDERR "       git remote rm <name>\n";
+       print STDERR "       git remote show <name>\n";
+       print STDERR "       git remote prune <name>\n";
+       print STDERR "       git remote update [group]\n";
+       exit(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 2838546d16073f29b3a87ce9126d92b0f640be5e..7959eab902d28bb3307c542514ca4c5f49deee0f 100755 (executable)
@@ -63,7 +63,7 @@ mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
 # create the links to the original repo.  explictly exclude index, HEAD and
 # logs/HEAD from the list since they are purely related to the current working
 # directory, and should not be shared.
-for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache
+for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
 do
        case $x in
        */*)
index 4581b594d0a65d5bbe782b1db5d69f33189a0e50..52dbac34a40b2ca7bcbb7c2343a0487e5cb90ccf 100644 (file)
@@ -600,8 +600,7 @@ static void mark_merge_entries(void)
  */
 static void do_oneway_diff(struct unpack_trees_options *o,
        struct cache_entry *idx,
-       struct cache_entry *tree,
-       int idx_pos, int idx_nr)
+       struct cache_entry *tree)
 {
        struct rev_info *revs = o->unpack_data;
        int match_missing, cached;
@@ -642,32 +641,19 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        show_modified(revs, tree, idx, 1, cached, match_missing);
 }
 
-/*
- * Count how many index entries go with the first one
- */
-static inline int count_skip(const struct cache_entry *src, int pos)
+static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-       int skip = 1;
-
-       /* We can only have multiple entries if the first one is not stage-0 */
-       if (ce_stage(src)) {
-               struct cache_entry **p = active_cache + pos;
-               int namelen = ce_namelen(src);
-
-               for (;;) {
-                       const struct cache_entry *ce;
-                       pos++;
-                       if (pos >= active_nr)
-                               break;
-                       ce = *++p;
-                       if (ce_namelen(ce) != namelen)
-                               break;
-                       if (memcmp(ce->name, src->name, namelen))
-                               break;
-                       skip++;
-               }
+       int len = ce_namelen(ce);
+       const struct index_state *index = o->src_index;
+
+       while (o->pos < index->cache_nr) {
+               struct cache_entry *next = index->cache[o->pos];
+               if (len != ce_namelen(next))
+                       break;
+               if (memcmp(ce->name, next->name, len))
+                       break;
+               o->pos++;
        }
-       return skip;
 }
 
 /*
@@ -685,17 +671,14 @@ static inline int count_skip(const struct cache_entry *src, int pos)
  * the fairly complex unpack_trees() semantic requirements, including
  * the skipping, the path matching, the type conflict cases etc.
  */
-static int oneway_diff(struct cache_entry **src,
-       struct unpack_trees_options *o,
-       int index_pos)
+static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
 {
-       int skip = 0;
        struct cache_entry *idx = src[0];
        struct cache_entry *tree = src[1];
        struct rev_info *revs = o->unpack_data;
 
-       if (index_pos >= 0)
-               skip = count_skip(idx, index_pos);
+       if (idx && ce_stage(idx))
+               skip_same_name(idx, o);
 
        /*
         * Unpack-trees generates a DF/conflict entry if
@@ -707,9 +690,9 @@ static int oneway_diff(struct cache_entry **src,
                tree = NULL;
 
        if (ce_path_match(idx ? idx : tree, revs->prune_data))
-               do_oneway_diff(o, idx, tree, index_pos, skip);
+               do_oneway_diff(o, idx, tree);
 
-       return skip;
+       return 0;
 }
 
 int run_diff_index(struct rev_info *revs, int cached)
@@ -734,6 +717,8 @@ int run_diff_index(struct rev_info *revs, int cached)
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = revs;
+       opts.src_index = &the_index;
+       opts.dst_index = NULL;
 
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts))
@@ -787,6 +772,8 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = &revs;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
 
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts))
diff --git a/diff.c b/diff.c
index 00e1590c6eec291c2a6b3f831b62f402768c7042..8022e678d1fc26eab75d73d01110d635fba11fd7 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -256,40 +256,41 @@ static int count_lines(const char *data, int size)
        return count;
 }
 
-static void print_line_count(int count)
+static void print_line_count(FILE *file, int count)
 {
        switch (count) {
        case 0:
-               printf("0,0");
+               fprintf(file, "0,0");
                break;
        case 1:
-               printf("1");
+               fprintf(file, "1");
                break;
        default:
-               printf("1,%d", count);
+               fprintf(file, "1,%d", count);
                break;
        }
 }
 
-static void copy_file_with_prefix(int prefix, const char *data, int size,
+static void copy_file_with_prefix(FILE *file,
+                                 int prefix, const char *data, int size,
                                  const char *set, const char *reset)
 {
        int ch, nl_just_seen = 1;
        while (0 < size--) {
                ch = *data++;
                if (nl_just_seen) {
-                       fputs(set, stdout);
-                       putchar(prefix);
+                       fputs(set, file);
+                       putc(prefix, file);
                }
                if (ch == '\n') {
                        nl_just_seen = 1;
-                       fputs(reset, stdout);
+                       fputs(reset, file);
                } else
                        nl_just_seen = 0;
-               putchar(ch);
+               putc(ch, file);
        }
        if (!nl_just_seen)
-               printf("%s\n\\ No newline at end of file\n", reset);
+               fprintf(file, "%s\n\\ No newline at end of file\n", reset);
 }
 
 static void emit_rewrite_diff(const char *name_a,
@@ -322,17 +323,18 @@ static void emit_rewrite_diff(const char *name_a,
        diff_populate_filespec(two, 0);
        lc_a = count_lines(one->data, one->size);
        lc_b = count_lines(two->data, two->size);
-       printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
-              metainfo, a_name.buf, name_a_tab, reset,
-              metainfo, b_name.buf, name_b_tab, reset, fraginfo);
-       print_line_count(lc_a);
-       printf(" +");
-       print_line_count(lc_b);
-       printf(" @@%s\n", reset);
+       fprintf(o->file,
+               "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
+               metainfo, a_name.buf, name_a_tab, reset,
+               metainfo, b_name.buf, name_b_tab, reset, fraginfo);
+       print_line_count(o->file, lc_a);
+       fprintf(o->file, " +");
+       print_line_count(o->file, lc_b);
+       fprintf(o->file, " @@%s\n", reset);
        if (lc_a)
-               copy_file_with_prefix('-', one->data, one->size, old, reset);
+               copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset);
        if (lc_b)
-               copy_file_with_prefix('+', two->data, two->size, new, reset);
+               copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset);
 }
 
 static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@ -372,9 +374,10 @@ static void diff_words_append(char *line, unsigned long len,
 struct diff_words_data {
        struct xdiff_emit_state xm;
        struct diff_words_buffer minus, plus;
+       FILE *file;
 };
 
-static void print_word(struct diff_words_buffer *buffer, int len, int color,
+static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color,
                int suppress_newline)
 {
        const char *ptr;
@@ -391,15 +394,15 @@ static void print_word(struct diff_words_buffer *buffer, int len, int color,
                len--;
        }
 
-       fputs(diff_get_color(1, color), stdout);
-       fwrite(ptr, len, 1, stdout);
-       fputs(diff_get_color(1, DIFF_RESET), stdout);
+       fputs(diff_get_color(1, color), file);
+       fwrite(ptr, len, 1, file);
+       fputs(diff_get_color(1, DIFF_RESET), file);
 
        if (eol) {
                if (suppress_newline)
                        buffer->suppressed_newline = 1;
                else
-                       putchar('\n');
+                       putc('\n', file);
        }
 }
 
@@ -409,20 +412,23 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 
        if (diff_words->minus.suppressed_newline) {
                if (line[0] != '+')
-                       putchar('\n');
+                       putc('\n', diff_words->file);
                diff_words->minus.suppressed_newline = 0;
        }
 
        len--;
        switch (line[0]) {
                case '-':
-                       print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
+                       print_word(diff_words->file,
+                                  &diff_words->minus, len, DIFF_FILE_OLD, 1);
                        break;
                case '+':
-                       print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
+                       print_word(diff_words->file,
+                                  &diff_words->plus, len, DIFF_FILE_NEW, 0);
                        break;
                case ' ':
-                       print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
+                       print_word(diff_words->file,
+                                  &diff_words->plus, len, DIFF_PLAIN, 0);
                        diff_words->minus.current += len;
                        break;
        }
@@ -466,7 +472,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
 
        if (diff_words->minus.suppressed_newline) {
-               putchar('\n');
+               putc('\n', diff_words->file);
                diff_words->minus.suppressed_newline = 0;
        }
 }
@@ -481,6 +487,7 @@ struct emit_callback {
        const char **label_path;
        struct diff_words_data *diff_words;
        int *found_changesp;
+       FILE *file;
 };
 
 static void free_diff_words_data(struct emit_callback *ecbdata)
@@ -505,11 +512,11 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
        return "";
 }
 
-static void emit_line(const char *set, const char *reset, const char *line, int len)
+static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
 {
-       fputs(set, stdout);
-       fwrite(line, len, 1, stdout);
-       fputs(reset, stdout);
+       fputs(set, file);
+       fwrite(line, len, 1, file);
+       fputs(reset, file);
 }
 
 static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
@@ -518,13 +525,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
        const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 
        if (!*ws)
-               emit_line(set, reset, line, len);
+               emit_line(ecbdata->file, set, reset, line, len);
        else {
                /* Emit just the prefix, then the rest. */
-               emit_line(set, reset, line, ecbdata->nparents);
+               emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
                (void)check_and_emit_line(line + ecbdata->nparents,
                    len - ecbdata->nparents, ecbdata->ws_rule,
-                   stdout, set, reset, ws);
+                   ecbdata->file, set, reset, ws);
        }
 }
 
@@ -563,10 +570,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
                name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-               printf("%s--- %s%s%s\n",
-                      meta, ecbdata->label_path[0], reset, name_a_tab);
-               printf("%s+++ %s%s%s\n",
-                      meta, ecbdata->label_path[1], reset, name_b_tab);
+               fprintf(ecbdata->file, "%s--- %s%s%s\n",
+                       meta, ecbdata->label_path[0], reset, name_a_tab);
+               fprintf(ecbdata->file, "%s+++ %s%s%s\n",
+                       meta, ecbdata->label_path[1], reset, name_b_tab);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
 
@@ -578,15 +585,16 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        if (2 <= i && i < len && line[i] == ' ') {
                ecbdata->nparents = i - 1;
                len = sane_truncate_line(ecbdata, line, len);
-               emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
+               emit_line(ecbdata->file,
+                         diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
                          reset, line, len);
                if (line[len-1] != '\n')
-                       putchar('\n');
+                       putc('\n', ecbdata->file);
                return;
        }
 
        if (len < ecbdata->nparents) {
-               emit_line(reset, reset, line, len);
+               emit_line(ecbdata->file, reset, reset, line, len);
                return;
        }
 
@@ -609,7 +617,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                        diff_words_show(ecbdata->diff_words);
                line++;
                len--;
-               emit_line(plain, reset, line, len);
+               emit_line(ecbdata->file, plain, reset, line, len);
                return;
        }
        for (i = 0; i < ecbdata->nparents && len; i++) {
@@ -620,7 +628,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        }
 
        if (color != DIFF_FILE_NEW) {
-               emit_line(diff_get_color(ecbdata->color_diff, color),
+               emit_line(ecbdata->file,
+                         diff_get_color(ecbdata->color_diff, color),
                          reset, line, len);
                return;
        }
@@ -759,20 +768,21 @@ static int scale_linear(int it, int width, int max_change)
        return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
 }
 
-static void show_name(const char *prefix, const char *name, int len,
+static void show_name(FILE *file,
+                     const char *prefix, const char *name, int len,
                      const char *reset, const char *set)
 {
-       printf(" %s%s%-*s%s |", set, prefix, len, name, reset);
+       fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset);
 }
 
-static void show_graph(char ch, int cnt, const char *set, const char *reset)
+static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset)
 {
        if (cnt <= 0)
                return;
-       printf("%s", set);
+       fprintf(file, "%s", set);
        while (cnt--)
-               putchar(ch);
-       printf("%s", reset);
+               putc(ch, file);
+       fprintf(file, "%s", reset);
 }
 
 static void fill_print_name(struct diffstat_file *file)
@@ -877,18 +887,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
                }
 
                if (data->files[i]->is_binary) {
-                       show_name(prefix, name, len, reset, set);
-                       printf("  Bin ");
-                       printf("%s%d%s", del_c, deleted, reset);
-                       printf(" -> ");
-                       printf("%s%d%s", add_c, added, reset);
-                       printf(" bytes");
-                       printf("\n");
+                       show_name(options->file, prefix, name, len, reset, set);
+                       fprintf(options->file, "  Bin ");
+                       fprintf(options->file, "%s%d%s", del_c, deleted, reset);
+                       fprintf(options->file, " -> ");
+                       fprintf(options->file, "%s%d%s", add_c, added, reset);
+                       fprintf(options->file, " bytes");
+                       fprintf(options->file, "\n");
                        continue;
                }
                else if (data->files[i]->is_unmerged) {
-                       show_name(prefix, name, len, reset, set);
-                       printf("  Unmerged\n");
+                       show_name(options->file, prefix, name, len, reset, set);
+                       fprintf(options->file, "  Unmerged\n");
                        continue;
                }
                else if (!data->files[i]->is_renamed &&
@@ -911,17 +921,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
                        del = scale_linear(del, width, max_change);
                        total = add + del;
                }
-               show_name(prefix, name, len, reset, set);
-               printf("%5d ", added + deleted);
-               show_graph('+', add, add_c, reset);
-               show_graph('-', del, del_c, reset);
-               putchar('\n');
-       }
-       printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
+               show_name(options->file, prefix, name, len, reset, set);
+               fprintf(options->file, "%5d ", added + deleted);
+               show_graph(options->file, '+', add, add_c, reset);
+               show_graph(options->file, '-', del, del_c, reset);
+               fprintf(options->file, "\n");
+       }
+       fprintf(options->file,
+              "%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
               set, total_files, adds, dels, reset);
 }
 
-static void show_shortstats(struct diffstat_t* data)
+static void show_shortstats(struct diffstat_t* data, struct diff_options *options)
 {
        int i, adds = 0, dels = 0, total_files = data->nr;
 
@@ -942,7 +953,7 @@ static void show_shortstats(struct diffstat_t* data)
                        }
                }
        }
-       printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+       fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
               total_files, adds, dels);
 }
 
@@ -957,24 +968,25 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
                struct diffstat_file *file = data->files[i];
 
                if (file->is_binary)
-                       printf("-\t-\t");
+                       fprintf(options->file, "-\t-\t");
                else
-                       printf("%d\t%d\t", file->added, file->deleted);
+                       fprintf(options->file,
+                               "%d\t%d\t", file->added, file->deleted);
                if (options->line_termination) {
                        fill_print_name(file);
                        if (!file->is_renamed)
-                               write_name_quoted(file->name, stdout,
+                               write_name_quoted(file->name, options->file,
                                                  options->line_termination);
                        else {
-                               fputs(file->print_name, stdout);
-                               putchar(options->line_termination);
+                               fputs(file->print_name, options->file);
+                               putc(options->line_termination, options->file);
                        }
                } else {
                        if (file->is_renamed) {
-                               putchar('\0');
-                               write_name_quoted(file->from_name, stdout, '\0');
+                               putc('\0', options->file);
+                               write_name_quoted(file->from_name, options->file, '\0');
                        }
-                       write_name_quoted(file->name, stdout, '\0');
+                       write_name_quoted(file->name, options->file, '\0');
                }
        }
 }
@@ -984,7 +996,7 @@ struct diffstat_dir {
        int nr, percent, cumulative;
 };
 
-static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
+static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
 {
        unsigned long this_dir = 0;
        unsigned int sources = 0;
@@ -1002,7 +1014,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons
                slash = strchr(f->name + baselen, '/');
                if (slash) {
                        int newbaselen = slash + 1 - f->name;
-                       this = gather_dirstat(dir, changed, f->name, newbaselen);
+                       this = gather_dirstat(file, dir, changed, f->name, newbaselen);
                        sources++;
                } else {
                        if (f->is_unmerged || f->is_binary)
@@ -1027,7 +1039,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons
                if (permille) {
                        int percent = permille / 10;
                        if (percent >= dir->percent) {
-                               printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+                               fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
                                if (!dir->cumulative)
                                        return 0;
                        }
@@ -1060,7 +1072,7 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
        dir.nr = data->nr;
        dir.percent = options->dirstat_percent;
        dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
-       gather_dirstat(&dir, changed, "", 0);
+       gather_dirstat(options->file, &dir, changed, "", 0);
 }
 
 static void free_diffstat_info(struct diffstat_t *diffstat)
@@ -1083,6 +1095,7 @@ struct checkdiff_t {
        int lineno, color_diff;
        unsigned ws_rule;
        unsigned status;
+       FILE *file;
 };
 
 static void checkdiff_consume(void *priv, char *line, unsigned long len)
@@ -1100,11 +1113,11 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
                if (!data->status)
                        return;
                err = whitespace_error_string(data->status);
-               printf("%s:%d: %s.\n", data->filename, data->lineno, err);
+               fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err);
                free(err);
-               emit_line(set, reset, line, 1);
+               emit_line(data->file, set, reset, line, 1);
                (void)check_and_emit_line(line + 1, len - 1, data->ws_rule,
-                   stdout, set, reset, ws);
+                   data->file, set, reset, ws);
        } else if (line[0] == ' ')
                data->lineno++;
        else if (line[0] == '@') {
@@ -1140,7 +1153,7 @@ static unsigned char *deflate_it(char *data,
        return deflated;
 }
 
-static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
 {
        void *cp;
        void *delta;
@@ -1169,13 +1182,13 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
        }
 
        if (delta && delta_size < deflate_size) {
-               printf("delta %lu\n", orig_size);
+               fprintf(file, "delta %lu\n", orig_size);
                free(deflated);
                data = delta;
                data_size = delta_size;
        }
        else {
-               printf("literal %lu\n", two->size);
+               fprintf(file, "literal %lu\n", two->size);
                free(delta);
                data = deflated;
                data_size = deflate_size;
@@ -1193,17 +1206,18 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
                        line[0] = bytes - 26 + 'a' - 1;
                encode_85(line + 1, cp, bytes);
                cp = (char *) cp + bytes;
-               puts(line);
+               fputs(line, file);
+               fputc('\n', file);
        }
-       printf("\n");
+       fprintf(file, "\n");
        free(data);
 }
 
-static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
 {
-       printf("GIT binary patch\n");
-       emit_binary_diff_body(one, two);
-       emit_binary_diff_body(two, one);
+       fprintf(file, "GIT binary patch\n");
+       emit_binary_diff_body(file, one, two);
+       emit_binary_diff_body(file, two, one);
 }
 
 static void setup_diff_attr_check(struct git_attr_check *check)
@@ -1334,25 +1348,25 @@ static void builtin_diff(const char *name_a,
        b_two = quote_two(o->b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
-       printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+       fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
        if (lbl[0][0] == '/') {
                /* /dev/null */
-               printf("%snew file mode %06o%s\n", set, two->mode, reset);
+               fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset);
                if (xfrm_msg && xfrm_msg[0])
-                       printf("%s%s%s\n", set, xfrm_msg, reset);
+                       fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
        }
        else if (lbl[1][0] == '/') {
-               printf("%sdeleted file mode %06o%s\n", set, one->mode, reset);
+               fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
                if (xfrm_msg && xfrm_msg[0])
-                       printf("%s%s%s\n", set, xfrm_msg, reset);
+                       fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
        }
        else {
                if (one->mode != two->mode) {
-                       printf("%sold mode %06o%s\n", set, one->mode, reset);
-                       printf("%snew mode %06o%s\n", set, two->mode, reset);
+                       fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset);
+                       fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset);
                }
                if (xfrm_msg && xfrm_msg[0])
-                       printf("%s%s%s\n", set, xfrm_msg, reset);
+                       fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
                /*
                 * we do not run diff between different kind
                 * of objects.
@@ -1376,10 +1390,10 @@ static void builtin_diff(const char *name_a,
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
                        goto free_ab_and_return;
                if (DIFF_OPT_TST(o, BINARY))
-                       emit_binary_diff(&mf1, &mf2);
+                       emit_binary_diff(o->file, &mf1, &mf2);
                else
-                       printf("Binary files %s and %s differ\n",
-                              lbl[0], lbl[1]);
+                       fprintf(o->file, "Binary files %s and %s differ\n",
+                               lbl[0], lbl[1]);
                o->found_changes = 1;
        }
        else {
@@ -1401,6 +1415,7 @@ static void builtin_diff(const char *name_a,
                ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
                ecbdata.found_changesp = &o->found_changes;
                ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+               ecbdata.file = o->file;
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1415,9 +1430,11 @@ static void builtin_diff(const char *name_a,
                ecb.outf = xdiff_outf;
                ecb.priv = &ecbdata;
                ecbdata.xm.consume = fn_out_consume;
-               if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
+               if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
                        ecbdata.diff_words =
                                xcalloc(1, sizeof(struct diff_words_data));
+                       ecbdata.diff_words->file = o->file;
+               }
                xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        free_diff_words_data(&ecbdata);
@@ -1496,6 +1513,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
        data.lineno = 0;
        data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
        data.ws_rule = whitespace_rule(attr_path);
+       data.file = o->file;
 
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
@@ -1966,7 +1984,7 @@ static void run_diff_cmd(const char *pgm,
                builtin_diff(name, other ? other : name,
                             one, two, xfrm_msg, o, complete_rewrite);
        else
-               printf("* Unmerged path %s\n", name);
+               fprintf(o->file, "* Unmerged path %s\n", name);
 }
 
 static void diff_fill_sha1_info(struct diff_filespec *one)
@@ -2157,6 +2175,9 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
 void diff_setup(struct diff_options *options)
 {
        memset(options, 0, sizeof(*options));
+
+       options->file = stdout;
+
        options->line_termination = '\n';
        options->break_opt = -1;
        options->rename_limit = -1;
@@ -2470,7 +2491,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->b_prefix = arg + 13;
        else if (!strcmp(arg, "--no-prefix"))
                options->a_prefix = options->b_prefix = "";
-       else
+       else if (!prefixcmp(arg, "--output=")) {
+               options->file = fopen(arg + strlen("--output="), "w");
+               options->close_file = 1;
+       } else
                return 0;
        return 1;
 }
@@ -2599,15 +2623,15 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
        int inter_name_termination = line_termination ? '\t' : '\0';
 
        if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
-               printf(":%06o %06o %s ", p->one->mode, p->two->mode,
-                      diff_unique_abbrev(p->one->sha1, opt->abbrev));
-               printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
+               fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
+                       diff_unique_abbrev(p->one->sha1, opt->abbrev));
+               fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
        }
        if (p->score) {
-               printf("%c%03d%c", p->status, similarity_index(p),
-                          inter_name_termination);
+               fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
+                       inter_name_termination);
        } else {
-               printf("%c%c", p->status, inter_name_termination);
+               fprintf(opt->file, "%c%c", p->status, inter_name_termination);
        }
 
        if (p->status == DIFF_STATUS_COPIED ||
@@ -2616,14 +2640,14 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
                name_a = p->one->path;
                name_b = p->two->path;
                strip_prefix(opt->prefix_length, &name_a, &name_b);
-               write_name_quoted(name_a, stdout, inter_name_termination);
-               write_name_quoted(name_b, stdout, line_termination);
+               write_name_quoted(name_a, opt->file, inter_name_termination);
+               write_name_quoted(name_b, opt->file, line_termination);
        } else {
                const char *name_a, *name_b;
                name_a = p->one->mode ? p->one->path : p->two->path;
                name_b = NULL;
                strip_prefix(opt->prefix_length, &name_a, &name_b);
-               write_name_quoted(name_a, stdout, line_termination);
+               write_name_quoted(name_a, opt->file, line_termination);
        }
 }
 
@@ -2825,62 +2849,62 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
                name_a = p->two->path;
                name_b = NULL;
                strip_prefix(opt->prefix_length, &name_a, &name_b);
-               write_name_quoted(name_a, stdout, opt->line_termination);
+               write_name_quoted(name_a, opt->file, opt->line_termination);
        }
 }
 
-static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
+static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_filespec *fs)
 {
        if (fs->mode)
-               printf(" %s mode %06o ", newdelete, fs->mode);
+               fprintf(file, " %s mode %06o ", newdelete, fs->mode);
        else
-               printf(" %s ", newdelete);
-       write_name_quoted(fs->path, stdout, '\n');
+               fprintf(file, " %s ", newdelete);
+       write_name_quoted(fs->path, file, '\n');
 }
 
 
-static void show_mode_change(struct diff_filepair *p, int show_name)
+static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name)
 {
        if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
-               printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode,
+               fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode,
                        show_name ? ' ' : '\n');
                if (show_name) {
-                       write_name_quoted(p->two->path, stdout, '\n');
+                       write_name_quoted(p->two->path, file, '\n');
                }
        }
 }
 
-static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
+static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p)
 {
        char *names = pprint_rename(p->one->path, p->two->path);
 
-       printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
+       fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
        free(names);
-       show_mode_change(p, 0);
+       show_mode_change(file, p, 0);
 }
 
-static void diff_summary(struct diff_filepair *p)
+static void diff_summary(FILE *file, struct diff_filepair *p)
 {
        switch(p->status) {
        case DIFF_STATUS_DELETED:
-               show_file_mode_name("delete", p->one);
+               show_file_mode_name(file, "delete", p->one);
                break;
        case DIFF_STATUS_ADDED:
-               show_file_mode_name("create", p->two);
+               show_file_mode_name(file, "create", p->two);
                break;
        case DIFF_STATUS_COPIED:
-               show_rename_copy("copy", p);
+               show_rename_copy(file, "copy", p);
                break;
        case DIFF_STATUS_RENAMED:
-               show_rename_copy("rename", p);
+               show_rename_copy(file, "rename", p);
                break;
        default:
                if (p->score) {
-                       fputs(" rewrite ", stdout);
-                       write_name_quoted(p->two->path, stdout, ' ');
-                       printf("(%d%%)\n", similarity_index(p));
+                       fputs(" rewrite ", file);
+                       write_name_quoted(p->two->path, file, ' ');
+                       fprintf(file, "(%d%%)\n", similarity_index(p));
                }
-               show_mode_change(p, !p->score);
+               show_mode_change(file, p, !p->score);
                break;
        }
 }
@@ -3088,14 +3112,14 @@ void diff_flush(struct diff_options *options)
                if (output_format & DIFF_FORMAT_DIFFSTAT)
                        show_stats(&diffstat, options);
                if (output_format & DIFF_FORMAT_SHORTSTAT)
-                       show_shortstats(&diffstat);
+                       show_shortstats(&diffstat, options);
                free_diffstat_info(&diffstat);
                separator++;
        }
 
        if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
                for (i = 0; i < q->nr; i++)
-                       diff_summary(q->queue[i]);
+                       diff_summary(options->file, q->queue[i]);
                separator++;
        }
 
@@ -3103,9 +3127,9 @@ void diff_flush(struct diff_options *options)
                if (separator) {
                        if (options->stat_sep) {
                                /* attach patch instead of inline */
-                               fputs(options->stat_sep, stdout);
+                               fputs(options->stat_sep, options->file);
                        } else {
-                               putchar(options->line_termination);
+                               putc(options->line_termination, options->file);
                        }
                }
 
@@ -3125,6 +3149,8 @@ void diff_flush(struct diff_options *options)
        free(q->queue);
        q->queue = NULL;
        q->nr = q->alloc = 0;
+       if (options->close_file)
+               fclose(options->file);
 }
 
 static void diffcore_apply_filter(const char *filter)
diff --git a/diff.h b/diff.h
index 9a652e7f38e3c1aeb7c7eaf840dea7bcea285311..f2c77391a965bcc731de6478a6553cdc652319ec 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -98,6 +98,9 @@ struct diff_options {
        /* this is set by diffcore for DIFF_FORMAT_PATCH */
        int found_changes;
 
+       FILE *file;
+       int close_file;
+
        int nr_paths;
        const char **paths;
        int *pathlens;
index 7f197d5e36ae977e21251dbb45fa72973ea7830f..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;
 }
 
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
index 6883d1bd68d158290acb18ae9b4e8baba7525201..797e3178ae279f444d2efa7e3758652ad0898dd7 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -155,8 +155,6 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
        o_mode = 0;
        o_name = NULL;
        o_sha1 = NULL;
-       if (!desc.size)
-               return error_func(&item->object, FSCK_ERROR, "empty tree");
 
        while (desc.size) {
                unsigned mode;
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..a18235e6d053322a85805d1b07b631c6739deb93 100644 (file)
@@ -68,6 +68,7 @@
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <utime.h>
 #ifndef NO_SYS_SELECT_H
 #include <sys/select.h>
 #endif
@@ -209,6 +210,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 +447,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 47f116f37ee1030ac0cab1f91feec04d673d94bd..95c5eec51ecc6ab6f142cef51eb4bd3b5842debb 100755 (executable)
@@ -735,7 +735,7 @@ sub commit {
                next unless $logmsg =~ $rx && $1;
                my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
                if (my $sha1 = get_headref("$remote/$mparent")) {
-                       push @commit_args, '-p', $mparent;
+                       push @commit_args, '-p', "$remote/$mparent";
                        print "Merge parent branch: $mparent\n" if $opt_v;
                }
        }
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..b19fb2d64e777102fed94d485f869ad6695bbb62 100644 (file)
@@ -221,7 +221,7 @@ ifdef NO_MSGFMT
        MSGFMT ?= $(TCL_PATH) po/po2msg.sh
 else
        MSGFMT ?= msgfmt
-       ifeq ($(shell $(MSGFMT) >/dev/null 2>&1 || echo $$?),127)
+       ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
                MSGFMT := $(TCL_PATH) po/po2msg.sh
        endif
 endif
index 238a2393ff7811675003e0e846f3d9409fb04e0c..3a58cd2c6b49b8734234c6fb5957c035df9787d4 100755 (executable)
@@ -611,6 +611,7 @@ set default_config(gui.matchtrackingbranch) false
 set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
 set default_config(gui.diffcontext) 5
+set default_config(gui.commitmsgwidth) 75
 set default_config(gui.newbranchtemplate) {}
 set default_config(gui.spellingdictionary) {}
 set default_config(gui.fontui) [font configure font_ui]
@@ -2289,8 +2290,9 @@ pack .vpane -anchor n -side top -fill both -expand 1
 #
 frame .vpane.files.index -height 100 -width 200
 label .vpane.files.index.title -text [mc "Staged Changes (Will Commit)"] \
-       -background lightgreen
-text $ui_index -background white -borderwidth 0 \
+       -background lightgreen -foreground black
+text $ui_index -background white -foreground black \
+       -borderwidth 0 \
        -width 20 -height 10 \
        -wrap none \
        -cursor $cursor_ptr \
@@ -2308,8 +2310,9 @@ pack $ui_index -side left -fill both -expand 1
 #
 frame .vpane.files.workdir -height 100 -width 200
 label .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
-       -background lightsalmon
-text $ui_workdir -background white -borderwidth 0 \
+       -background lightsalmon -foreground black
+text $ui_workdir -background white -foreground black \
+       -borderwidth 0 \
        -width 20 -height 10 \
        -wrap none \
        -cursor $cursor_ptr \
@@ -2416,12 +2419,13 @@ pack $ui_coml -side left -fill x
 pack .vpane.lower.commarea.buffer.header.amend -side right
 pack .vpane.lower.commarea.buffer.header.new -side right
 
-text $ui_comm -background white -borderwidth 1 \
+text $ui_comm -background white -foreground black \
+       -borderwidth 1 \
        -undo true \
        -maxundo 20 \
        -autoseparators true \
        -relief sunken \
-       -width 75 -height 9 -wrap none \
+       -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
        -font font_diff \
        -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
 scrollbar .vpane.lower.commarea.buffer.sby \
@@ -2493,15 +2497,18 @@ trace add variable current_diff_path write trace_current_diff_path
 frame .vpane.lower.diff.header -background gold
 label .vpane.lower.diff.header.status \
        -background gold \
+       -foreground black \
        -width $max_status_desc \
        -anchor w \
        -justify left
 label .vpane.lower.diff.header.file \
        -background gold \
+       -foreground black \
        -anchor w \
        -justify left
 label .vpane.lower.diff.header.path \
        -background gold \
+       -foreground black \
        -anchor w \
        -justify left
 pack .vpane.lower.diff.header.status -side left
@@ -2525,7 +2532,8 @@ bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
 #
 frame .vpane.lower.diff.body
 set ui_diff .vpane.lower.diff.body.t
-text $ui_diff -background white -borderwidth 0 \
+text $ui_diff -background white -foreground black \
+       -borderwidth 0 \
        -width 80 -height 15 -wrap none \
        -font font_diff \
        -xscrollcommand {.vpane.lower.diff.body.sbx set} \
index 00ecf21333c976d4c6002cd66a055803362f3523..92fac1bad402a0d7772af0b8817217555b61b6c7 100644 (file)
@@ -80,6 +80,7 @@ constructor new {i_commit i_path} {
        label $w.header.commit_l \
                -text [mc "Commit:"] \
                -background gold \
+               -foreground black \
                -anchor w \
                -justify left
        set w_back $w.header.commit_b
@@ -89,6 +90,7 @@ constructor new {i_commit i_path} {
                -relief flat \
                -state disabled \
                -background gold \
+               -foreground black \
                -activebackground gold
        bind $w_back <Button-1> "
                if {\[$w_back cget -state\] eq {normal}} {
@@ -98,16 +100,19 @@ constructor new {i_commit i_path} {
        label $w.header.commit \
                -textvariable @commit \
                -background gold \
+               -foreground black \
                -anchor w \
                -justify left
        label $w.header.path_l \
                -text [mc "File:"] \
                -background gold \
+               -foreground black \
                -anchor w \
                -justify left
        set w_path $w.header.path
        label $w_path \
                -background gold \
+               -foreground black \
                -anchor w \
                -justify left
        pack $w.header.commit_l -side left
@@ -135,7 +140,9 @@ constructor new {i_commit i_path} {
                -takefocus 0 \
                -highlightthickness 0 \
                -padx 0 -pady 0 \
-               -background white -borderwidth 0 \
+               -background white \
+               -foreground black \
+               -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 40 \
@@ -148,7 +155,9 @@ constructor new {i_commit i_path} {
                -takefocus 0 \
                -highlightthickness 0 \
                -padx 0 -pady 0 \
-               -background white -borderwidth 0 \
+               -background white \
+               -foreground black \
+               -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 40 \
@@ -166,7 +175,9 @@ constructor new {i_commit i_path} {
                -takefocus 0 \
                -highlightthickness 0 \
                -padx 0 -pady 0 \
-               -background white -borderwidth 0 \
+               -background white \
+               -foreground black \
+               -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 40 \
@@ -184,7 +195,9 @@ constructor new {i_commit i_path} {
                -takefocus 0 \
                -highlightthickness 0 \
                -padx 0 -pady 0 \
-               -background white -borderwidth 0 \
+               -background white \
+               -foreground black \
+               -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 40 \
@@ -213,7 +226,9 @@ constructor new {i_commit i_path} {
 
        set w_cviewer $w.file_pane.cm.t
        text $w_cviewer \
-               -background white -borderwidth 0 \
+               -background white \
+               -foreground black \
+               -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 10 \
index 53d5a628165a29c592ab14c234a000a56d7c6a12..ab470d12648c1dd3560b411e70ea210cc4381e3e 100644 (file)
@@ -39,7 +39,8 @@ constructor new {commit {path {}}} {
 
        frame $w.list
        set w_list $w.list.l
-       text $w_list -background white -borderwidth 0 \
+       text $w_list -background white -foreground black \
+               -borderwidth 0 \
                -cursor $cursor_ptr \
                -state disabled \
                -wrap none \
index 0c4051b3752b4f88e50e1f4f22189d826234668a..56443b042c62bc10765aaf6484ad1077a843cb30 100644 (file)
@@ -55,6 +55,7 @@ constructor pick {path title a_family a_size} {
        set w_family $w.inner.family.v
        text $w_family \
                -background white \
+               -foreground black \
                -borderwidth 1 \
                -relief sunken \
                -cursor $::cursor_ptr \
@@ -92,6 +93,7 @@ constructor pick {path title a_family a_size} {
        set w_example $w.example.t
        text $w_example \
                -background white \
+               -foreground black \
                -borderwidth 1 \
                -relief sunken \
                -height 3 \
index 5597188d803a1c8217011412a39c14fbbeaf0b3a..c112464ec367a2db707a3f28ff6c588aefe7985f 100644 (file)
@@ -46,7 +46,9 @@ method _init {} {
                -justify left \
                -font font_uibold
        text $w_t \
-               -background white -borderwidth 1 \
+               -background white \
+               -foreground black \
+               -borderwidth 1 \
                -relief sunken \
                -width 80 -height 10 \
                -wrap none \
@@ -180,7 +182,8 @@ method done {ok} {
        if {$ok} {
                if {[winfo exists $w.m.s]} {
                        bind $w.m.s <Destroy> [list delete_this $this]
-                       $w.m.s conf -background green -text [mc "Success"]
+                       $w.m.s conf -background green -foreground black \
+                               -text [mc "Success"]
                        if {$is_toplevel} {
                                $w.ok conf -state normal
                                focus $w.ok
@@ -193,7 +196,8 @@ method done {ok} {
                        _init $this
                }
                bind $w.m.s <Destroy> [list delete_this $this]
-               $w.m.s conf -background red -text [mc "Error: Command Failed"]
+               $w.m.s conf -background red -foreground black \
+                       -text [mc "Error: Command Failed"]
                if {$is_toplevel} {
                        $w.ok conf -state normal
                        focus $w.ok
index 8c27678e3a45c011cfc6a21f5d53b0d87c66b4a6..75650157e551e34dab650d89f3fa6d25afc91d6a 100644 (file)
@@ -80,7 +80,9 @@ proc hook_failed_popup {hook msg {is_fatal 1}} {
                -justify left \
                -font font_uibold
        text $w.m.t \
-               -background white -borderwidth 1 \
+               -background white \
+               -foreground black \
+               -borderwidth 1 \
                -relief sunken \
                -width 80 -height 10 \
                -font font_diff \
index ea80df009226d0ff14e8fe423abfe3d7a94ef56b..9270512582034a6629c4ff15abb1f30889f76903 100644 (file)
@@ -124,6 +124,7 @@ proc do_options {} {
                {b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}}
                {b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
                {i-0..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
+               {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}}
                {t gui.newbranchtemplate {mc "New Branch Name Template"}}
                } {
                set type [lindex $option 0]
index 621c9479b2b90c13119985df889af70b206e3cbd..f8697216f71a56fcda47056fe5b5c287839ef7f0 100644 (file)
@@ -3,46 +3,63 @@
 # This file is distributed under the same license as the git-gui package.
 # Xudong Guan <xudong.guan@gmail.com>, 2007.
 #
+# Please use the following translation throughout the file for consistence:
+#
+#      repository      版本库
+#      commit          提交
+#      revision        版本
+#      branch          分支
+#      tag             标签
+#      annotation      标注
+#      merge           合并
+#      fast forward    快速合并(??)
+#      stage           缓存 (译自 index/cache)
+#      amend           修正
+#      reset           复位
+#
+# 2008-01-06 Eric Miao <eric.y.miao@gmail.com>
+# FIXME: checkout 的标准翻译
+#
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-10-10 04:04-0400\n"
+"POT-Creation-Date: 2007-11-24 10:36+0100\n"
 "PO-Revision-Date: 2007-07-21 01:23-0700\n"
-"Last-Translator: Xudong Guan <xudong.guan@gmail.com>\n"
+"Last-Translator: Eric Miao <eric.y.miao@gmail.com>\n"
 "Language-Team: Chinese\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
-#: git-gui.sh:763
+#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
+#: git-gui.sh:733
 msgid "git-gui: fatal error"
-msgstr ""
+msgstr "git-gui: 致命错误"
 
-#: git-gui.sh:595
+#: git-gui.sh:565
 #, tcl-format
 msgid "Invalid font specified in %s:"
-msgstr ""
+msgstr "%s 中指定的字体无效:"
 
-#: git-gui.sh:620
+#: git-gui.sh:590
 msgid "Main Font"
-msgstr ""
+msgstr "主要字体"
 
-#: git-gui.sh:621
+#: git-gui.sh:591
 msgid "Diff/Console Font"
-msgstr ""
+msgstr "Diff/控制终端字体"
 
-#: git-gui.sh:635
+#: git-gui.sh:605
 msgid "Cannot find git in PATH."
-msgstr ""
+msgstr "PATH 中没有找到 git"
 
-#: git-gui.sh:662
+#: git-gui.sh:632
 msgid "Cannot parse Git version string:"
-msgstr ""
+msgstr "无法解析 Git 的版本信息:"
 
-#: git-gui.sh:680
+#: git-gui.sh:650
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -53,388 +70,386 @@ msgid ""
 "\n"
 "Assume '%s' is version 1.5.0?\n"
 msgstr ""
+"无法确定 Git 的版本.\n"
+"\n"
+"%s 声明其版本为 '%s'.\n"
+"\n"
+"而 %s 需要 1.5.0 或这以后的 Git 版本.\n"
+"\n"
+"是否假定 '%s' 为版本 1.5.0?\n"
 
-#: git-gui.sh:853
+#: git-gui.sh:888
 msgid "Git directory not found:"
-msgstr ""
+msgstr "Git 目录无法找到:"
 
-#: git-gui.sh:860
+#: git-gui.sh:895
 msgid "Cannot move to top of working directory:"
-msgstr ""
+msgstr "无法移动到工作根目录:"
 
-#: git-gui.sh:867
+#: git-gui.sh:902
 msgid "Cannot use funny .git directory:"
-msgstr ""
+msgstr "无法使用 .git 目录:"
 
-#: git-gui.sh:872
+#: git-gui.sh:907
 msgid "No working directory"
-msgstr ""
+msgstr "没有工作目录"
 
-#: git-gui.sh:1019
+#: git-gui.sh:1054
 msgid "Refreshing file status..."
-msgstr ""
+msgstr "更新文件状态..."
 
-#: git-gui.sh:1084
+#: git-gui.sh:1119
 msgid "Scanning for modified files ..."
-msgstr ""
+msgstr "扫描修改过的文件 ..."
 
-#: git-gui.sh:1259 lib/browser.tcl:245
-#, fuzzy
+#: git-gui.sh:1294 lib/browser.tcl:245
 msgid "Ready."
-msgstr "重做"
+msgstr "就绪"
 
-#: git-gui.sh:1525
+#: git-gui.sh:1560
 msgid "Unmodified"
-msgstr ""
+msgstr "未修改"
 
-#: git-gui.sh:1527
+#: git-gui.sh:1562
 msgid "Modified, not staged"
-msgstr ""
+msgstr "修改但未缓存"
 
-#: git-gui.sh:1528 git-gui.sh:1533
-#, fuzzy
+#: git-gui.sh:1563 git-gui.sh:1568
 msgid "Staged for commit"
-msgstr "ä»\8eæ\9c¬æ¬¡æ\8f\90交移é\99¤"
+msgstr "ç¼\93å­\98为æ\8f\90交"
 
-#: git-gui.sh:1529 git-gui.sh:1534
-#, fuzzy
+#: git-gui.sh:1564 git-gui.sh:1569
 msgid "Portions staged for commit"
-msgstr "ä»\8eæ\9c¬æ¬¡æ\8f\90交移é\99¤"
+msgstr "é\83¨å\88\86ç¼\93å­\98为æ\8f\90交"
 
-#: git-gui.sh:1530 git-gui.sh:1535
+#: git-gui.sh:1565 git-gui.sh:1570
 msgid "Staged for commit, missing"
-msgstr ""
+msgstr "缓存为提交, 不存在"
 
-#: git-gui.sh:1532
+#: git-gui.sh:1567
 msgid "Untracked, not staged"
-msgstr ""
+msgstr "未跟踪, 未缓存"
 
-#: git-gui.sh:1537
+#: git-gui.sh:1572
 msgid "Missing"
-msgstr ""
+msgstr "不存在"
 
-#: git-gui.sh:1538
+#: git-gui.sh:1573
 msgid "Staged for removal"
-msgstr ""
+msgstr "缓存为删除"
 
-#: git-gui.sh:1539
+#: git-gui.sh:1574
 msgid "Staged for removal, still present"
-msgstr ""
+msgstr "缓存为删除, 但仍存在"
 
-#: git-gui.sh:1541 git-gui.sh:1542 git-gui.sh:1543 git-gui.sh:1544
+#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
 msgid "Requires merge resolution"
-msgstr ""
+msgstr "需要解决合并冲突"
 
-#: git-gui.sh:1579
+#: git-gui.sh:1614
 msgid "Starting gitk... please wait..."
-msgstr ""
+msgstr "启动 gitk... 请等待..."
 
-#: git-gui.sh:1588
+#: git-gui.sh:1623
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
 "\n"
 "%s does not exist"
 msgstr ""
+"无法启动 gitk:\n"
+"\n"
+"%s 不存在"
 
-#: git-gui.sh:1788 lib/choose_repository.tcl:32
+#: git-gui.sh:1823 lib/choose_repository.tcl:35
 msgid "Repository"
-msgstr "版本"
+msgstr "版本库(repository)"
 
-#: git-gui.sh:1789
+#: git-gui.sh:1824
 msgid "Edit"
 msgstr "编辑"
 
-#: git-gui.sh:1791 lib/choose_rev.tcl:560
+#: git-gui.sh:1826 lib/choose_rev.tcl:560
 msgid "Branch"
-msgstr "分支"
+msgstr "分支(branch)"
 
-#: git-gui.sh:1794 lib/choose_rev.tcl:547
-#, fuzzy
+#: git-gui.sh:1829 lib/choose_rev.tcl:547
 msgid "Commit@@noun"
-msgstr "提交"
+msgstr "提交(commit)"
 
-#: git-gui.sh:1797 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
-msgstr "合并"
+msgstr "合并(merge)"
 
-#: git-gui.sh:1798 lib/choose_rev.tcl:556
-#, fuzzy
+#: git-gui.sh:1833 lib/choose_rev.tcl:556
 msgid "Remote"
-msgstr "改名..."
+msgstr "远端(remote)"
 
-#: git-gui.sh:1807
+#: git-gui.sh:1842
 msgid "Browse Current Branch's Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览当前分支上的文件"
 
-#: git-gui.sh:1811
-#, fuzzy
+#: git-gui.sh:1846
 msgid "Browse Branch Files..."
-msgstr "æµ\8fè§\88å½\93å\89\8då\88\86æ\94¯æ\96\87件"
+msgstr "æµ\8fè§\88å\88\86æ\94¯ä¸\8aç\9a\84æ\96\87件..."
 
-#: git-gui.sh:1816
+#: git-gui.sh:1851
 msgid "Visualize Current Branch's History"
-msgstr "调用gitk显示当前分支"
+msgstr "图示当前分支的历史"
 
-#: git-gui.sh:1820
+#: git-gui.sh:1855
 msgid "Visualize All Branch History"
-msgstr "调用gitk显示所有分支"
+msgstr "图示所有分支的历史"
 
-#: git-gui.sh:1827
-#, fuzzy, tcl-format
+#: git-gui.sh:1862
+#, tcl-format
 msgid "Browse %s's Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览 %s 上的文件"
 
-#: git-gui.sh:1829
-#, fuzzy, tcl-format
+#: git-gui.sh:1864
+#, tcl-format
 msgid "Visualize %s's History"
-msgstr "调用gitk显示所有分支"
+msgstr "图示 %s 分支的历史"
 
-#: git-gui.sh:1834 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
-msgstr "数据库统计数据"
+msgstr "数据库统计信息"
 
-#: git-gui.sh:1837 lib/database.tcl:34
+#: git-gui.sh:1872 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "压缩数据库"
 
-#: git-gui.sh:1840
+#: git-gui.sh:1875
 msgid "Verify Database"
 msgstr "验证数据库"
 
-#: git-gui.sh:1847 git-gui.sh:1851 git-gui.sh:1855 lib/shortcut.tcl:9
-#: lib/shortcut.tcl:45 lib/shortcut.tcl:84
+#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "创建桌面图标"
 
-#: git-gui.sh:1860 lib/choose_repository.tcl:36 lib/choose_repository.tcl:95
+#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
 msgid "Quit"
 msgstr "退出"
 
-#: git-gui.sh:1867
+#: git-gui.sh:1902
 msgid "Undo"
 msgstr "撤销"
 
-#: git-gui.sh:1870
+#: git-gui.sh:1905
 msgid "Redo"
 msgstr "重做"
 
-#: git-gui.sh:1874 git-gui.sh:2366
+#: git-gui.sh:1909 git-gui.sh:2403
 msgid "Cut"
 msgstr "剪切"
 
-#: git-gui.sh:1877 git-gui.sh:2369 git-gui.sh:2440 git-gui.sh:2512
+#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
 #: lib/console.tcl:67
 msgid "Copy"
 msgstr "复制"
 
-#: git-gui.sh:1880 git-gui.sh:2372
+#: git-gui.sh:1915 git-gui.sh:2409
 msgid "Paste"
 msgstr "粘贴"
 
-#: git-gui.sh:1883 git-gui.sh:2375 lib/branch_delete.tcl:26
+#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "删除"
 
-#: git-gui.sh:1887 git-gui.sh:2379 git-gui.sh:2516 lib/console.tcl:69
+#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
 msgid "Select All"
 msgstr "全选"
 
-#: git-gui.sh:1896
+#: git-gui.sh:1931
 msgid "Create..."
 msgstr "新建..."
 
-#: git-gui.sh:1902
+#: git-gui.sh:1937
 msgid "Checkout..."
-msgstr "切换..."
+msgstr "Checkout..."
 
-#: git-gui.sh:1908
+#: git-gui.sh:1943
 msgid "Rename..."
-msgstr "æ\94¹名..."
+msgstr "æ\9b´名..."
 
-#: git-gui.sh:1913 git-gui.sh:2012
+#: git-gui.sh:1948 git-gui.sh:2048
 msgid "Delete..."
 msgstr "删除..."
 
-#: git-gui.sh:1918
+#: git-gui.sh:1953
 msgid "Reset..."
-msgstr "重置所有修动..."
+msgstr "复位(Reset)..."
 
-#: git-gui.sh:1930 git-gui.sh:2313
+#: git-gui.sh:1965 git-gui.sh:2350
 msgid "New Commit"
-msgstr "新提交"
+msgstr "新提交"
 
-#: git-gui.sh:1938 git-gui.sh:2320
+#: git-gui.sh:1973 git-gui.sh:2357
 msgid "Amend Last Commit"
-msgstr "修上次提交"
+msgstr "修上次提交"
 
-#: git-gui.sh:1947 git-gui.sh:2280 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "重新扫描"
 
-#: git-gui.sh:1953
-#, fuzzy
+#: git-gui.sh:1988
 msgid "Stage To Commit"
-msgstr "ä»\8eæ\9c¬æ¬¡æ\8f\90交移é\99¤"
+msgstr "ç¼\93å­\98为æ\8f\90交"
 
-#: git-gui.sh:1958
-#, fuzzy
+#: git-gui.sh:1994
 msgid "Stage Changed Files To Commit"
-msgstr "将被提交的修改"
+msgstr "缓存修改的文件为提交"
 
-#: git-gui.sh:1964
+#: git-gui.sh:2000
 msgid "Unstage From Commit"
-msgstr "从本次提交除"
+msgstr "从本次提交除"
 
-#: git-gui.sh:1969 lib/index.tcl:352
+#: git-gui.sh:2005 lib/index.tcl:393
 msgid "Revert Changes"
-msgstr "æ\81¢å¤\8d修改"
+msgstr "æ\92¤é\94\80修改"
 
-#: git-gui.sh:1976 git-gui.sh:2292 git-gui.sh:2390
+#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
 msgid "Sign Off"
-msgstr "签名"
+msgstr "签名(Sign Off)"
 
-#: git-gui.sh:1980 git-gui.sh:2296
-#, fuzzy
+#: git-gui.sh:2016 git-gui.sh:2333
 msgid "Commit@@verb"
 msgstr "提交"
 
-#: git-gui.sh:1991
+#: git-gui.sh:2027
 msgid "Local Merge..."
 msgstr "本地合并..."
 
-#: git-gui.sh:1996
+#: git-gui.sh:2032
 msgid "Abort Merge..."
-msgstr "取消合并..."
+msgstr "中止合并..."
 
-#: git-gui.sh:2008
+#: git-gui.sh:2044
 msgid "Push..."
 msgstr "上传..."
 
-#: git-gui.sh:2019 lib/choose_repository.tcl:41
+#: git-gui.sh:2055 lib/choose_repository.tcl:40
 msgid "Apple"
 msgstr "苹果"
 
-#: git-gui.sh:2022 git-gui.sh:2044 lib/about.tcl:13
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
+#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
 #, tcl-format
 msgid "About %s"
-msgstr "关于%s"
+msgstr "关于 %s"
 
-#: git-gui.sh:2026
+#: git-gui.sh:2062
 msgid "Preferences..."
-msgstr ""
+msgstr "首选项..."
 
-#: git-gui.sh:2034 git-gui.sh:2558
+#: git-gui.sh:2070 git-gui.sh:2595
 msgid "Options..."
 msgstr "选项..."
 
-#: git-gui.sh:2040 lib/choose_repository.tcl:47
+#: git-gui.sh:2076 lib/choose_repository.tcl:46
 msgid "Help"
 msgstr "帮助"
 
-#: git-gui.sh:2081
+#: git-gui.sh:2117
 msgid "Online Documentation"
 msgstr "在线文档"
 
-#: git-gui.sh:2165
+#: git-gui.sh:2201
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr ""
+msgstr "致命错误: 无法获取路径 %s 的信息: 该文件或目录不存在"
 
-#: git-gui.sh:2198
+#: git-gui.sh:2234
 msgid "Current Branch:"
 msgstr "当前分支:"
 
-#: git-gui.sh:2219
-#, fuzzy
+#: git-gui.sh:2255
 msgid "Staged Changes (Will Commit)"
-msgstr "å°\86被æ\8f\90交ç\9a\84ä¿®æ\94¹"
+msgstr "å·²ç¼\93å­\98ç\9a\84æ\94¹å\8a¨ (å°\86被æ\8f\90交)"
 
-#: git-gui.sh:2239
+#: git-gui.sh:2274
 msgid "Unstaged Changes"
-msgstr ""
+msgstr "未缓存的改动"
 
-#: git-gui.sh:2286
+#: git-gui.sh:2323
 msgid "Stage Changed"
-msgstr ""
+msgstr "缓存改动"
 
-#: git-gui.sh:2302 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "上传"
 
-#: git-gui.sh:2332
+#: git-gui.sh:2369
 msgid "Initial Commit Message:"
-msgstr "初始提交描述:"
+msgstr "初始提交描述:"
 
-#: git-gui.sh:2333
+#: git-gui.sh:2370
 msgid "Amended Commit Message:"
-msgstr "修提交描述:"
+msgstr "修正的提交描述:"
 
-#: git-gui.sh:2334
+#: git-gui.sh:2371
 msgid "Amended Initial Commit Message:"
-msgstr "修初始提交描述:"
+msgstr "修正的初始提交描述:"
 
-#: git-gui.sh:2335
+#: git-gui.sh:2372
 msgid "Amended Merge Commit Message:"
-msgstr "修合并提交描述:"
+msgstr "修正的合并提交描述:"
 
-#: git-gui.sh:2336
+#: git-gui.sh:2373
 msgid "Merge Commit Message:"
 msgstr "合并提交描述:"
 
-#: git-gui.sh:2337
+#: git-gui.sh:2374
 msgid "Commit Message:"
 msgstr "提交描述:"
 
-#: git-gui.sh:2382 git-gui.sh:2520 lib/console.tcl:71
+#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
 msgid "Copy All"
 msgstr "全部复制"
 
-#: git-gui.sh:2406 lib/blame.tcl:104
+#: git-gui.sh:2443 lib/blame.tcl:104
 msgid "File:"
-msgstr ""
+msgstr "文件:"
 
-#: git-gui.sh:2508
+#: git-gui.sh:2545
 msgid "Refresh"
 msgstr "刷新"
 
-#: git-gui.sh:2529
+#: git-gui.sh:2566
 msgid "Apply/Reverse Hunk"
 msgstr "应用/撤消此修改块"
 
-#: git-gui.sh:2535
+#: git-gui.sh:2572
 msgid "Decrease Font Size"
 msgstr "缩小字体"
 
-#: git-gui.sh:2539
+#: git-gui.sh:2576
 msgid "Increase Font Size"
 msgstr "放大字体"
 
-#: git-gui.sh:2544
+#: git-gui.sh:2581
 msgid "Show Less Context"
-msgstr "æ\98¾ç¤ºæ\9b´å¤\9adiff上下文"
+msgstr "æ\98¾ç¤ºæ\9b´å°\91上下文"
 
-#: git-gui.sh:2551
+#: git-gui.sh:2588
 msgid "Show More Context"
-msgstr "æ\98¾ç¤ºæ\9b´å°\91diff上下文"
+msgstr "æ\98¾ç¤ºæ\9b´å¤\9a上下文"
 
-#: git-gui.sh:2565
-#, fuzzy
+#: git-gui.sh:2602
 msgid "Unstage Hunk From Commit"
-msgstr "ä»\8eæ\9c¬æ¬¡æ\8f\90交移é\99¤"
+msgstr "ä»\8eæ\8f\90交中æ\92¤é\99¤ä¿®æ\94¹å\9d\97"
 
-#: git-gui.sh:2567
-#, fuzzy
+#: git-gui.sh:2604
 msgid "Stage Hunk For Commit"
-msgstr "ä»\8eæ\9c¬æ¬¡æ\8f\90交移é\99¤"
+msgstr "ç¼\93å­\98ä¿®æ\94¹å\9d\97为æ\8f\90交"
 
-#: git-gui.sh:2586
+#: git-gui.sh:2623
 msgid "Initializing..."
-msgstr ""
+msgstr "初始化..."
 
-#: git-gui.sh:2677
+#: git-gui.sh:2718
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -444,15 +459,22 @@ msgid ""
 "by %s:\n"
 "\n"
 msgstr ""
+"可能存在环境变量的问题.\n"
+"\n"
+"由 %s 执行的 Git 子进程可能忽略下列环境变量:\n"
+"\n"
 
-#: git-gui.sh:2707
+#: git-gui.sh:2748
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
 "Tcl binary distributed by Cygwin."
 msgstr ""
+"\n"
+"这是由 Cygwin 发布的 Tcl 代码中一个\n"
+"已知问题所引起."
 
-#: git-gui.sh:2712
+#: git-gui.sh:2753
 #, tcl-format
 msgid ""
 "\n"
@@ -462,206 +484,197 @@ msgid ""
 "user.email settings into your personal\n"
 "~/.gitconfig file.\n"
 msgstr ""
+"\n"
+"\n"
+"%s 的一个很好的替代方案是将 user.name 以及\n"
+"user.email 设置放在你的个人 ~/.gitconfig 文件中.\n"
 
 #: lib/about.tcl:25
 msgid "git-gui - a graphical user interface for Git."
-msgstr ""
+msgstr "git-gui - Git 的图形化用户界面"
 
 #: lib/blame.tcl:77
 msgid "File Viewer"
-msgstr ""
+msgstr "文件查看器"
 
 #: lib/blame.tcl:81
-#, fuzzy
 msgid "Commit:"
-msgstr "提交"
+msgstr "提交:"
 
 #: lib/blame.tcl:249
-#, fuzzy
 msgid "Copy Commit"
-msgstr "提交"
+msgstr "复制提交"
 
 #: lib/blame.tcl:369
 #, tcl-format
 msgid "Reading %s..."
-msgstr ""
+msgstr "读取 %s..."
 
 #: lib/blame.tcl:473
 msgid "Loading copy/move tracking annotations..."
-msgstr ""
+msgstr "装载复制/移动跟踪标注..."
 
 #: lib/blame.tcl:493
 msgid "lines annotated"
-msgstr ""
+msgstr "标注行"
 
 #: lib/blame.tcl:674
 msgid "Loading original location annotations..."
-msgstr ""
+msgstr "装载原始位置标注..."
 
 #: lib/blame.tcl:677
 msgid "Annotation complete."
-msgstr ""
+msgstr "标注完成."
 
 #: lib/blame.tcl:731
 msgid "Loading annotation..."
-msgstr ""
+msgstr "裝載标注..."
 
 #: lib/blame.tcl:787
 msgid "Author:"
-msgstr ""
+msgstr "作者:"
 
 #: lib/blame.tcl:791
-#, fuzzy
 msgid "Committer:"
-msgstr "提交"
+msgstr "提交者:"
 
 #: lib/blame.tcl:796
 msgid "Original File:"
-msgstr ""
+msgstr "原始文件:"
 
 #: lib/blame.tcl:910
 msgid "Originally By:"
-msgstr ""
+msgstr "最初由:"
 
 #: lib/blame.tcl:916
 msgid "In File:"
-msgstr ""
+msgstr "在文件:"
 
 #: lib/blame.tcl:921
 msgid "Copied Or Moved Here By:"
-msgstr ""
+msgstr "由复制或移动至此:"
 
 #: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
-#, fuzzy
 msgid "Checkout Branch"
-msgstr "当前分支:"
+msgstr "Checkout 分支"
 
 #: lib/branch_checkout.tcl:23
-#, fuzzy
 msgid "Checkout"
-msgstr "切换..."
+msgstr "Checkout"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
 #: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
 #: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
-msgstr ""
+msgstr "取消"
 
 #: lib/branch_checkout.tcl:32 lib/browser.tcl:286
 msgid "Revision"
-msgstr ""
+msgstr "版本"
 
 #: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
-#, fuzzy
 msgid "Options"
 msgstr "选项..."
 
 #: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
 msgid "Fetch Tracking Branch"
-msgstr ""
+msgstr "获取跟踪分支"
 
 #: lib/branch_checkout.tcl:44
 msgid "Detach From Local Branch"
-msgstr ""
+msgstr "从本地分支脱离"
 
 #: lib/branch_create.tcl:22
-#, fuzzy
 msgid "Create Branch"
-msgstr "å½\93å\89\8då\88\86æ\94¯:"
+msgstr "å\88\9b建å\88\86æ\94¯"
 
 #: lib/branch_create.tcl:27
-#, fuzzy
 msgid "Create New Branch"
-msgstr "当前分支:"
+msgstr "新建分支"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:199
-#, fuzzy
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
 msgid "Create"
-msgstr "新建..."
+msgstr "新建"
 
 #: lib/branch_create.tcl:40
-#, fuzzy
 msgid "Branch Name"
-msgstr "分支"
+msgstr "分支"
 
 #: lib/branch_create.tcl:43
 msgid "Name:"
-msgstr ""
+msgstr "名字:"
 
 #: lib/branch_create.tcl:58
 msgid "Match Tracking Branch Name"
-msgstr ""
+msgstr "匹配跟踪分支名字"
 
 #: lib/branch_create.tcl:66
 msgid "Starting Revision"
-msgstr ""
+msgstr "起始版本"
 
 #: lib/branch_create.tcl:72
 msgid "Update Existing Branch:"
-msgstr ""
+msgstr "更新已有分支:"
 
 #: lib/branch_create.tcl:75
 msgid "No"
-msgstr ""
+msgstr "号码"
 
 #: lib/branch_create.tcl:80
 msgid "Fast Forward Only"
-msgstr ""
+msgstr "仅快速合并"
 
 #: lib/branch_create.tcl:85 lib/checkout_op.tcl:514
-#, fuzzy
 msgid "Reset"
-msgstr "重置所有修动..."
+msgstr "复位"
 
 #: lib/branch_create.tcl:97
 msgid "Checkout After Creation"
-msgstr ""
+msgstr "在创建后Checkout"
 
 #: lib/branch_create.tcl:131
 msgid "Please select a tracking branch."
-msgstr ""
+msgstr "请选择某个跟踪分支."
 
 #: lib/branch_create.tcl:140
 #, tcl-format
 msgid "Tracking branch %s is not a branch in the remote repository."
-msgstr ""
+msgstr "跟踪分支 %s 并不是远端版本库中的一个分支"
 
 #: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
 msgid "Please supply a branch name."
-msgstr ""
+msgstr "请提供分支名字."
 
 #: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
 #, tcl-format
 msgid "'%s' is not an acceptable branch name."
-msgstr ""
+msgstr "'%s'不是一个可接受的分支名."
 
 #: lib/branch_delete.tcl:15
-#, fuzzy
 msgid "Delete Branch"
-msgstr "å½\93å\89\8då\88\86æ\94¯:"
+msgstr "å\88 é\99¤å\88\86æ\94¯"
 
 #: lib/branch_delete.tcl:20
 msgid "Delete Local Branch"
-msgstr ""
+msgstr "删除本地分支"
 
 #: lib/branch_delete.tcl:37
-#, fuzzy
 msgid "Local Branches"
-msgstr "分支"
+msgstr "本地分支"
 
 #: lib/branch_delete.tcl:52
 msgid "Delete Only If Merged Into"
-msgstr ""
+msgstr "仅在合并后删除"
 
 #: lib/branch_delete.tcl:54
 msgid "Always (Do not perform merge test.)"
-msgstr ""
+msgstr "总是合并 (不作合并测试.)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
-msgstr ""
+msgstr "下列分支没有完全被合并到 %s:"
 
 #: lib/branch_delete.tcl:115
 msgid ""
@@ -669,6 +682,9 @@ msgid ""
 "\n"
 " Delete the selected branches?"
 msgstr ""
+"恢复被删除的分支非常困难.\n"
+"\n"
+"是否要删除所选分支?"
 
 #: lib/branch_delete.tcl:141
 #, tcl-format
@@ -676,86 +692,84 @@ msgid ""
 "Failed to delete branches:\n"
 "%s"
 msgstr ""
+"无法删除分支:\n"
+"%s"
 
 #: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
-#, fuzzy
 msgid "Rename Branch"
-msgstr "当前分支:"
+msgstr "更改分支名:"
 
 #: lib/branch_rename.tcl:26
-#, fuzzy
 msgid "Rename"
-msgstr "æ\94¹名..."
+msgstr "æ\9b´名..."
 
 #: lib/branch_rename.tcl:36
-#, fuzzy
 msgid "Branch:"
-msgstr "分支"
+msgstr "分支:"
 
 #: lib/branch_rename.tcl:39
 msgid "New Name:"
-msgstr ""
+msgstr "新名字:"
 
 #: lib/branch_rename.tcl:75
 msgid "Please select a branch to rename."
-msgstr ""
+msgstr "请选择分支更名."
 
 #: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179
 #, tcl-format
 msgid "Branch '%s' already exists."
-msgstr ""
+msgstr "分支 '%s' 已经存在."
 
 #: lib/branch_rename.tcl:117
 #, tcl-format
 msgid "Failed to rename '%s'."
-msgstr ""
+msgstr "无法更名 '%s'."
 
 #: lib/browser.tcl:17
 msgid "Starting..."
-msgstr ""
+msgstr "开始..."
 
 #: lib/browser.tcl:26
 msgid "File Browser"
-msgstr ""
+msgstr "文件浏览器"
 
 #: lib/browser.tcl:125 lib/browser.tcl:142
 #, tcl-format
 msgid "Loading %s..."
-msgstr ""
+msgstr "装载 %s..."
 
 #: lib/browser.tcl:186
 msgid "[Up To Parent]"
-msgstr ""
+msgstr "[上层目录]"
 
 #: lib/browser.tcl:266 lib/browser.tcl:272
-#, fuzzy
 msgid "Browse Branch Files"
-msgstr "æµ\8fè§\88å½\93å\89\8då\88\86æ\94¯æ\96\87件"
+msgstr "浏览分支文件"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:215
-#: lib/choose_repository.tcl:305 lib/choose_repository.tcl:315
-#: lib/choose_repository.tcl:811
+#: lib/browser.tcl:277 lib/choose_repository.tcl:391
+#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
+#: lib/choose_repository.tcl:989
 msgid "Browse"
-msgstr ""
+msgstr "浏览"
 
 #: lib/checkout_op.tcl:79
 #, tcl-format
 msgid "Fetching %s from %s"
-msgstr ""
+msgstr "获取 %s 自 %s"
 
 #: lib/checkout_op.tcl:127
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
-msgstr ""
+msgstr "致命错误: 无法解决 %s"
 
 #: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
 msgid "Close"
-msgstr ""
+msgstr "关闭"
 
 #: lib/checkout_op.tcl:169
 #, tcl-format
 msgid "Branch '%s' does not exist."
-msgstr ""
+msgstr "分支 '%s' 并不存在."
 
 #: lib/checkout_op.tcl:206
 #, tcl-format
@@ -765,20 +779,24 @@ msgid ""
 "It cannot fast-forward to %s.\n"
 "A merge is required."
 msgstr ""
+"分支 '%s' 已经存在.\n"
+"\n"
+"无法快速合并到 %s.\n"
+"需要普通合并."
 
 #: lib/checkout_op.tcl:220
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
-msgstr ""
+msgstr "合并策略 '%s' 不支持."
 
 #: lib/checkout_op.tcl:239
 #, tcl-format
 msgid "Failed to update '%s'."
-msgstr ""
+msgstr "无法更新 '%s'."
 
 #: lib/checkout_op.tcl:251
 msgid "Staging area (index) is already locked."
-msgstr ""
+msgstr "缓存区域 (index) 已被锁定."
 
 #: lib/checkout_op.tcl:266
 msgid ""
@@ -789,25 +807,31 @@ msgid ""
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
 #: lib/checkout_op.tcl:322
 #, tcl-format
 msgid "Updating working directory to '%s'..."
-msgstr ""
+msgstr "更新工作目录到 '%s'..."
 
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr ""
+msgstr "中止 '%s' 的 checkout 操作 (需要做文件级合并)."
 
 #: lib/checkout_op.tcl:354
 msgid "File level merge required."
-msgstr ""
+msgstr "需要文件级合并."
 
 #: lib/checkout_op.tcl:358
 #, tcl-format
 msgid "Staying on branch '%s'."
-msgstr ""
+msgstr "停留在分支 '%s'."
 
 #: lib/checkout_op.tcl:429
 msgid ""
@@ -816,29 +840,32 @@ msgid ""
 "If you wanted to be on a branch, create one now starting from 'This Detached "
 "Checkout'."
 msgstr ""
+"你不在某个本地分支上.\n"
+"\n"
+"如果你想位于某分支上, 从当前脱节的Checkout中创建一个新分支."
 
 #: lib/checkout_op.tcl:446
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "Checked out '%s'."
-msgstr "切换..."
+msgstr "'%s' 已被 checkout"
 
 #: lib/checkout_op.tcl:478
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
-msgstr ""
+msgstr "复位 '%s' 到 '%s' 将导致下列提交的丢失:"
 
 #: lib/checkout_op.tcl:500
 msgid "Recovering lost commits may not be easy."
-msgstr ""
+msgstr "恢复丢失的提交是比较困难的."
 
 #: lib/checkout_op.tcl:505
 #, tcl-format
 msgid "Reset '%s'?"
-msgstr ""
+msgstr "复位 '%s'?"
 
 #: lib/checkout_op.tcl:510 lib/merge.tcl:164
 msgid "Visualize"
-msgstr ""
+msgstr "图示"
 
 #: lib/checkout_op.tcl:578
 #, tcl-format
@@ -850,286 +877,301 @@ msgid ""
 "\n"
 "This should not have occurred.  %s will now close and give up."
 msgstr ""
+"无法设定当前分支.\n"
+"\n"
+"当前工作目录仅有部分被切换出, 我们已成功的更新了您的文件但是无法更新某个内部"
+"的Git文件.\n"
+"\n"
+"这本不该发生, %s 将关闭并放弃."
 
 #: lib/choose_font.tcl:39
-#, fuzzy
 msgid "Select"
-msgstr "全选"
+msgstr "选择"
 
 #: lib/choose_font.tcl:53
 msgid "Font Family"
-msgstr ""
+msgstr "字体族"
 
 #: lib/choose_font.tcl:73
-#, fuzzy
 msgid "Font Size"
-msgstr "缩小字体"
+msgstr "字体大小"
 
 #: lib/choose_font.tcl:90
 msgid "Font Example"
-msgstr ""
+msgstr "字体样例"
 
 #: lib/choose_font.tcl:101
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
 msgstr ""
+"这是样例文本.\n"
+"如果你喜欢, 你可以设置该字体."
 
-#: lib/choose_repository.tcl:25
+#: lib/choose_repository.tcl:27
 msgid "Git Gui"
-msgstr ""
+msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:69 lib/choose_repository.tcl:204
-#, fuzzy
+#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
 msgid "Create New Repository"
-msgstr "版本树"
+msgstr "创建新的版本库"
 
-#: lib/choose_repository.tcl:74 lib/choose_repository.tcl:291
-#, fuzzy
+#: lib/choose_repository.tcl:86
+msgid "New..."
+msgstr "新建..."
+
+#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
 msgid "Clone Existing Repository"
-msgstr "版本树"
+msgstr "克隆已有版本库"
 
-#: lib/choose_repository.tcl:79 lib/choose_repository.tcl:800
-#, fuzzy
+#: lib/choose_repository.tcl:99
+msgid "Clone..."
+msgstr "克隆..."
+
+#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
 msgid "Open Existing Repository"
-msgstr "版本树"
+msgstr "打开已有版本库"
 
-#: lib/choose_repository.tcl:91
-msgid "Next >"
-msgstr ""
+#: lib/choose_repository.tcl:112
+msgid "Open..."
+msgstr "打开..."
 
-#: lib/choose_repository.tcl:152
+#: lib/choose_repository.tcl:125
+msgid "Recent Repositories"
+msgstr "最近版本库"
+
+#: lib/choose_repository.tcl:131
+msgid "Open Recent Repository:"
+msgstr "打开最近版本库"
+
+#: lib/choose_repository.tcl:294
 #, tcl-format
 msgid "Location %s already exists."
-msgstr ""
+msgstr "位置 %s 已经存在."
 
-#: lib/choose_repository.tcl:158 lib/choose_repository.tcl:165
-#: lib/choose_repository.tcl:172
+#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
+#: lib/choose_repository.tcl:314
 #, tcl-format
 msgid "Failed to create repository %s:"
-msgstr ""
+msgstr "无法创建版本库 %s:"
 
-#: lib/choose_repository.tcl:209 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
 msgid "Directory:"
-msgstr ""
+msgstr "目录:"
 
-#: lib/choose_repository.tcl:238 lib/choose_repository.tcl:363
-#: lib/choose_repository.tcl:834
-#, fuzzy
+#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1013
 msgid "Git Repository"
-msgstr "版本树"
+msgstr "Git 版本库"
 
-#: lib/choose_repository.tcl:253 lib/choose_repository.tcl:260
+#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
-msgstr ""
+msgstr "目录 %s 已经存在."
 
-#: lib/choose_repository.tcl:265
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "File %s already exists."
-msgstr ""
+msgstr "文件 %s 已经存在."
 
-#: lib/choose_repository.tcl:286
+#: lib/choose_repository.tcl:463
 msgid "Clone"
-msgstr ""
+msgstr "克隆"
 
-#: lib/choose_repository.tcl:299
+#: lib/choose_repository.tcl:476
 msgid "URL:"
-msgstr ""
+msgstr "URL:"
 
-#: lib/choose_repository.tcl:319
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
-msgstr ""
+msgstr "克隆类型:"
 
-#: lib/choose_repository.tcl:325
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
-msgstr ""
+msgstr "标准方式 (快速, 部分备份, 作硬连接)"
 
-#: lib/choose_repository.tcl:331
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
-msgstr ""
+msgstr "全部复制 (较慢, 做备份)"
 
-#: lib/choose_repository.tcl:337
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
-msgstr ""
+msgstr "共享方式 (最快, 不推荐, 不做备份)"
 
-#: lib/choose_repository.tcl:369 lib/choose_repository.tcl:418
-#: lib/choose_repository.tcl:560 lib/choose_repository.tcl:630
-#: lib/choose_repository.tcl:840 lib/choose_repository.tcl:848
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
+#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
 #, tcl-format
 msgid "Not a Git repository: %s"
-msgstr ""
+msgstr "不是一个 Git 版本库: %s"
 
-#: lib/choose_repository.tcl:405
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
-msgstr ""
+msgstr "标准方式仅当是本地版本库时有效."
 
-#: lib/choose_repository.tcl:409
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
-msgstr ""
+msgstr "共享方式仅当是本地版本库时有效."
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:617
 msgid "Failed to configure origin"
-msgstr ""
+msgstr "无法配置 origin"
 
-#: lib/choose_repository.tcl:451
+#: lib/choose_repository.tcl:629
 msgid "Counting objects"
-msgstr ""
+msgstr "清点对象"
 
-#: lib/choose_repository.tcl:452
+#: lib/choose_repository.tcl:630
+#, fuzzy
 msgid "buckets"
-msgstr ""
+msgstr "水桶??"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:654
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
-msgstr ""
+msgstr "无法复制 objects/info/alternates: %s"
 
-#: lib/choose_repository.tcl:512
+#: lib/choose_repository.tcl:690
 #, tcl-format
 msgid "Nothing to clone from %s."
-msgstr ""
+msgstr "没有东西可从 %s 克隆."
 
-#: lib/choose_repository.tcl:514 lib/choose_repository.tcl:728
-#: lib/choose_repository.tcl:740
+#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
+#: lib/choose_repository.tcl:918
 msgid "The 'master' branch has not been initialized."
-msgstr ""
+msgstr "'master'分支尚未初始化."
 
-#: lib/choose_repository.tcl:527
+#: lib/choose_repository.tcl:705
 msgid "Hardlinks are unavailable.  Falling back to copying."
-msgstr ""
+msgstr "硬连接不可用. 使用复制."
 
-#: lib/choose_repository.tcl:539
+#: lib/choose_repository.tcl:717
 #, tcl-format
 msgid "Cloning from %s"
-msgstr ""
+msgstr "从 %s 克隆"
 
-#: lib/choose_repository.tcl:570
-#, fuzzy
+#: lib/choose_repository.tcl:748
 msgid "Copying objects"
-msgstr "å\8e\8b缩æ\95°æ\8d®åº\93"
+msgstr "å¤\8då\88¶ objects"
 
-#: lib/choose_repository.tcl:571
+#: lib/choose_repository.tcl:749
 msgid "KiB"
-msgstr ""
+msgstr "KiB"
 
-#: lib/choose_repository.tcl:595
+#: lib/choose_repository.tcl:773
 #, tcl-format
 msgid "Unable to copy object: %s"
-msgstr ""
+msgstr "无法复制 object: %s"
 
-#: lib/choose_repository.tcl:605
+#: lib/choose_repository.tcl:783
 msgid "Linking objects"
-msgstr ""
+msgstr "链接 objects"
 
-#: lib/choose_repository.tcl:606
+#: lib/choose_repository.tcl:784
 msgid "objects"
-msgstr ""
+msgstr "objects"
 
-#: lib/choose_repository.tcl:614
+#: lib/choose_repository.tcl:792
 #, tcl-format
 msgid "Unable to hardlink object: %s"
-msgstr ""
+msgstr "无法硬链接 object: %s"
 
-#: lib/choose_repository.tcl:669
+#: lib/choose_repository.tcl:847
 msgid "Cannot fetch branches and objects.  See console output for details."
-msgstr ""
+msgstr "无法获取分支和对象. 请查看控制终端的输出."
 
-#: lib/choose_repository.tcl:680
+#: lib/choose_repository.tcl:858
 msgid "Cannot fetch tags.  See console output for details."
-msgstr ""
+msgstr "无法获取标签. 请查看控制终端的输出."
 
-#: lib/choose_repository.tcl:704
+#: lib/choose_repository.tcl:882
 msgid "Cannot determine HEAD.  See console output for details."
-msgstr ""
+msgstr "无法确定 HEAD. 请查看控制终端的输出."
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:891
 #, tcl-format
 msgid "Unable to cleanup %s"
-msgstr ""
+msgstr "无法清理 %s"
 
-#: lib/choose_repository.tcl:719
+#: lib/choose_repository.tcl:897
 msgid "Clone failed."
-msgstr ""
+msgstr "克隆失败."
 
-#: lib/choose_repository.tcl:726
+#: lib/choose_repository.tcl:904
 msgid "No default branch obtained."
-msgstr ""
+msgstr "没有获取缺省分支"
 
-#: lib/choose_repository.tcl:737
+#: lib/choose_repository.tcl:915
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
-msgstr ""
+msgstr "无法解析 %s 为提交."
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:927
 msgid "Creating working directory"
-msgstr ""
+msgstr "创建工作目录"
 
-#: lib/choose_repository.tcl:750 lib/index.tcl:15 lib/index.tcl:80
-#: lib/index.tcl:149
+#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/index.tcl:193
 msgid "files"
-msgstr ""
+msgstr "文件"
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:957
 msgid "Initial file checkout failed."
-msgstr ""
+msgstr "初始的文件checkout失败"
 
-#: lib/choose_repository.tcl:795
+#: lib/choose_repository.tcl:973
 msgid "Open"
-msgstr ""
+msgstr "打开"
 
-#: lib/choose_repository.tcl:805
-#, fuzzy
+#: lib/choose_repository.tcl:983
 msgid "Repository:"
-msgstr "版本"
+msgstr "版本"
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:1033
 #, tcl-format
 msgid "Failed to open repository %s:"
-msgstr ""
+msgstr "无法打开版本库 %s:"
 
 #: lib/choose_rev.tcl:53
 msgid "This Detached Checkout"
-msgstr ""
+msgstr "该脱节的Checkout"
 
 #: lib/choose_rev.tcl:60
 msgid "Revision Expression:"
-msgstr ""
+msgstr "版本表达式:"
 
 #: lib/choose_rev.tcl:74
-#, fuzzy
 msgid "Local Branch"
-msgstr "分支"
+msgstr "本地分支"
 
 #: lib/choose_rev.tcl:79
-#, fuzzy
 msgid "Tracking Branch"
-msgstr "当前分支:"
+msgstr "跟踪分支:"
 
 #: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
 msgid "Tag"
-msgstr ""
+msgstr "标签"
 
 #: lib/choose_rev.tcl:317
 #, tcl-format
 msgid "Invalid revision: %s"
-msgstr ""
+msgstr "无效版本: %s"
 
 #: lib/choose_rev.tcl:338
 msgid "No revision selected."
-msgstr ""
+msgstr "没有选择版本."
 
 #: lib/choose_rev.tcl:346
 msgid "Revision expression is empty."
-msgstr ""
+msgstr "版本表达式为空."
 
 #: lib/choose_rev.tcl:530
 msgid "Updated"
-msgstr ""
+msgstr "已更新"
 
 #: lib/choose_rev.tcl:558
 msgid "URL"
-msgstr ""
+msgstr "URL"
 
 #: lib/commit.tcl:9
 msgid ""
@@ -1138,6 +1180,9 @@ msgid ""
 "You are about to create the initial commit.  There is no commit before this "
 "to amend.\n"
 msgstr ""
+"没有改动需要修正.\n"
+"\n"
+"你正在创建最初的提交. 在此之前没有提交可以修正.\n"
 
 #: lib/commit.tcl:18
 msgid ""
@@ -1147,18 +1192,22 @@ msgid ""
 "completed.  You cannot amend the prior commit unless you first abort the "
 "current merge activity.\n"
 msgstr ""
+"在合并时无法修正.\n"
+"\n"
+"你当前正在一次尚未完成的合并操作过程中. 除非中止当前合并活动,\n"
+"否则无法修正之前的提交.\n"
 
 #: lib/commit.tcl:49
 msgid "Error loading commit data for amend:"
-msgstr ""
+msgstr "为修正装载提交数据出错:"
 
 #: lib/commit.tcl:76
 msgid "Unable to obtain your identity:"
-msgstr ""
+msgstr "无法获知你的身份:"
 
 #: lib/commit.tcl:81
 msgid "Invalid GIT_COMMITTER_IDENT:"
-msgstr ""
+msgstr "无效的 GIT_COMMITTER_IDENT"
 
 #: lib/commit.tcl:133
 msgid ""
@@ -1169,6 +1218,12 @@ msgid ""
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
 #: lib/commit.tcl:154
 #, tcl-format
@@ -1178,6 +1233,9 @@ msgid ""
 "File %s has merge conflicts.  You must resolve them and stage the file "
 "before committing.\n"
 msgstr ""
+"尚未合并的文件没有办法提交.\n"
+"\n"
+"文件 %s 有合并冲突, 你必须解决这些冲突并缓存该文件作提交.\n"
 
 #: lib/commit.tcl:162
 #, tcl-format
@@ -1186,6 +1244,9 @@ msgid ""
 "\n"
 "File %s cannot be committed by this program.\n"
 msgstr ""
+"检测到未知文件状态 %s.\n"
+"\n"
+"文件 %s 无法由该程序提交.\n"
 
 #: lib/commit.tcl:170
 msgid ""
@@ -1193,6 +1254,9 @@ msgid ""
 "\n"
 "You must stage at least 1 file before you can commit.\n"
 msgstr ""
+"没有需要提交的变动.\n"
+"\n"
+"提交前你必须首先缓存至少一个文件.\n"
 
 #: lib/commit.tcl:183
 msgid ""
@@ -1200,19 +1264,26 @@ msgid ""
 "\n"
 "A good commit message has the following format:\n"
 "\n"
-"- First line: Describe in one sentance what you did.\n"
+"- First line: Describe in one sentence what you did.\n"
 "- Second line: Blank\n"
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
+"请提供一条提交信息.\n"
+"\n"
+"一条好的提交信息有下列格式:\n"
+"\n"
+"- 第一行: 一句话概括你做的修改.\n"
+"- 第二行: 空行\n"
+"- 剩余行: 请描述为什么你做的这些改动是好的.\n"
 
 #: lib/commit.tcl:257
 msgid "write-tree failed:"
-msgstr ""
+msgstr "write-tree 失败:"
 
 #: lib/commit.tcl:275
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
-msgstr ""
+msgstr "提交 %s 似乎已损坏"
 
 #: lib/commit.tcl:279
 msgid ""
@@ -1222,77 +1293,81 @@ msgid ""
 "\n"
 "A rescan will be automatically started now.\n"
 msgstr ""
+"没有改动提交.\n"
+"\n"
+"该提交没有改动任何文件也不是一个合并提交.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
 #: lib/commit.tcl:286
 msgid "No changes to commit."
-msgstr ""
+msgstr "没有改动要提交."
 
 #: lib/commit.tcl:303
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
-msgstr ""
+msgstr "警告: Tcl 不支持编码方式 '%s'."
 
 #: lib/commit.tcl:317
 msgid "commit-tree failed:"
-msgstr ""
+msgstr "commit-tree 失败:"
 
 #: lib/commit.tcl:339
 msgid "update-ref failed:"
-msgstr ""
+msgstr "update-ref 失败:"
 
 #: lib/commit.tcl:430
 #, tcl-format
 msgid "Created commit %s: %s"
-msgstr ""
+msgstr "创建了 commit %s: %s"
 
 #: lib/console.tcl:57
 msgid "Working... please wait..."
-msgstr ""
+msgstr "工作中... 请等待..."
 
 #: lib/console.tcl:183
 msgid "Success"
-msgstr ""
+msgstr "成功"
 
 #: lib/console.tcl:196
 msgid "Error: Command Failed"
-msgstr ""
+msgstr "错误: 命令失败"
 
 #: lib/database.tcl:43
 msgid "Number of loose objects"
-msgstr ""
+msgstr "松散对象的数量"
 
 #: lib/database.tcl:44
 msgid "Disk space used by loose objects"
-msgstr ""
+msgstr "松散对象所使用的磁盘空间"
 
 #: lib/database.tcl:45
 msgid "Number of packed objects"
-msgstr ""
+msgstr "压缩对象数量"
 
 #: lib/database.tcl:46
 msgid "Number of packs"
-msgstr ""
+msgstr "压缩包数量"
 
 #: lib/database.tcl:47
 msgid "Disk space used by packed objects"
-msgstr ""
+msgstr "压缩对象所使用的磁盘空间"
 
 #: lib/database.tcl:48
 msgid "Packed objects waiting for pruning"
-msgstr ""
+msgstr "压缩对象等待清理"
 
 #: lib/database.tcl:49
 msgid "Garbage files"
-msgstr ""
+msgstr "垃圾文件"
 
 #: lib/database.tcl:72
-#, fuzzy
 msgid "Compressing the object database"
-msgstr "压缩数据库"
+msgstr "压缩对象数据库"
 
 #: lib/database.tcl:83
 msgid "Verifying the object database with fsck-objects"
-msgstr ""
+msgstr "使用 fsck-objects 验证对象数据库"
 
 #: lib/database.tcl:108
 #, tcl-format
@@ -1304,11 +1379,16 @@ msgid ""
 "\n"
 "Compress the database now?"
 msgstr ""
+"该版本库当前约有 %i 个松散对象.\n"
+"\n"
+"为达到较优的性能,强烈建议你在松散对象多于 %i 时压缩数据库.\n"
+"\n"
+"现在就压缩数据库么?"
 
 #: lib/date.tcl:25
 #, tcl-format
 msgid "Invalid date from Git: %s"
-msgstr ""
+msgstr "无效的日期: %s"
 
 #: lib/diff.tcl:42
 #, tcl-format
@@ -1323,80 +1403,107 @@ msgid ""
 "A rescan will be automatically started to find other files which may have "
 "the same state."
 msgstr ""
+"未检测到改动.\n"
+"\n"
+"该文件的修改日期被另一个程序所更新, 但其内容并没有变化.\n"
+"\n"
+"对于类似情况的其他文件的重新扫描将自动开始."
 
 #: lib/diff.tcl:81
-#, tcl-format
+#, fuzzy, tcl-format
 msgid "Loading diff of %s..."
-msgstr ""
+msgstr "装载 %s 的 diff ..."
 
 #: lib/diff.tcl:114 lib/diff.tcl:184
 #, tcl-format
 msgid "Unable to display %s"
-msgstr ""
+msgstr "无法显示 %s"
 
 #: lib/diff.tcl:115
 msgid "Error loading file:"
-msgstr ""
+msgstr "装载文件出错:"
 
 #: lib/diff.tcl:122
 msgid "Git Repository (subproject)"
-msgstr ""
+msgstr "Git 版本库 (子项目)"
 
 #: lib/diff.tcl:134
 msgid "* Binary file (not showing content)."
-msgstr ""
+msgstr "* 二进制文件 (不显示内容)."
 
 #: lib/diff.tcl:185
 msgid "Error loading diff:"
-msgstr ""
+msgstr "装载 diff 错误:"
 
 #: lib/diff.tcl:302
 msgid "Failed to unstage selected hunk."
-msgstr ""
+msgstr "无法将选择的代码段从缓存中删除."
 
 #: lib/diff.tcl:309
 msgid "Failed to stage selected hunk."
-msgstr ""
+msgstr "无法缓存所选代码段."
 
 #: lib/error.tcl:12 lib/error.tcl:102
 msgid "error"
-msgstr ""
+msgstr "错误"
 
 #: lib/error.tcl:28
 msgid "warning"
-msgstr ""
+msgstr "警告"
 
 #: lib/error.tcl:81
 msgid "You must correct the above errors before committing."
-msgstr ""
+msgstr "你必须在提交前修正上述错误."
 
-#: lib/index.tcl:241
-#, fuzzy, tcl-format
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "无法解锁缓存 (index)"
+
+#: lib/index.tcl:15
+msgid "Index Error"
+msgstr "缓存(Index)错误"
+
+#: lib/index.tcl:21
+msgid ""
+"Updating the Git index failed.  A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr "更新 Git 缓存(Index)失败, 重新扫描将自动开始以重新同步 git-gui."
+
+#: lib/index.tcl:27
+msgid "Continue"
+msgstr "继续"
+
+#: lib/index.tcl:31
+msgid "Unlock Index"
+msgstr "解锁 Index"
+
+#: lib/index.tcl:282
+#, tcl-format
 msgid "Unstaging %s from commit"
-msgstr "ä»\8eæ\9c¬æ¬¡æ\8f\90交移é\99¤"
+msgstr "ä»\8eæ\8f\90交ç¼\93å­\98中å\88 é\99¤ %s"
 
-#: lib/index.tcl:285
+#: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
-msgstr ""
+msgstr "添加 %s"
 
-#: lib/index.tcl:340
-#, fuzzy, tcl-format
+#: lib/index.tcl:381
+#, tcl-format
 msgid "Revert changes in file %s?"
-msgstr "æ\81¢å¤\8dä¿®æ\94¹"
+msgstr "æ\92¤é\94\80æ\96\87件 %s ä¸­ç\9a\84æ\94¹å\8a¨?"
 
-#: lib/index.tcl:342
+#: lib/index.tcl:383
 #, tcl-format
 msgid "Revert changes in these %i files?"
-msgstr ""
+msgstr "撤销这些 (%i个) 文件的改动?"
 
-#: lib/index.tcl:348
+#: lib/index.tcl:389
 msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr ""
+msgstr "任何未缓存的改动将在这次撤销中永久丢失."
 
-#: lib/index.tcl:351
+#: lib/index.tcl:392
 msgid "Do Nothing"
-msgstr ""
+msgstr "不做操作"
 
 #: lib/merge.tcl:13
 msgid ""
@@ -1404,6 +1511,9 @@ msgid ""
 "\n"
 "You must finish amending this commit before starting any type of merge.\n"
 msgstr ""
+"修正时无法做合并.\n"
+"\n"
+"你必须完成对该提交的修正才能继续任何类型的合并操作.\n"
 
 #: lib/merge.tcl:27
 msgid ""
@@ -1414,6 +1524,12 @@ msgid ""
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
 #: lib/merge.tcl:44
 #, tcl-format
@@ -1425,6 +1541,12 @@ msgid ""
 "You must resolve them, stage the file, and commit to complete the current "
 "merge.  Only then can you begin another merge.\n"
 msgstr ""
+"你正处在一个有冲突的合并操作中.\n"
+"\n"
+"文件 %s 有合并冲突.\n"
+"\n"
+"你必须解决这些冲突, 缓存该文件, 并提交来完成当前的合并.仅当这样后才能开始下一"
+"个合并操作.\n"
 
 #: lib/merge.tcl:54
 #, tcl-format
@@ -1436,6 +1558,12 @@ msgid ""
 "You should complete the current commit before starting a merge.  Doing so "
 "will help you abort a failed merge, should the need arise.\n"
 msgstr ""
+"你正处在一个改动当中.\n"
+"\n"
+"文件 %s 已被修改.\n"
+"\n"
+"你必须完成当前的提交后才能开始合并. 如果需要, 这么做将有助于"
+"中止一次失败的合并.\n"
 
 #: lib/merge.tcl:106
 #, tcl-format
@@ -1445,24 +1573,24 @@ msgstr ""
 #: lib/merge.tcl:119
 #, tcl-format
 msgid "Merging %s and %s"
-msgstr ""
+msgstr "合并 %s 和 %s"
 
 #: lib/merge.tcl:131
 msgid "Merge completed successfully."
-msgstr ""
+msgstr "合并成功完成."
 
 #: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
-msgstr ""
+msgstr "合并失败. 需要解决冲突."
 
 #: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
-msgstr ""
+msgstr "合并到 %s"
 
 #: lib/merge.tcl:177
 msgid "Revision To Merge"
-msgstr ""
+msgstr "要合并的版本"
 
 #: lib/merge.tcl:212
 msgid ""
@@ -1470,6 +1598,9 @@ msgid ""
 "\n"
 "You must finish amending this commit.\n"
 msgstr ""
+"修正操作中无法中止.\n"
+"\n"
+"你必须先完成本次修正操作.\n"
 
 #: lib/merge.tcl:222
 msgid ""
@@ -1479,6 +1610,11 @@ msgid ""
 "\n"
 "Continue with aborting the current merge?"
 msgstr ""
+"中止合并?\n"
+"\n"
+"中止当前的合并操作将导致 *所有* 尚未提交的改动丢失.\n"
+"\n"
+"是否要继续中止当前的合并操作?"
 
 #: lib/merge.tcl:228
 msgid ""
@@ -1488,150 +1624,137 @@ msgid ""
 "\n"
 "Continue with resetting the current changes?"
 msgstr ""
+"是否复位当前改动?\n"
+"\n"
+"复位当前的改动将导致 *所有* 未提交的改动丢失.\n"
+"\n"
+"是否要继续复位当前的改动?"
 
 #: lib/merge.tcl:239
 msgid "Aborting"
-msgstr ""
+msgstr "中止"
 
 #: lib/merge.tcl:266
 msgid "Abort failed."
-msgstr ""
+msgstr "中止失败"
 
 #: lib/merge.tcl:268
 msgid "Abort completed.  Ready."
-msgstr ""
+msgstr "中止完成. 就绪."
 
 #: lib/option.tcl:82
 msgid "Restore Defaults"
-msgstr ""
+msgstr "恢复默认值"
 
 #: lib/option.tcl:86
 msgid "Save"
-msgstr ""
+msgstr "保存"
 
 #: lib/option.tcl:96
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "%s Repository"
-msgstr "版本树"
+msgstr "%s 版本库"
 
 #: lib/option.tcl:97
 msgid "Global (All Repositories)"
-msgstr ""
+msgstr "全局 (所有版本库)"
 
 #: lib/option.tcl:103
 msgid "User Name"
-msgstr ""
+msgstr "用户名"
 
 #: lib/option.tcl:104
 msgid "Email Address"
-msgstr ""
+msgstr "Email 地址"
 
 #: lib/option.tcl:106
-#, fuzzy
 msgid "Summarize Merge Commits"
-msgstr "修订合并提交描述:"
+msgstr "概述合并提交:"
 
 #: lib/option.tcl:107
 msgid "Merge Verbosity"
-msgstr ""
+msgstr "合并冗余度"
 
 #: lib/option.tcl:108
 msgid "Show Diffstat After Merge"
-msgstr ""
+msgstr "在合并后显示 Diffstat"
 
 #: lib/option.tcl:110
 msgid "Trust File Modification Timestamps"
-msgstr ""
+msgstr "相信文件的改动时间"
 
 #: lib/option.tcl:111
 msgid "Prune Tracking Branches During Fetch"
-msgstr ""
+msgstr "获取时清除跟踪分支"
 
 #: lib/option.tcl:112
 msgid "Match Tracking Branches"
-msgstr ""
+msgstr "匹配跟踪分支"
 
 #: lib/option.tcl:113
 msgid "Number of Diff Context Lines"
-msgstr ""
+msgstr "Diff 上下文行数"
 
 #: lib/option.tcl:114
 msgid "New Branch Name Template"
-msgstr ""
+msgstr "新建分支命名模板"
 
 #: lib/option.tcl:176
 msgid "Change Font"
-msgstr ""
+msgstr "更改字体"
 
 #: lib/option.tcl:180
 #, tcl-format
 msgid "Choose %s"
-msgstr ""
+msgstr "选择 %s"
 
 #: lib/option.tcl:186
 msgid "pt."
-msgstr ""
+msgstr ""
 
 #: lib/option.tcl:200
 msgid "Preferences"
-msgstr ""
+msgstr "首选项"
 
 #: lib/option.tcl:235
 msgid "Failed to completely save options:"
-msgstr ""
-
-#: lib/remote.tcl:165
-msgid "Prune from"
-msgstr ""
-
-#: lib/remote.tcl:170
-#, fuzzy
-msgid "Fetch from"
-msgstr "导入"
-
-#: lib/remote.tcl:213
-#, fuzzy
-msgid "Push to"
-msgstr "上传"
+msgstr "无法完全保存选项:"
 
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
 msgid "Delete Remote Branch"
-msgstr ""
+msgstr "删除远端分支"
 
 #: lib/remote_branch_delete.tcl:47
-#, fuzzy
 msgid "From Repository"
-msgstr "版本树"
+msgstr "从版本库"
 
 #: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
 msgid "Remote:"
-msgstr ""
+msgstr "Remote:"
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
 msgid "Arbitrary URL:"
-msgstr ""
+msgstr "任意 URL:"
 
 #: lib/remote_branch_delete.tcl:84
-#, fuzzy
 msgid "Branches"
 msgstr "分支"
 
 #: lib/remote_branch_delete.tcl:109
-#, fuzzy
 msgid "Delete Only If"
-msgstr "删除"
+msgstr "删除仅当"
 
 #: lib/remote_branch_delete.tcl:111
 msgid "Merged Into:"
-msgstr ""
+msgstr "合并到"
 
 #: lib/remote_branch_delete.tcl:119
 msgid "Always (Do not perform merge checks)"
-msgstr ""
+msgstr "总是合并 (不作合并检查)"
 
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
-msgstr ""
+msgstr "'合并到' 需要指定某个分支"
 
 #: lib/remote_branch_delete.tcl:184
 #, tcl-format
@@ -1640,6 +1763,9 @@ msgid ""
 "\n"
 " - %s"
 msgstr ""
+"下列分支没有被全部合并到 %s 中:\n"
+"\n"
+" - %s"
 
 #: lib/remote_branch_delete.tcl:189
 #, tcl-format
@@ -1647,10 +1773,11 @@ msgid ""
 "One or more of the merge tests failed because you have not fetched the "
 "necessary commits.  Try fetching from %s first."
 msgstr ""
+"由于没有获取到必要的提交,一个或多个合并测试失败。请尝试从 %s 处先获取。"
 
 #: lib/remote_branch_delete.tcl:207
 msgid "Please select one or more branches to delete."
-msgstr ""
+msgstr "请选择某个或多个分支来删除"
 
 #: lib/remote_branch_delete.tcl:216
 msgid ""
@@ -1658,112 +1785,108 @@ msgid ""
 "\n"
 "Delete the selected branches?"
 msgstr ""
+"恢复被删除的分支非常困难.\n"
+"\n"
+"是否要删除所选分支?"
 
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
-msgstr ""
+msgstr "从 %s 中删除分支"
 
 #: lib/remote_branch_delete.tcl:286
 msgid "No repository selected."
-msgstr ""
+msgstr "没有选择版本库"
 
 #: lib/remote_branch_delete.tcl:291
 #, tcl-format
 msgid "Scanning %s..."
-msgstr ""
+msgstr "正在扫描 %s..."
 
-#: lib/shortcut.tcl:26 lib/shortcut.tcl:74
-msgid "Cannot write script:"
-msgstr ""
+#: lib/remote.tcl:165
+msgid "Prune from"
+msgstr "从..清除(prune)"
+
+#: lib/remote.tcl:170
+msgid "Fetch from"
+msgstr "从..获取(fetch)"
+
+#: lib/remote.tcl:213
+msgid "Push to"
+msgstr "上传到(push)"
+
+#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+msgid "Cannot write shortcut:"
+msgstr "无法修改快捷方式:"
 
-#: lib/shortcut.tcl:149
+#: lib/shortcut.tcl:136
 msgid "Cannot write icon:"
-msgstr ""
+msgstr "无法修改图标:"
 
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
-msgstr ""
+msgstr "%s ... %*i of %*i %s (%3i%%)"
 
 #: lib/transport.tcl:6
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "fetch %s"
-msgstr "导入"
+msgstr "获取(fetch)"
 
 #: lib/transport.tcl:7
 #, tcl-format
 msgid "Fetching new changes from %s"
-msgstr ""
+msgstr "从 %s 处获取新的改动"
 
 #: lib/transport.tcl:18
 #, tcl-format
 msgid "remote prune %s"
-msgstr ""
+msgstr "清除远端 %s"
 
 #: lib/transport.tcl:19
 #, tcl-format
 msgid "Pruning tracking branches deleted from %s"
-msgstr ""
+msgstr "清除"
 
 #: lib/transport.tcl:25 lib/transport.tcl:71
 #, tcl-format
 msgid "push %s"
-msgstr ""
+msgstr "上传 %s"
 
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
-msgstr ""
+msgstr "上传改动到 %s"
 
 #: lib/transport.tcl:72
 #, tcl-format
 msgid "Pushing %s %s to %s"
-msgstr ""
+msgstr "上传 %s %s 到 %s"
 
 #: lib/transport.tcl:89
-#, fuzzy
 msgid "Push Branches"
-msgstr "分支"
+msgstr "上传分支"
 
 #: lib/transport.tcl:103
-#, fuzzy
 msgid "Source Branches"
-msgstr "当前分支:"
+msgstr "源端分支:"
 
 #: lib/transport.tcl:120
-#, fuzzy
 msgid "Destination Repository"
-msgstr "ç\89\88æ\9c¬æ \91"
+msgstr "ç\9b®æ \87ç\89\88æ\9c¬åº\93"
 
 #: lib/transport.tcl:158
 msgid "Transfer Options"
-msgstr ""
+msgstr "传输选项"
 
 #: lib/transport.tcl:160
 msgid "Force overwrite existing branch (may discard changes)"
-msgstr ""
+msgstr "强制覆盖已有的分支 (可能会丢失改动)"
 
 #: lib/transport.tcl:164
 msgid "Use thin pack (for slow network connections)"
-msgstr ""
+msgstr "使用 thin pack (适用于低速网络连接)"
 
 #: lib/transport.tcl:168
 msgid "Include tags"
-msgstr ""
-
-#~ msgid "Add To Commit"
-#~ msgstr "添加到本次提交"
-
-#~ msgid "Add Existing To Commit"
-#~ msgstr "添加默认修改文件"
-
-#~ msgid "Unstaged Changes (Will Not Be Committed)"
-#~ msgstr "不被提交的修改"
-
-#~ msgid "Add Existing"
-#~ msgstr "添加默认修改文件"
-
-#, fuzzy
-#~ msgid "Push to %s..."
-#~ msgstr "上传..."
+msgstr "包含标签"
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 233e5eae1d337bff40d0adba4bbb117bbd4b5dee..7cd8f7134e696312d243d73acebb6ecfe07d1e13 100755 (executable)
@@ -63,7 +63,23 @@ tmp_info="$tmp_dir/info"
 commit=$(git rev-parse HEAD)
 
 mkdir $tmp_dir || exit 2
-for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
+while read patch_name level garbage
+do
+       case "$patch_name" in ''|'#'*) continue;; esac
+       case "$level" in
+       -p*)    ;;
+       ''|'#'*)
+               level=;;
+       *)
+               echo "unable to parse patch level, ignoring it."
+               level=;;
+       esac
+       case "$garbage" in
+       ''|'#'*);;
+       *)
+               echo "trailing garbage found in series file: $garbage"
+               exit 1;;
+       esac
        if ! [ -f "$QUILT_PATCHES/$patch_name" ] ; then
                echo "$patch_name doesn't exist. Skipping."
                continue
@@ -113,10 +129,10 @@ for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
        fi
 
        if [ -z "$dry_run" ] ; then
-               git apply --index -C1 "$tmp_patch" &&
+               git apply --index -C1 ${level:+"$level"} "$tmp_patch" &&
                tree=$(git write-tree) &&
                commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
                git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
        fi
-done
+done <"$QUILT_PATCHES/series"
 rm -rf $tmp_dir || exit 5
index 6b9af962a9cee734510518e82cf2537441ed843a..ff66af3ba8348ff86b25c91a1875b93f885a048c 100755 (executable)
@@ -18,8 +18,7 @@ original <branch> and remove the .dotest working files, use the command
 git rebase --abort instead.
 
 Note that if <branch> is not specified on the command line, the
-currently checked out branch is used.  You must be in the top
-directory of your project to start (or continue) a rebase.
+currently checked out branch is used.
 
 Example:       git-rebase master~1 topic
 
@@ -376,7 +375,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 &&
diff --git a/git-remote.perl b/git-remote.perl
deleted file mode 100755 (executable)
index b30ed73..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-use Git;
-my $git = Git->repository();
-
-sub add_remote_config {
-       my ($hash, $name, $what, $value) = @_;
-       if ($what eq 'url') {
-               # Having more than one is Ok -- it is used for push.
-               if (! exists $hash->{'URL'}) {
-                       $hash->{$name}{'URL'} = $value;
-               }
-       }
-       elsif ($what eq 'fetch') {
-               $hash->{$name}{'FETCH'} ||= [];
-               push @{$hash->{$name}{'FETCH'}}, $value;
-       }
-       elsif ($what eq 'push') {
-               $hash->{$name}{'PUSH'} ||= [];
-               push @{$hash->{$name}{'PUSH'}}, $value;
-       }
-       if (!exists $hash->{$name}{'SOURCE'}) {
-               $hash->{$name}{'SOURCE'} = 'config';
-       }
-}
-
-sub add_remote_remotes {
-       my ($hash, $file, $name) = @_;
-
-       if (exists $hash->{$name}) {
-               $hash->{$name}{'WARNING'} = 'ignored due to config';
-               return;
-       }
-
-       my $fh;
-       if (!open($fh, '<', $file)) {
-               print STDERR "Warning: cannot open $file\n";
-               return;
-       }
-       my $it = { 'SOURCE' => 'remotes' };
-       $hash->{$name} = $it;
-       while (<$fh>) {
-               chomp;
-               if (/^URL:\s*(.*)$/) {
-                       # Having more than one is Ok -- it is used for push.
-                       if (! exists $it->{'URL'}) {
-                               $it->{'URL'} = $1;
-                       }
-               }
-               elsif (/^Push:\s*(.*)$/) {
-                       $it->{'PUSH'} ||= [];
-                       push @{$it->{'PUSH'}}, $1;
-               }
-               elsif (/^Pull:\s*(.*)$/) {
-                       $it->{'FETCH'} ||= [];
-                       push @{$it->{'FETCH'}}, $1;
-               }
-               elsif (/^\#/) {
-                       ; # ignore
-               }
-               else {
-                       print STDERR "Warning: funny line in $file: $_\n";
-               }
-       }
-       close($fh);
-}
-
-sub list_remote {
-       my ($git) = @_;
-       my %seen = ();
-       my @remotes = eval {
-               $git->command(qw(config --get-regexp), '^remote\.');
-       };
-       for (@remotes) {
-               if (/^remote\.(\S+?)\.([^.\s]+)\s+(.*)$/) {
-                       add_remote_config(\%seen, $1, $2, $3);
-               }
-       }
-
-       my $dir = $git->repo_path() . "/remotes";
-       if (opendir(my $dh, $dir)) {
-               local $_;
-               while ($_ = readdir($dh)) {
-                       chomp;
-                       next if (! -f "$dir/$_" || ! -r _);
-                       add_remote_remotes(\%seen, "$dir/$_", $_);
-               }
-       }
-
-       return \%seen;
-}
-
-sub add_branch_config {
-       my ($hash, $name, $what, $value) = @_;
-       if ($what eq 'remote') {
-               if (exists $hash->{$name}{'REMOTE'}) {
-                       print STDERR "Warning: more than one branch.$name.remote\n";
-               }
-               $hash->{$name}{'REMOTE'} = $value;
-       }
-       elsif ($what eq 'merge') {
-               $hash->{$name}{'MERGE'} ||= [];
-               push @{$hash->{$name}{'MERGE'}}, $value;
-       }
-}
-
-sub list_branch {
-       my ($git) = @_;
-       my %seen = ();
-       my @branches = eval {
-               $git->command(qw(config --get-regexp), '^branch\.');
-       };
-       for (@branches) {
-               if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) {
-                       add_branch_config(\%seen, $1, $2, $3);
-               }
-       }
-
-       return \%seen;
-}
-
-my $remote = list_remote($git);
-my $branch = list_branch($git);
-
-sub update_ls_remote {
-       my ($harder, $info) = @_;
-
-       return if (($harder == 0) ||
-                  (($harder == 1) && exists $info->{'LS_REMOTE'}));
-
-       my @ref = map {
-               s|^[0-9a-f]{40}\s+refs/heads/||;
-               $_;
-       } $git->command(qw(ls-remote --heads), $info->{'URL'});
-       $info->{'LS_REMOTE'} = \@ref;
-}
-
-sub list_wildcard_mapping {
-       my ($forced, $ours, $ls) = @_;
-       my %refs;
-       for (@$ls) {
-               $refs{$_} = 01; # bit #0 to say "they have"
-       }
-       for ($git->command('for-each-ref', "refs/remotes/$ours")) {
-               chomp;
-               next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||);
-               next if ($_ eq 'HEAD');
-               $refs{$_} ||= 0;
-               $refs{$_} |= 02; # bit #1 to say "we have"
-       }
-       my (@new, @stale, @tracked);
-       for (sort keys %refs) {
-               my $have = $refs{$_};
-               if ($have == 1) {
-                       push @new, $_;
-               }
-               elsif ($have == 2) {
-                       push @stale, $_;
-               }
-               elsif ($have == 3) {
-                       push @tracked, $_;
-               }
-       }
-       return \@new, \@stale, \@tracked;
-}
-
-sub list_mapping {
-       my ($name, $info) = @_;
-       my $fetch = $info->{'FETCH'};
-       my $ls = $info->{'LS_REMOTE'};
-       my (@new, @stale, @tracked);
-
-       for (@$fetch) {
-               next unless (/(\+)?([^:]+):(.*)/);
-               my ($forced, $theirs, $ours) = ($1, $2, $3);
-               if ($theirs eq 'refs/heads/*' &&
-                   $ours =~ /^refs\/remotes\/(.*)\/\*$/) {
-                       # wildcard mapping
-                       my ($w_new, $w_stale, $w_tracked)
-                               = list_wildcard_mapping($forced, $1, $ls);
-                       push @new, @$w_new;
-                       push @stale, @$w_stale;
-                       push @tracked, @$w_tracked;
-               }
-               elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
-                       print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
-               }
-               elsif ($theirs =~ s|^refs/heads/||) {
-                       if (!grep { $_ eq $theirs } @$ls) {
-                               push @stale, $theirs;
-                       }
-                       elsif ($ours ne '') {
-                               push @tracked, $theirs;
-                       }
-               }
-       }
-       return \@new, \@stale, \@tracked;
-}
-
-sub show_mapping {
-       my ($name, $info) = @_;
-       my ($new, $stale, $tracked) = list_mapping($name, $info);
-       if (@$new) {
-               print "  New remote branches (next fetch will store in remotes/$name)\n";
-               print "    @$new\n";
-       }
-       if (@$stale) {
-               print "  Stale tracking branches in remotes/$name (use 'git remote prune')\n";
-               print "    @$stale\n";
-       }
-       if (@$tracked) {
-               print "  Tracked remote branches\n";
-               print "    @$tracked\n";
-       }
-}
-
-sub prune_remote {
-       my ($name, $ls_remote) = @_;
-       if (!exists $remote->{$name}) {
-               print STDERR "No such remote $name\n";
-               return 1;
-       }
-       my $info = $remote->{$name};
-       update_ls_remote($ls_remote, $info);
-
-       my ($new, $stale, $tracked) = list_mapping($name, $info);
-       my $prefix = "refs/remotes/$name";
-       foreach my $to_prune (@$stale) {
-               my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune");
-               $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]);
-       }
-       return 0;
-}
-
-sub show_remote {
-       my ($name, $ls_remote) = @_;
-       if (!exists $remote->{$name}) {
-               print STDERR "No such remote $name\n";
-               return 1;
-       }
-       my $info = $remote->{$name};
-       update_ls_remote($ls_remote, $info);
-
-       print "* remote $name\n";
-       print "  URL: $info->{'URL'}\n";
-       for my $branchname (sort keys %$branch) {
-               next unless (defined $branch->{$branchname}{'REMOTE'} &&
-                            $branch->{$branchname}{'REMOTE'} eq $name);
-               my @merged = map {
-                       s|^refs/heads/||;
-                       $_;
-               } split(' ',"@{$branch->{$branchname}{'MERGE'}}");
-               next unless (@merged);
-               print "  Remote branch(es) merged with 'git pull' while on branch $branchname\n";
-               print "    @merged\n";
-       }
-       if ($info->{'LS_REMOTE'}) {
-               show_mapping($name, $info);
-       }
-       if ($info->{'PUSH'}) {
-               my @pushed = map {
-                       s|^refs/heads/||;
-                       s|^\+refs/heads/|+|;
-                       s|:refs/heads/|:|;
-                       $_;
-               } @{$info->{'PUSH'}};
-               print "  Local branch(es) pushed with 'git push'\n";
-               print "    @pushed\n";
-       }
-       return 0;
-}
-
-sub add_remote {
-       my ($name, $url, $opts) = @_;
-       if (exists $remote->{$name}) {
-               print STDERR "remote $name already exists.\n";
-               exit(1);
-       }
-       $git->command('config', "remote.$name.url", $url);
-       my $track = $opts->{'track'} || ["*"];
-
-       for (@$track) {
-               $git->command('config', '--add', "remote.$name.fetch",
-                               $opts->{'mirror'} ?
-                               "+refs/$_:refs/$_" :
-                               "+refs/heads/$_:refs/remotes/$name/$_");
-       }
-       if ($opts->{'fetch'}) {
-               $git->command('fetch', $name);
-       }
-       if (exists $opts->{'master'}) {
-               $git->command('symbolic-ref', "refs/remotes/$name/HEAD",
-                             "refs/remotes/$name/$opts->{'master'}");
-       }
-}
-
-sub update_remote {
-       my ($name) = @_;
-       my @remotes;
-
-        my $conf = $git->config("remotes." . $name);
-       if (defined($conf)) {
-               @remotes = split(' ', $conf);
-       } elsif ($name eq 'default') {
-               @remotes = ();
-               for (sort keys %$remote) {
-                       my $do_fetch = $git->config_bool("remote." . $_ .
-                                                   ".skipDefaultUpdate");
-                       unless ($do_fetch) {
-                               push @remotes, $_;
-                       }
-               }
-       } else {
-               print STDERR "Remote group $name does not exists.\n";
-               exit(1);
-       }
-       for (@remotes) {
-               print "Updating $_\n";
-               $git->command('fetch', "$_");
-       }
-}
-
-sub rm_remote {
-       my ($name) = @_;
-       if (!exists $remote->{$name}) {
-               print STDERR "No such remote $name\n";
-               return 1;
-       }
-
-       $git->command('config', '--remove-section', "remote.$name");
-
-       eval {
-           my @trackers = $git->command('config', '--get-regexp',
-                       'branch.*.remote', $name);
-               for (@trackers) {
-                       /^branch\.(.*)?\.remote/;
-                       $git->config('--unset', "branch.$1.remote");
-                       $git->config('--unset', "branch.$1.merge");
-               }
-       };
-
-       my @refs = $git->command('for-each-ref',
-               '--format=%(refname) %(objectname)', "refs/remotes/$name");
-       for (@refs) {
-               my ($ref, $object) = split;
-               $git->command(qw(update-ref -d), $ref, $object);
-       }
-       return 0;
-}
-
-sub add_usage {
-       print STDERR "Usage: git remote add [-f] [-t track]* [-m master] <name> <url>\n";
-       exit(1);
-}
-
-my $VERBOSE = 0;
-@ARGV = grep {
-       if ($_ eq '-v' or $_ eq '--verbose') {
-               $VERBOSE=1;
-               0
-       } else {
-               1
-       }
-} @ARGV;
-
-if (!@ARGV) {
-       for (sort keys %$remote) {
-               print "$_";
-               print "\t$remote->{$_}->{URL}" if $VERBOSE;
-               print "\n";
-       }
-}
-elsif ($ARGV[0] eq 'show') {
-       my $ls_remote = 1;
-       my $i;
-       for ($i = 1; $i < @ARGV; $i++) {
-               if ($ARGV[$i] eq '-n') {
-                       $ls_remote = 0;
-               }
-               else {
-                       last;
-               }
-       }
-       if ($i >= @ARGV) {
-               print STDERR "Usage: git remote show <remote>\n";
-               exit(1);
-       }
-       my $status = 0;
-       for (; $i < @ARGV; $i++) {
-               $status |= show_remote($ARGV[$i], $ls_remote);
-       }
-       exit($status);
-}
-elsif ($ARGV[0] eq 'update') {
-       if (@ARGV <= 1) {
-               update_remote("default");
-               exit(1);
-       }
-       for (my $i = 1; $i < @ARGV; $i++) {
-               update_remote($ARGV[$i]);
-       }
-}
-elsif ($ARGV[0] eq 'prune') {
-       my $ls_remote = 1;
-       my $i;
-       for ($i = 1; $i < @ARGV; $i++) {
-               if ($ARGV[$i] eq '-n') {
-                       $ls_remote = 0;
-               }
-               else {
-                       last;
-               }
-       }
-       if ($i >= @ARGV) {
-               print STDERR "Usage: git remote prune <remote>\n";
-               exit(1);
-       }
-       my $status = 0;
-       for (; $i < @ARGV; $i++) {
-               $status |= prune_remote($ARGV[$i], $ls_remote);
-       }
-        exit($status);
-}
-elsif ($ARGV[0] eq 'add') {
-       my %opts = ();
-       while (1 < @ARGV && $ARGV[1] =~ /^-/) {
-               my $opt = $ARGV[1];
-               shift @ARGV;
-               if ($opt eq '-f' || $opt eq '--fetch') {
-                       $opts{'fetch'} = 1;
-                       next;
-               }
-               if ($opt eq '-t' || $opt eq '--track') {
-                       if (@ARGV < 1) {
-                               add_usage();
-                       }
-                       $opts{'track'} ||= [];
-                       push @{$opts{'track'}}, $ARGV[1];
-                       shift @ARGV;
-                       next;
-               }
-               if ($opt eq '-m' || $opt eq '--master') {
-                       if ((@ARGV < 1) || exists $opts{'master'}) {
-                               add_usage();
-                       }
-                       $opts{'master'} = $ARGV[1];
-                       shift @ARGV;
-                       next;
-               }
-               if ($opt eq '--mirror') {
-                       $opts{'mirror'} = 1;
-                       next;
-               }
-               add_usage();
-       }
-       if (@ARGV != 3) {
-               add_usage();
-       }
-       add_remote($ARGV[1], $ARGV[2], \%opts);
-}
-elsif ($ARGV[0] eq 'rm') {
-       if (@ARGV <= 1) {
-               print STDERR "Usage: git remote rm <remote>\n";
-               exit(1);
-       }
-       exit(rm_remote($ARGV[1]));
-}
-else {
-       print STDERR "Usage: git remote\n";
-       print STDERR "       git remote add <name> <url>\n";
-       print STDERR "       git remote rm <name>\n";
-       print STDERR "       git remote show <name>\n";
-       print STDERR "       git remote prune <name>\n";
-       print STDERR "       git remote update [group]\n";
-       exit(1);
-}
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 558a5ca1073953a752a5727809686612e10ed5d3..5f1d5ef06e2a60e7777d5d5532d3f738ab610440 100755 (executable)
@@ -155,20 +155,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')
@@ -176,15 +162,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'"
 
index 9e2faf90aa1676974234882a2f557c17f7bc59f4..bba22c1321056f5daa58a6913ccc406850700b25 100755 (executable)
@@ -522,7 +522,8 @@ sub cmd_dcommit {
 }
 
 sub cmd_find_rev {
-       my $revision_or_hash = shift;
+       my $revision_or_hash = shift or die "SVN or git revision required ",
+                                           "as a command-line argument\n";
        my $result;
        if ($revision_or_hash =~ /^r\d+$/) {
                my $head = shift;
@@ -957,9 +958,10 @@ sub complete_url_ls_init {
                    "wanted to set to: $gs->{url}\n";
        }
        command_oneline('config', $k, $gs->{url}) unless $orig_url;
-       my $remote_path = "$ra->{svn_path}/$repo_path/*";
+       my $remote_path = "$ra->{svn_path}/$repo_path";
        $remote_path =~ s#/+#/#g;
        $remote_path =~ s#^/##g;
+       $remote_path .= "/*" if $remote_path !~ /\*/;
        my ($n) = ($switch =~ /^--(\w+)/);
        if (length $pfx && $pfx !~ m#/$#) {
                die "--prefix='$pfx' must have a trailing slash '/'\n";
@@ -1540,9 +1542,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 1023b9085989aecfdcdc161232ee22de866d5f57..384148a59fc492d8fb1d6ea4fc4532aa1e5ffc22 100755 (executable)
@@ -23,12 +23,18 @@ USAGE='[--browser=browser|--tool=browser] [--config=conf.var] url/file ...'
 NONGIT_OK=Yes
 . git-sh-setup
 
+valid_custom_tool()
+{
+       browser_cmd="$(git config "browser.$1.cmd")"
+       test -n "$browser_cmd"
+}
+
 valid_tool() {
        case "$1" in
                firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open)
                        ;; # happy
                *)
-                       return 1
+                       valid_custom_tool "$1" || return 1
                        ;;
        esac
 }
@@ -122,7 +128,7 @@ else
 
     init_browser_path "$browser"
 
-    if ! type "$browser_path" > /dev/null 2>&1; then
+    if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then
        die "The browser $browser is not available as '$browser_path'."
     fi
 fi
@@ -157,4 +163,9 @@ case "$browser" in
     dillo)
        "$browser_path" "$@" &
        ;;
+    *)
+       if test -n "$browser_cmd"; then
+           ( eval $browser_cmd "$@" )
+       fi
+       ;;
 esac
diff --git a/git.c b/git.c
index 9cca81a60e6d4f93cf3132b76fd8f147a6a5d98f..13de8018f0451c1e36d8fbc8f8c10233d2b45be9 100644 (file)
--- a/git.c
+++ b/git.c
@@ -334,6 +334,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "push", cmd_push, RUN_SETUP },
                { "read-tree", cmd_read_tree, RUN_SETUP },
                { "reflog", cmd_reflog, RUN_SETUP },
+               { "remote", cmd_remote, RUN_SETUP },
                { "repo-config", cmd_config },
                { "rerere", cmd_rerere, RUN_SETUP },
                { "reset", cmd_reset, RUN_SETUP },
@@ -342,7 +343,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
                { "rm", cmd_rm, RUN_SETUP },
                { "send-pack", cmd_send_pack, RUN_SETUP },
-               { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
+               { "shortlog", cmd_shortlog, USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
                { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
index ae2b80b1083c18c6b0a15959fee304b6c43b3792..f90dfabffa894741f500e513d5380c7752d0195c 100644 (file)
@@ -8,6 +8,7 @@ gitk_libdir   ?= $(sharedir)/gitk/lib
 msgsdir    ?= $(gitk_libdir)/msgs
 msgsdir_SQ  = $(subst ','\'',$(msgsdir))
 
+TCL_PATH ?= tclsh
 TCLTK_PATH ?= wish
 INSTALL ?= install
 RM ?= rm -f
@@ -22,6 +23,9 @@ ifdef NO_MSGFMT
        MSGFMT ?= $(TCL_PATH) po/po2msg.sh
 else
        MSGFMT ?= msgfmt
+       ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
+               MSGFMT := $(TCL_PATH) po/po2msg.sh
+       endif
 endif
 
 PO_TEMPLATE = po/gitk.pot
index f1f21e97bf5e89bd7609cf5b6d1f417e35a43b6c..84ab02e15f6bdecdd04e8835e3aefd0875efea44 100644 (file)
@@ -82,7 +82,7 @@ proc dorunq {} {
 proc start_rev_list {view} {
     global startmsecs
     global commfd leftover tclencoding datemode
-    global viewargs viewfiles commitidx viewcomplete vnextroot
+    global viewargs viewargscmd viewfiles commitidx viewcomplete vnextroot
     global showlocalchanges commitinterest mainheadid
     global progressdirn progresscoords proglastnc curview
 
@@ -90,13 +90,23 @@ proc start_rev_list {view} {
     set commitidx($view) 0
     set viewcomplete($view) 0
     set vnextroot($view) 0
+    set args $viewargs($view)
+    if {$viewargscmd($view) ne {}} {
+       if {[catch {
+           set str [exec sh -c $viewargscmd($view)]
+       } err]} {
+           error_popup "Error executing --argscmd command: $err"
+           exit 1
+       }
+       set args [concat $args [split $str "\n"]]
+    }
     set order "--topo-order"
     if {$datemode} {
        set order "--date-order"
     }
     if {[catch {
        set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \
-                        --boundary $viewargs($view) "--" $viewfiles($view)] r]
+                        --boundary $args "--" $viewfiles($view)] r]
     } err]} {
        error_popup "[mc "Error executing git rev-list:"] $err"
        exit 1
@@ -393,6 +403,9 @@ proc readcommit {id} {
 proc updatecommits {} {
     global viewdata curview phase displayorder ordertok idpending
     global children commitrow selectedline thickerline showneartags
+    global isworktree
+
+    set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
 
     if {$phase ne {}} {
        stop_rev_list
@@ -827,6 +840,7 @@ proc makewindow {} {
     }
     frame .bleft.top
     frame .bleft.mid
+    frame .bleft.bottom
 
     button .bleft.top.search -text [mc "Search"] -command dosearch
     pack .bleft.top.search -side left -padx 5
@@ -854,18 +868,25 @@ proc makewindow {} {
     checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
        -command changeignorespace -variable ignorespace
     pack .bleft.mid.ignspace -side left -padx 5
-    set ctext .bleft.ctext
+    set ctext .bleft.bottom.ctext
     text $ctext -background $bgcolor -foreground $fgcolor \
        -state disabled -font textfont \
-       -yscrollcommand scrolltext -wrap none
+       -yscrollcommand scrolltext -wrap none \
+       -xscrollcommand ".bleft.bottom.sbhorizontal set"
     if {$have_tk85} {
        $ctext conf -tabstyle wordprocessor
     }
-    scrollbar .bleft.sb -command "$ctext yview"
+    scrollbar .bleft.bottom.sb -command "$ctext yview"
+    scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h \
+       -width 10
     pack .bleft.top -side top -fill x
     pack .bleft.mid -side top -fill x
-    pack .bleft.sb -side right -fill y
-    pack $ctext -side left -fill both -expand 1
+    grid $ctext .bleft.bottom.sb -sticky nsew
+    grid .bleft.bottom.sbhorizontal -sticky ew
+    grid columnconfigure .bleft.bottom 0 -weight 1
+    grid rowconfigure .bleft.bottom 0 -weight 1
+    grid rowconfigure .bleft.bottom 1 -weight 0
+    pack .bleft.bottom -side top -fill both -expand 1
     lappend bglist $ctext
     lappend fglist $ctext
 
@@ -930,9 +951,17 @@ proc makewindow {} {
     .pwbottom add .bright
     .ctop add .pwbottom
 
-    # restore window position if known
+    # restore window width & height if known
     if {[info exists geometry(main)]} {
-        wm geometry . "$geometry(main)"
+       if {[scan $geometry(main) "%dx%d" w h] >= 2} {
+           if {$w > [winfo screenwidth .]} {
+               set w [winfo screenwidth .]
+           }
+           if {$h > [winfo screenheight .]} {
+               set h [winfo screenheight .]
+           }
+           wm geometry . "${w}x$h"
+       }
     }
 
     if {[tk windowingsystem] eq {aqua}} {
@@ -1160,9 +1189,10 @@ proc savestuff {w} {
     global canv canv2 canv3 mainfont textfont uifont tabstop
     global stuffsaved findmergefiles maxgraphpct
     global maxwidth showneartags showlocalchanges
-    global viewname viewfiles viewargs viewperm nextviewnum
+    global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
+    global autoselect
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -1177,6 +1207,7 @@ proc savestuff {w} {
        puts $f [list set maxwidth $maxwidth]
        puts $f [list set cmitmode $cmitmode]
        puts $f [list set wrapcomment $wrapcomment]
+       puts $f [list set autoselect $autoselect]
        puts $f [list set showneartags $showneartags]
        puts $f [list set showlocalchanges $showlocalchanges]
        puts $f [list set datetimeformat $datetimeformat]
@@ -1199,7 +1230,7 @@ proc savestuff {w} {
        puts -nonewline $f "set permviews {"
        for {set v 0} {$v < $nextviewnum} {incr v} {
            if {$viewperm($v)} {
-               puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
+               puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
            }
        }
        puts $f "}"
@@ -1850,7 +1881,7 @@ proc shellsplit {str} {
 
 proc newview {ishighlight} {
     global nextviewnum newviewname newviewperm newishighlight
-    global newviewargs revtreeargs
+    global newviewargs revtreeargs viewargscmd newviewargscmd curview
 
     set newishighlight $ishighlight
     set top .gitkview
@@ -1858,16 +1889,17 @@ proc newview {ishighlight} {
        raise $top
        return
     }
-    set newviewname($nextviewnum) "View $nextviewnum"
+    set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
     set newviewperm($nextviewnum) 0
     set newviewargs($nextviewnum) [shellarglist $revtreeargs]
+    set newviewargscmd($nextviewnum) $viewargscmd($curview)
     vieweditor $top $nextviewnum [mc "Gitk view definition"]
 }
 
 proc editview {} {
     global curview
     global viewname viewperm newviewname newviewperm
-    global viewargs newviewargs
+    global viewargs newviewargs viewargscmd newviewargscmd
 
     set top .gitkvedit-$curview
     if {[winfo exists $top]} {
@@ -1877,6 +1909,7 @@ proc editview {} {
     set newviewname($curview) $viewname($curview)
     set newviewperm($curview) $viewperm($curview)
     set newviewargs($curview) [shellarglist $viewargs($curview)]
+    set newviewargscmd($curview) $viewargscmd($curview)
     vieweditor $top $curview "Gitk: edit view $viewname($curview)"
 }
 
@@ -1897,6 +1930,14 @@ proc vieweditor {top n title} {
     entry $top.args -width 50 -textvariable newviewargs($n) \
        -background $bgcolor
     grid $top.args - -sticky ew -padx 5
+
+    message $top.ac -aspect 1000 \
+       -text [mc "Command to generate more commits to include:"]
+    grid $top.ac - -sticky w -pady 5
+    entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
+       -background white
+    grid $top.argscmd - -sticky ew -padx 5
+
     message $top.l -aspect 1000 \
        -text [mc "Enter files and directories to include, one per line:"]
     grid $top.l - -sticky w
@@ -1940,7 +1981,7 @@ proc allviewmenus {n op args} {
 proc newviewok {top n} {
     global nextviewnum newviewperm newviewname newishighlight
     global viewname viewfiles viewperm selectedview curview
-    global viewargs newviewargs viewhlmenu
+    global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
 
     if {[catch {
        set newargs [shellsplit $newviewargs($n)]
@@ -1964,6 +2005,7 @@ proc newviewok {top n} {
        set viewperm($n) $newviewperm($n)
        set viewfiles($n) $files
        set viewargs($n) $newargs
+       set viewargscmd($n) $newviewargscmd($n)
        addviewmenu $n
        if {!$newishighlight} {
            run showview $n
@@ -1980,9 +2022,11 @@ proc newviewok {top n} {
            # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
                # entryconf [list -label $viewname($n) -value $viewname($n)]
        }
-       if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
+       if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
+               $newviewargscmd($n) ne $viewargscmd($n)} {
            set viewfiles($n) $files
            set viewargs($n) $newargs
+           set viewargscmd($n) $newviewargscmd($n)
            if {$curview == $n} {
                run updatecommits
            }
@@ -2058,8 +2102,6 @@ proc showview {n} {
        set ybot [expr {[lindex $span 1] * $ymax}]
        if {$ytop < $y && $y < $ybot} {
            set yscreen [expr {$y - $ytop}]
-       } else {
-           set yscreen [expr {($ybot - $ytop) / 2}]
        }
     } elseif {[info exists pending_select]} {
        set selid $pending_select
@@ -2120,7 +2162,7 @@ proc showview {n} {
     set yf 0
     set row {}
     set selectfirst 0
-    if {$selid ne {} && [info exists commitrow($n,$selid)]} {
+    if {[info exists yscreen] && [info exists commitrow($n,$selid)]} {
        set row $commitrow($n,$selid)
        # try to get the selected row in the same position on the screen
        set ymax [lindex [$canv cget -scrollregion] 3]
@@ -2844,8 +2886,9 @@ proc dohidelocalchanges {} {
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
     global localirow localfrow lserial showlocalchanges
+    global isworktree
 
-    if {!$showlocalchanges} return
+    if {!$showlocalchanges || !$isworktree} return
     incr lserial
     set localfrow -1
     set localirow -1
@@ -4650,6 +4693,7 @@ proc selectline {l isnew} {
     global commentend idtags linknum
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
+    global autoselect
 
     catch {unset pending_select}
     $canv delete hover
@@ -4705,8 +4749,10 @@ proc selectline {l isnew} {
     set currentid $id
     $sha1entry delete 0 end
     $sha1entry insert 0 $id
-    $sha1entry selection from 0
-    $sha1entry selection to end
+    if {$autoselect} {
+       $sha1entry selection from 0
+       $sha1entry selection to end
+    }
     rhighlight_sel $id
 
     $ctext conf -state normal
@@ -5604,7 +5650,7 @@ proc searchmarkvisible {doall} {
 proc scrolltext {f0 f1} {
     global searchstring
 
-    .bleft.sb set $f0 $f1
+    .bleft.bottom.sb set $f0 $f1
     if {$searchstring ne {}} {
        searchmarkvisible 0
     }
@@ -7943,7 +7989,7 @@ proc doprefs {} {
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
     global bgcolor fgcolor ctext diffcolors selectbgcolor
-    global tabstop limitdiffs
+    global tabstop limitdiffs autoselect
 
     set top .gitkprefs
     set prefstop $top
@@ -7973,6 +8019,11 @@ proc doprefs {} {
     checkbutton $top.showlocal.b -variable showlocalchanges
     pack $top.showlocal.b $top.showlocal.l -side left
     grid x $top.showlocal -sticky w
+    frame $top.autoselect
+    label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
+    checkbutton $top.autoselect.b -variable autoselect
+    pack $top.autoselect.b $top.autoselect.l -side left
+    grid x $top.autoselect -sticky w
 
     label $top.ddisp -text [mc "Diff display options"]
     grid $top.ddisp - -sticky w -pady 10
@@ -8463,6 +8514,7 @@ set maxlinelen 200
 set showlocalchanges 1
 set limitdiffs 1
 set datetimeformat "%Y-%m-%d %H:%M:%S"
+set autoselect 1
 
 set colors {green red blue magenta darkgrey brown orange}
 set bgcolor white
@@ -8522,8 +8574,9 @@ set mergeonly 0
 set revtreeargs {}
 set cmdline_files {}
 set i 0
+set revtreeargscmd {}
 foreach arg $argv {
-    switch -- $arg {
+    switch -glob -- $arg {
        "" { }
        "-d" { set datemode 1 }
        "--merge" {
@@ -8534,6 +8587,9 @@ foreach arg $argv {
            set cmdline_files [lrange $argv [expr {$i + 1}] end]
            break
        }
+       "--argscmd=*" {
+           set revtreeargscmd [string range $arg 10 end]
+       }
        default {
            lappend revtreeargs $arg
        }
@@ -8635,6 +8691,7 @@ set highlight_files {}
 set viewfiles(0) {}
 set viewperm(0) 0
 set viewargs(0) {}
+set viewargscmd(0) {}
 
 set cmdlineok 0
 set stopped 0
@@ -8643,6 +8700,7 @@ set patchnum 0
 set localirow -1
 set localfrow -1
 set lserial 0
+set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
 setcoords
 makewindow
 # wait for the window to become visible
@@ -8650,7 +8708,7 @@ tkwait visibility .
 wm title . "[file tail $argv0]: [file tail [pwd]]"
 readrefs
 
-if {$cmdline_files ne {} || $revtreeargs ne {}} {
+if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
     # create a view for the files/dirs specified on the command line
     set curview 1
     set selectedview 1
@@ -8658,6 +8716,7 @@ if {$cmdline_files ne {} || $revtreeargs ne {}} {
     set viewname(1) [mc "Command line"]
     set viewfiles(1) $cmdline_files
     set viewargs(1) $revtreeargs
+    set viewargscmd(1) $revtreeargscmd
     set viewperm(1) 0
     addviewmenu 1
     .bar.view entryconf [mc "Edit view..."] -state normal
@@ -8671,6 +8730,7 @@ if {[info exists permviews]} {
        set viewname($n) [lindex $v 0]
        set viewfiles($n) [lindex $v 1]
        set viewargs($n) [lindex $v 2]
+       set viewargscmd($n) [lindex $v 3]
        set viewperm($n) 1
        addviewmenu $n
     }
diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po
new file mode 100644 (file)
index 0000000..d0f4c2e
--- /dev/null
@@ -0,0 +1,890 @@
+# Translation of gitk
+# Copyright (C) 2005-2008 Paul Mackerras
+# This file is distributed under the same license as the gitk package.
+# Michele Ballabio <barra_cuda@katamail.com>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"PO-Revision-Date: 2008-03-13 17:34+0100\n"
+"Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
+"Language-Team: Italian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:111
+msgid "Error executing git rev-list:"
+msgstr "Errore nell'esecuzione di git rev-list:"
+
+#: gitk:124
+msgid "Reading"
+msgstr "Lettura in corso"
+
+#: gitk:151 gitk:2191
+msgid "Reading commits..."
+msgstr "Lettura delle revisioni in corso..."
+
+#: gitk:275
+msgid "Can't parse git log output:"
+msgstr "Impossibile elaborare i dati di git log:"
+
+#: gitk:386 gitk:2195
+msgid "No commits selected"
+msgstr "Nessuna revisione selezionata"
+
+#: gitk:500
+msgid "No commit information available"
+msgstr "Nessuna informazione disponibile sulle revisioni"
+
+#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+msgid "OK"
+msgstr "OK"
+
+#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
+#: gitk:7924 gitk:8083
+msgid "Cancel"
+msgstr "Annulla"
+
+#: gitk:661
+msgid "File"
+msgstr "File"
+
+#: gitk:663
+msgid "Update"
+msgstr "Aggiorna"
+
+#: gitk:664
+msgid "Reread references"
+msgstr "Rileggi riferimenti"
+
+#: gitk:665
+msgid "List references"
+msgstr "Elenca riferimenti"
+
+#: gitk:666
+msgid "Quit"
+msgstr "Esci"
+
+#: gitk:668
+msgid "Edit"
+msgstr "Modifica"
+
+#: gitk:669
+msgid "Preferences"
+msgstr "Preferenze"
+
+#: gitk:672 gitk:1892
+msgid "View"
+msgstr "Vista"
+
+#: gitk:673
+msgid "New view..."
+msgstr "Nuova vista..."
+
+#: gitk:674 gitk:2133 gitk:8722
+msgid "Edit view..."
+msgstr "Modifica vista..."
+
+#: gitk:676 gitk:2134 gitk:8723
+msgid "Delete view"
+msgstr "Elimina vista"
+
+#: gitk:678
+msgid "All files"
+msgstr "Tutti i file"
+
+#: gitk:682
+msgid "Help"
+msgstr "Aiuto"
+
+#: gitk:683 gitk:1317
+msgid "About gitk"
+msgstr "Informazioni su gitk"
+
+#: gitk:684
+msgid "Key bindings"
+msgstr "Scorciatoie da tastiera"
+
+#: gitk:741
+msgid "SHA1 ID: "
+msgstr "SHA1 ID: "
+
+#: gitk:791
+msgid "Find"
+msgstr "Trova"
+
+#: gitk:792
+msgid "next"
+msgstr "succ"
+
+#: gitk:793
+msgid "prev"
+msgstr "prec"
+
+#: gitk:794
+msgid "commit"
+msgstr "revisione"
+
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+msgid "containing:"
+msgstr "contenente:"
+
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+msgid "touching paths:"
+msgstr "che riguarda i percorsi:"
+
+#: gitk:801 gitk:2436
+msgid "adding/removing string:"
+msgstr "che aggiunge/rimuove la stringa:"
+
+#: gitk:810 gitk:812
+msgid "Exact"
+msgstr "Esatto"
+
+#: gitk:812 gitk:2514 gitk:4274
+msgid "IgnCase"
+msgstr ""
+
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+msgid "Regexp"
+msgstr ""
+
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+msgid "All fields"
+msgstr "Tutti i campi"
+
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+msgid "Headline"
+msgstr "Titolo"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+msgid "Comments"
+msgstr "Commenti"
+
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
+#: gitk:5971
+msgid "Author"
+msgstr "Autore"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+msgid "Committer"
+msgstr "Revisione creata da"
+
+#: gitk:845
+msgid "Search"
+msgstr "Cerca"
+
+#: gitk:852
+msgid "Diff"
+msgstr ""
+
+#: gitk:854
+msgid "Old version"
+msgstr "Vecchia versione"
+
+#: gitk:856
+msgid "New version"
+msgstr "Nuova versione"
+
+#: gitk:858
+msgid "Lines of context"
+msgstr "Linee di contesto"
+
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Ignora modifiche agli spazi"
+
+#: gitk:926
+msgid "Patch"
+msgstr "Modifiche"
+
+#: gitk:928
+msgid "Tree"
+msgstr "Directory"
+
+#: gitk:1053 gitk:1068 gitk:6022
+msgid "Diff this -> selected"
+msgstr "Diff questo -> selezionato"
+
+#: gitk:1055 gitk:1070 gitk:6023
+msgid "Diff selected -> this"
+msgstr "Diff selezionato -> questo"
+
+#: gitk:1057 gitk:1072 gitk:6024
+msgid "Make patch"
+msgstr "Crea patch"
+
+#: gitk:1058 gitk:6162
+msgid "Create tag"
+msgstr "Crea etichetta"
+
+#: gitk:1059 gitk:6255
+msgid "Write commit to file"
+msgstr "Scrivi revisione in un file"
+
+#: gitk:1060 gitk:6309
+msgid "Create new branch"
+msgstr "Crea un nuovo ramo"
+
+#: gitk:1061
+msgid "Cherry-pick this commit"
+msgstr "Porta questa revisione in cima al ramo attuale"
+
+#: gitk:1063
+msgid "Reset HEAD branch to here"
+msgstr "Aggiorna il ramo HEAD a questa revisione"
+
+#: gitk:1079
+msgid "Check out this branch"
+msgstr "Attiva questo ramo"
+
+#: gitk:1081
+msgid "Remove this branch"
+msgstr "Elimina questo ramo"
+
+#: gitk:1087
+msgid "Highlight this too"
+msgstr "Evidenzia anche questo"
+
+#: gitk:1089
+msgid "Highlight this only"
+msgstr "Evidenzia solo questo"
+
+#: gitk:1318
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - un visualizzatore di revisioni per git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
+"License"
+
+#: gitk:1326 gitk:1387 gitk:6581
+msgid "Close"
+msgstr "Chiudi"
+
+#: gitk:1345
+msgid "Gitk key bindings"
+msgstr "Scorciatoie da tastiera di Gitk"
+
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Scorciatoie da tastiera di Gitk:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tEsci"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tVai alla prima revisione"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tVai all'ultima revisione"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tVai più in alto di una revisione"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tVai più in basso di una revisione"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tTorna indietro nella cronologia"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tVai avanti nella cronologia"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tVai più in alto di una pagina nella lista delle revisioni"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tVai più in basso di una pagina nella lista delle revisioni"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tScorri alla fine della lista delle revisioni"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tScorri la lista delle revisioni in alto di una riga"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tScorri la lista delle revisioni in basso di una riga"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tScorri la lista delle revisioni in alto di una pagina"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tScorri la lista delle revisioni in basso di una pagina"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tTrova all'indietro (verso l'alto, revisioni successive)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tTrova in avanti (verso il basso, revisioni precedenti)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tScorri la vista delle differenze in basso di una pagina"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tTrova"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tTrova in avanti"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tTrova in avanti"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tTrova in avanti, o cerca di nuovo"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tTrova all'indietro"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tScorri la vista delle differenze al file successivo"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumenta grandezza carattere"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAumenta grandezza carattere"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tDiminuisci grandezza carattere"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tDiminuisci grandezza carattere"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAggiorna"
+
+#: gitk:1896
+msgid "Gitk view definition"
+msgstr "Scelta vista Gitk"
+
+#: gitk:1921
+msgid "Name"
+msgstr "Nome"
+
+#: gitk:1924
+msgid "Remember this view"
+msgstr "Ricorda questa vista"
+
+#: gitk:1928
+msgid "Commits to include (arguments to git rev-list):"
+msgstr "Revisioni da includere (argomenti di git rev-list):"
+
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Comando che genera altre revisioni da visualizzare:"
+
+#: gitk:1942
+msgid "Enter files and directories to include, one per line:"
+msgstr "Inserire file e directory da includere, uno per riga:"
+
+#: gitk:1989
+msgid "Error in commit selection arguments:"
+msgstr "Errore negli argomenti di selezione delle revisioni:"
+
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+msgid "None"
+msgstr "Nessuno"
+
+#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+msgid "Date"
+msgstr "Data"
+
+#: gitk:2531 gitk:4336
+msgid "CDate"
+msgstr ""
+
+#: gitk:2680 gitk:2685
+msgid "Descendant"
+msgstr "Discendente"
+
+#: gitk:2681
+msgid "Not descendant"
+msgstr "Non discendente"
+
+#: gitk:2688 gitk:2693
+msgid "Ancestor"
+msgstr "Ascendente"
+
+#: gitk:2689
+msgid "Not ancestor"
+msgstr "Non ascendente"
+
+#: gitk:2924
+msgid "Local changes checked in to index but not committed"
+msgstr "Modifiche locali presenti nell'indice ma non nell'archivio"
+
+#: gitk:2954
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Modifiche locali non presenti né nell'archivio né nell'indice"
+
+#: gitk:4305
+msgid "Searching"
+msgstr "Ricerca in corso"
+
+#: gitk:4767
+msgid "Tags:"
+msgstr "Etichette:"
+
+#: gitk:4784 gitk:4790 gitk:5951
+msgid "Parent"
+msgstr "Genitore"
+
+#: gitk:4795
+msgid "Child"
+msgstr "Figlio"
+
+#: gitk:4804
+msgid "Branch"
+msgstr "Ramo"
+
+#: gitk:4807
+msgid "Follows"
+msgstr "Segue"
+
+#: gitk:4810
+msgid "Precedes"
+msgstr "Precede"
+
+#: gitk:5093
+msgid "Error getting merge diffs:"
+msgstr "Errore nella lettura delle differenze di fusione:"
+
+#: gitk:5778
+msgid "Goto:"
+msgstr "Vai a:"
+
+#: gitk:5780
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:5805
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "La SHA1 id abbreviata %s è ambigua"
+
+#: gitk:5817
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "La SHA1 id %s è sconosciuta"
+
+#: gitk:5819
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "L'etichetta/ramo %s è sconosciuto"
+
+#: gitk:5961
+msgid "Children"
+msgstr "Figli"
+
+#: gitk:6018
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Aggiorna il ramo %s a questa revisione"
+
+#: gitk:6049
+msgid "Top"
+msgstr "Inizio"
+
+#: gitk:6050
+msgid "From"
+msgstr "Da"
+
+#: gitk:6055
+msgid "To"
+msgstr "A"
+
+#: gitk:6078
+msgid "Generate patch"
+msgstr "Genera patch"
+
+#: gitk:6080
+msgid "From:"
+msgstr "Da:"
+
+#: gitk:6089
+msgid "To:"
+msgstr "A:"
+
+#: gitk:6098
+msgid "Reverse"
+msgstr "Inverti"
+
+#: gitk:6100 gitk:6269
+msgid "Output file:"
+msgstr "Scrivi sul file:"
+
+#: gitk:6106
+msgid "Generate"
+msgstr "Genera"
+
+#: gitk:6142
+msgid "Error creating patch:"
+msgstr "Errore nella creazione della patch:"
+
+#: gitk:6164 gitk:6257 gitk:6311
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:6173
+msgid "Tag name:"
+msgstr "Nome etichetta:"
+
+#: gitk:6177 gitk:6320
+msgid "Create"
+msgstr "Crea"
+
+#: gitk:6192
+msgid "No tag name specified"
+msgstr "Nessuna etichetta specificata"
+
+#: gitk:6196
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "L'etichetta \"%s\" esiste già"
+
+#: gitk:6202
+msgid "Error creating tag:"
+msgstr "Errore nella creazione dell'etichetta:"
+
+#: gitk:6266
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:6274
+msgid "Write"
+msgstr "Scrivi"
+
+#: gitk:6290
+msgid "Error writing commit:"
+msgstr "Errore nella scrittura della revisione:"
+
+#: gitk:6316
+msgid "Name:"
+msgstr "Nome:"
+
+#: gitk:6335
+msgid "Please specify a name for the new branch"
+msgstr "Specificare un nome per il nuovo ramo"
+
+#: gitk:6364
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?"
+
+#: gitk:6369
+msgid "Cherry-picking"
+msgstr ""
+
+#: gitk:6381
+msgid "No changes committed"
+msgstr "Nessuna modifica archiviata"
+
+#: gitk:6404
+msgid "Confirm reset"
+msgstr "Conferma git reset"
+
+#: gitk:6406
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Aggiornare il ramo %s a %s?"
+
+#: gitk:6410
+msgid "Reset type:"
+msgstr "Tipo di aggiornamento:"
+
+#: gitk:6414
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono"
+
+#: gitk:6417
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice"
+
+#: gitk:6420
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: Aggiorna la directory di lavoro e l'indice\n"
+"(abbandona TUTTE le modifiche locali)"
+
+#: gitk:6436
+msgid "Resetting"
+msgstr "git reset in corso"
+
+#: gitk:6493
+msgid "Checking out"
+msgstr "Attivazione in corso"
+
+#: gitk:6523
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Impossibile cancellare il ramo attualmente attivo"
+
+#: gitk:6529
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Le revisioni nel ramo %s non sono presenti su altri rami.\n"
+"Cancellare il ramo %s?"
+
+#: gitk:6560
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Etichette e rami: %s"
+
+#: gitk:6574
+msgid "Filter"
+msgstr "Filtro"
+
+#: gitk:6868
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Errore nella lettura della topologia delle revisioni: le informazioni sul "
+"ramo e le etichette precedenti e seguenti saranno incomplete."
+
+#: gitk:7852
+msgid "Tag"
+msgstr "Etichetta"
+
+#: gitk:7852
+msgid "Id"
+msgstr "Id"
+
+#: gitk:7892
+msgid "Gitk font chooser"
+msgstr "Scelta caratteri gitk"
+
+#: gitk:7909
+msgid "B"
+msgstr "B"
+
+#: gitk:7912
+msgid "I"
+msgstr "I"
+
+#: gitk:8005
+msgid "Gitk preferences"
+msgstr "Preferenze gitk"
+
+#: gitk:8006
+msgid "Commit list display options"
+msgstr "Opzioni visualizzazione dell'elenco revisioni"
+
+#: gitk:8009
+msgid "Maximum graph width (lines)"
+msgstr "Larghezza massima del grafico (in linee)"
+
+#: gitk:8013
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Larghezza massima del grafico (% del pannello)"
+
+#: gitk:8018
+msgid "Show local changes"
+msgstr "Mostra modifiche locali"
+
+#: gitk:8023
+msgid "Auto-select SHA1"
+msgstr "Seleziona automaticamente SHA1 hash"
+
+#: gitk:8028
+msgid "Diff display options"
+msgstr "Opzioni di visualizzazione delle differenze"
+
+#: gitk:8030
+msgid "Tab spacing"
+msgstr "Spaziatura tabulazioni"
+
+#: gitk:8034
+msgid "Display nearby tags"
+msgstr "Mostra etichette vicine"
+
+#: gitk:8039
+msgid "Limit diffs to listed paths"
+msgstr "Limita le differenze ai percorsi elencati"
+
+#: gitk:8044
+msgid "Colors: press to choose"
+msgstr "Colori: premere per scegliere"
+
+#: gitk:8047
+msgid "Background"
+msgstr "Sfondo"
+
+#: gitk:8051
+msgid "Foreground"
+msgstr "Primo piano"
+
+#: gitk:8055
+msgid "Diff: old lines"
+msgstr "Diff: vecchie linee"
+
+#: gitk:8060
+msgid "Diff: new lines"
+msgstr "Diff: nuove linee"
+
+#: gitk:8065
+msgid "Diff: hunk header"
+msgstr "Diff: intestazione della sezione"
+
+#: gitk:8071
+msgid "Select bg"
+msgstr "Sfondo selezione"
+
+#: gitk:8075
+msgid "Fonts: press to choose"
+msgstr "Carattere: premere per scegliere"
+
+#: gitk:8077
+msgid "Main font"
+msgstr "Carattere principale"
+
+#: gitk:8078
+msgid "Diff display font"
+msgstr "Carattere per differenze"
+
+#: gitk:8079
+msgid "User interface font"
+msgstr "Carattere per interfaccia utente"
+
+#: gitk:8095
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: scegliere un colore per %s"
+
+#: gitk:8476
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Questa versione di Tcl/Tk non può avviare gitk.\n"
+" Gitk richiede Tcl/Tk versione 8.4 o superiore."
+
+#: gitk:8565
+msgid "Cannot find a git repository here."
+msgstr "Archivio git non trovato."
+
+#: gitk:8569
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Directory git \"%s\" non trovata."
+
+#: gitk:8612
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file"
+
+#: gitk:8624
+msgid "Bad arguments to gitk:"
+msgstr "Gitk: argomenti errati:"
+
+#: gitk:8636
+msgid "Couldn't get list of unmerged files:"
+msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
+
+#: gitk:8652
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
+"sono file in attesa di fusione."
+
+#: gitk:8655
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
+"specificati non sono in attesa di fusione."
+
+#: gitk:8716
+msgid "Command line"
+msgstr "Linea di comando"
index 922dee98b91132b4a4aca37a382ae7c9711aa2e8..ec73cb1256ba99a990648c548348ecdc3340a592 100755 (executable)
@@ -5305,51 +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/'/'\\''/;
-               my $pickaxe_flags = $search_use_regexp ? '--pickaxe-regex' : '';
-               open my $fd, "-|", "$git_command rev-list $hash | " .
-                       "$git_command diff-tree -r --stdin -S\'$searchqtext\' $pickaxe_flags";
+               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") .
@@ -5358,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";
        }
 
diff --git a/hash.c b/hash.c
index d9ec82fa663804210e8ef200f2f300e914662f22..1cd4c9d5c0945994b84bb25edd6e4685cf76b5c5 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -9,7 +9,7 @@
  * the existing entry, or the empty slot if none existed. The caller
  * can then look at the (*ptr) to see whether it existed or not.
  */
-static struct hash_table_entry *lookup_hash_entry(unsigned int hash, struct hash_table *table)
+static struct hash_table_entry *lookup_hash_entry(unsigned int hash, const struct hash_table *table)
 {
        unsigned int size = table->size, nr = hash % size;
        struct hash_table_entry *array = table->array;
@@ -66,7 +66,7 @@ static void grow_hash_table(struct hash_table *table)
        free(old_array);
 }
 
-void *lookup_hash(unsigned int hash, struct hash_table *table)
+void *lookup_hash(unsigned int hash, const struct hash_table *table)
 {
        if (!table->array)
                return NULL;
@@ -81,7 +81,7 @@ void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table)
        return insert_hash_entry(hash, ptr, table);
 }
 
-int for_each_hash(struct hash_table *table, int (*fn)(void *))
+int for_each_hash(const struct hash_table *table, int (*fn)(void *))
 {
        int sum = 0;
        unsigned int i;
diff --git a/hash.h b/hash.h
index a8b0fbb5b502669f0e6f5db55e4573b8277c04d2..69e33a47b9861df9ac12c354eae180b4f8fea857 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -28,9 +28,9 @@ struct hash_table {
        struct hash_table_entry *array;
 };
 
-extern void *lookup_hash(unsigned int hash, struct hash_table *table);
+extern void *lookup_hash(unsigned int hash, const struct hash_table *table);
 extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table);
-extern int for_each_hash(struct hash_table *table, int (*fn)(void *));
+extern int for_each_hash(const struct hash_table *table, int (*fn)(void *));
 extern void free_hash(struct hash_table *table);
 
 static inline void init_hash(struct hash_table *table)
diff --git a/help.c b/help.c
index e57a50ed59badbb968ce508b6c1802f1fcb68903..ecaca770d398e8068ec18e9f4c5058044001d4ea 100644 (file)
--- a/help.c
+++ b/help.c
@@ -8,6 +8,12 @@
 #include "exec_cmd.h"
 #include "common-cmds.h"
 #include "parse-options.h"
+#include "run-command.h"
+
+static struct man_viewer_list {
+       void (*exec)(const char *);
+       struct man_viewer_list *next;
+} *man_viewer_list;
 
 enum help_format {
        HELP_FORMAT_MAN,
@@ -42,6 +48,102 @@ static enum help_format parse_help_format(const char *format)
        die("unrecognized help format '%s'", format);
 }
 
+static int check_emacsclient_version(void)
+{
+       struct strbuf buffer = STRBUF_INIT;
+       struct child_process ec_process;
+       const char *argv_ec[] = { "emacsclient", "--version", NULL };
+       int version;
+
+       /* emacsclient prints its version number on stderr */
+       memset(&ec_process, 0, sizeof(ec_process));
+       ec_process.argv = argv_ec;
+       ec_process.err = -1;
+       ec_process.stdout_to_stderr = 1;
+       if (start_command(&ec_process)) {
+               fprintf(stderr, "Failed to start emacsclient.\n");
+               return -1;
+       }
+       strbuf_read(&buffer, ec_process.err, 20);
+       close(ec_process.err);
+
+       /*
+        * Don't bother checking return value, because "emacsclient --version"
+        * seems to always exits with code 1.
+        */
+       finish_command(&ec_process);
+
+       if (prefixcmp(buffer.buf, "emacsclient")) {
+               fprintf(stderr, "Failed to parse emacsclient version.\n");
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_remove(&buffer, 0, strlen("emacsclient"));
+       version = atoi(buffer.buf);
+
+       if (version < 22) {
+               fprintf(stderr,
+                       "emacsclient version '%d' too old (< 22).\n",
+                       version);
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_release(&buffer);
+       return 0;
+}
+
+static void exec_woman_emacs(const char *page)
+{
+       if (!check_emacsclient_version()) {
+               /* This works only with emacsclient version >= 22. */
+               struct strbuf man_page = STRBUF_INIT;
+               strbuf_addf(&man_page, "(woman \"%s\")", page);
+               execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL);
+       }
+}
+
+static void exec_man_konqueror(const char *page)
+{
+       const char *display = getenv("DISPLAY");
+       if (display && *display) {
+               struct strbuf man_page = STRBUF_INIT;
+               strbuf_addf(&man_page, "man:%s(1)", page);
+               execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL);
+       }
+}
+
+static void exec_man_man(const char *page)
+{
+       execlp("man", "man", page, NULL);
+}
+
+static void do_add_man_viewer(void (*exec)(const char *))
+{
+       struct man_viewer_list **p = &man_viewer_list;
+
+       while (*p)
+               p = &((*p)->next);
+       *p = xmalloc(sizeof(**p));
+       (*p)->next = NULL;
+       (*p)->exec = exec;
+}
+
+static int add_man_viewer(const char *value)
+{
+       if (!strcasecmp(value, "man"))
+               do_add_man_viewer(exec_man_man);
+       else if (!strcasecmp(value, "woman"))
+               do_add_man_viewer(exec_woman_emacs);
+       else if (!strcasecmp(value, "konqueror"))
+               do_add_man_viewer(exec_man_konqueror);
+       else
+               warning("'%s': unsupported man viewer.", value);
+
+       return 0;
+}
+
 static int git_help_config(const char *var, const char *value)
 {
        if (!strcmp(var, "help.format")) {
@@ -50,6 +152,11 @@ static int git_help_config(const char *var, const char *value)
                help_format = parse_help_format(value);
                return 0;
        }
+       if (!strcmp(var, "man.viewer")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer(value);
+       }
        return git_default_config(var, value);
 }
 
@@ -347,9 +454,16 @@ static void setup_man_path(void)
 
 static void show_man_page(const char *git_cmd)
 {
+       struct man_viewer_list *viewer;
        const char *page = cmd_to_page(git_cmd);
+
        setup_man_path();
-       execlp("man", "man", page, NULL);
+       for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+       {
+               viewer->exec(page); /* will return when unable */
+       }
+       exec_man_man(page);
+       die("no man viewer handled the request");
 }
 
 static void show_info_page(const char *git_cmd)
diff --git a/http.c b/http.c
index 8e554c09694456acc96dd9ba70c44870cb89f25d..256a5f15f40a8d9389560e1fb08e34a56e9f7140 100644 (file)
--- a/http.c
+++ b/http.c
@@ -284,23 +284,15 @@ void http_init(struct remote *remote)
 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;
        }
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"
diff --git a/ll-merge.c b/ll-merge.c
new file mode 100644 (file)
index 0000000..5ae7433
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Low level 3-way in-core file merge.
+ *
+ * Copyright (c) 2007 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "attr.h"
+#include "xdiff-interface.h"
+#include "run-command.h"
+#include "interpolate.h"
+#include "ll-merge.h"
+
+struct ll_merge_driver;
+
+typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
+                          mmbuffer_t *result,
+                          const char *path,
+                          mmfile_t *orig,
+                          mmfile_t *src1, const char *name1,
+                          mmfile_t *src2, const char *name2,
+                          int virtual_ancestor);
+
+struct ll_merge_driver {
+       const char *name;
+       const char *description;
+       ll_merge_fn fn;
+       const char *recursive;
+       struct ll_merge_driver *next;
+       char *cmdline;
+};
+
+/*
+ * Built-in low-levels
+ */
+static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
+                          mmbuffer_t *result,
+                          const char *path_unused,
+                          mmfile_t *orig,
+                          mmfile_t *src1, const char *name1,
+                          mmfile_t *src2, const char *name2,
+                          int virtual_ancestor)
+{
+       /*
+        * The tentative merge result is "ours" for the final round,
+        * or common ancestor for an internal merge.  Still return
+        * "conflicted merge" status.
+        */
+       mmfile_t *stolen = virtual_ancestor ? orig : src1;
+
+       result->ptr = stolen->ptr;
+       result->size = stolen->size;
+       stolen->ptr = NULL;
+       return 1;
+}
+
+static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
+                       mmbuffer_t *result,
+                       const char *path_unused,
+                       mmfile_t *orig,
+                       mmfile_t *src1, const char *name1,
+                       mmfile_t *src2, const char *name2,
+                       int virtual_ancestor)
+{
+       xpparam_t xpp;
+
+       if (buffer_is_binary(orig->ptr, orig->size) ||
+           buffer_is_binary(src1->ptr, src1->size) ||
+           buffer_is_binary(src2->ptr, src2->size)) {
+               warning("Cannot merge binary files: %s vs. %s\n",
+                       name1, name2);
+               return ll_binary_merge(drv_unused, result,
+                                      path_unused,
+                                      orig, src1, name1,
+                                      src2, name2,
+                                      virtual_ancestor);
+       }
+
+       memset(&xpp, 0, sizeof(xpp));
+       return xdl_merge(orig,
+                        src1, name1,
+                        src2, name2,
+                        &xpp, XDL_MERGE_ZEALOUS,
+                        result);
+}
+
+static int ll_union_merge(const struct ll_merge_driver *drv_unused,
+                         mmbuffer_t *result,
+                         const char *path_unused,
+                         mmfile_t *orig,
+                         mmfile_t *src1, const char *name1,
+                         mmfile_t *src2, const char *name2,
+                         int virtual_ancestor)
+{
+       char *src, *dst;
+       long size;
+       const int marker_size = 7;
+
+       int status = ll_xdl_merge(drv_unused, result, path_unused,
+                                 orig, src1, NULL, src2, NULL,
+                                 virtual_ancestor);
+       if (status <= 0)
+               return status;
+       size = result->size;
+       src = dst = result->ptr;
+       while (size) {
+               char ch;
+               if ((marker_size < size) &&
+                   (*src == '<' || *src == '=' || *src == '>')) {
+                       int i;
+                       ch = *src;
+                       for (i = 0; i < marker_size; i++)
+                               if (src[i] != ch)
+                                       goto not_a_marker;
+                       if (src[marker_size] != '\n')
+                               goto not_a_marker;
+                       src += marker_size + 1;
+                       size -= marker_size + 1;
+                       continue;
+               }
+       not_a_marker:
+               do {
+                       ch = *src++;
+                       *dst++ = ch;
+                       size--;
+               } while (ch != '\n' && size);
+       }
+       result->size = dst - result->ptr;
+       return 0;
+}
+
+#define LL_BINARY_MERGE 0
+#define LL_TEXT_MERGE 1
+#define LL_UNION_MERGE 2
+static struct ll_merge_driver ll_merge_drv[] = {
+       { "binary", "built-in binary merge", ll_binary_merge },
+       { "text", "built-in 3-way text merge", ll_xdl_merge },
+       { "union", "built-in union merge", ll_union_merge },
+};
+
+static void create_temp(mmfile_t *src, char *path)
+{
+       int fd;
+
+       strcpy(path, ".merge_file_XXXXXX");
+       fd = xmkstemp(path);
+       if (write_in_full(fd, src->ptr, src->size) != src->size)
+               die("unable to write temp-file");
+       close(fd);
+}
+
+/*
+ * User defined low-level merge driver support.
+ */
+static int ll_ext_merge(const struct ll_merge_driver *fn,
+                       mmbuffer_t *result,
+                       const char *path,
+                       mmfile_t *orig,
+                       mmfile_t *src1, const char *name1,
+                       mmfile_t *src2, const char *name2,
+                       int virtual_ancestor)
+{
+       char temp[3][50];
+       char cmdbuf[2048];
+       struct interp table[] = {
+               { "%O" },
+               { "%A" },
+               { "%B" },
+       };
+       struct child_process child;
+       const char *args[20];
+       int status, fd, i;
+       struct stat st;
+
+       if (fn->cmdline == NULL)
+               die("custom merge driver %s lacks command line.", fn->name);
+
+       result->ptr = NULL;
+       result->size = 0;
+       create_temp(orig, temp[0]);
+       create_temp(src1, temp[1]);
+       create_temp(src2, temp[2]);
+
+       interp_set_entry(table, 0, temp[0]);
+       interp_set_entry(table, 1, temp[1]);
+       interp_set_entry(table, 2, temp[2]);
+
+       interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
+
+       memset(&child, 0, sizeof(child));
+       child.argv = args;
+       args[0] = "sh";
+       args[1] = "-c";
+       args[2] = cmdbuf;
+       args[3] = NULL;
+
+       status = run_command(&child);
+       if (status < -ERR_RUN_COMMAND_FORK)
+               ; /* failure in run-command */
+       else
+               status = -status;
+       fd = open(temp[1], O_RDONLY);
+       if (fd < 0)
+               goto bad;
+       if (fstat(fd, &st))
+               goto close_bad;
+       result->size = st.st_size;
+       result->ptr = xmalloc(result->size + 1);
+       if (read_in_full(fd, result->ptr, result->size) != result->size) {
+               free(result->ptr);
+               result->ptr = NULL;
+               result->size = 0;
+       }
+ close_bad:
+       close(fd);
+ bad:
+       for (i = 0; i < 3; i++)
+               unlink(temp[i]);
+       return status;
+}
+
+/*
+ * merge.default and merge.driver configuration items
+ */
+static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
+static const char *default_ll_merge;
+
+static int read_merge_config(const char *var, const char *value)
+{
+       struct ll_merge_driver *fn;
+       const char *ep, *name;
+       int namelen;
+
+       if (!strcmp(var, "merge.default")) {
+               if (value)
+                       default_ll_merge = strdup(value);
+               return 0;
+       }
+
+       /*
+        * We are not interested in anything but "merge.<name>.variable";
+        * especially, we do not want to look at variables such as
+        * "merge.summary", "merge.tool", and "merge.verbosity".
+        */
+       if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
+               return 0;
+
+       /*
+        * Find existing one as we might be processing merge.<name>.var2
+        * after seeing merge.<name>.var1.
+        */
+       name = var + 6;
+       namelen = ep - name;
+       for (fn = ll_user_merge; fn; fn = fn->next)
+               if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
+                       break;
+       if (!fn) {
+               fn = xcalloc(1, sizeof(struct ll_merge_driver));
+               fn->name = xmemdupz(name, namelen);
+               fn->fn = ll_ext_merge;
+               *ll_user_merge_tail = fn;
+               ll_user_merge_tail = &(fn->next);
+       }
+
+       ep++;
+
+       if (!strcmp("name", ep)) {
+               if (!value)
+                       return error("%s: lacks value", var);
+               fn->description = strdup(value);
+               return 0;
+       }
+
+       if (!strcmp("driver", ep)) {
+               if (!value)
+                       return error("%s: lacks value", var);
+               /*
+                * merge.<name>.driver specifies the command line:
+                *
+                *      command-line
+                *
+                * The command-line will be interpolated with the following
+                * tokens and is given to the shell:
+                *
+                *    %O - temporary file name for the merge base.
+                *    %A - temporary file name for our version.
+                *    %B - temporary file name for the other branches' version.
+                *
+                * The external merge driver should write the results in the
+                * file named by %A, and signal that it has done with zero exit
+                * status.
+                */
+               fn->cmdline = strdup(value);
+               return 0;
+       }
+
+       if (!strcmp("recursive", ep)) {
+               if (!value)
+                       return error("%s: lacks value", var);
+               fn->recursive = strdup(value);
+               return 0;
+       }
+
+       return 0;
+}
+
+static void initialize_ll_merge(void)
+{
+       if (ll_user_merge_tail)
+               return;
+       ll_user_merge_tail = &ll_user_merge;
+       git_config(read_merge_config);
+}
+
+static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
+{
+       struct ll_merge_driver *fn;
+       const char *name;
+       int i;
+
+       initialize_ll_merge();
+
+       if (ATTR_TRUE(merge_attr))
+               return &ll_merge_drv[LL_TEXT_MERGE];
+       else if (ATTR_FALSE(merge_attr))
+               return &ll_merge_drv[LL_BINARY_MERGE];
+       else if (ATTR_UNSET(merge_attr)) {
+               if (!default_ll_merge)
+                       return &ll_merge_drv[LL_TEXT_MERGE];
+               else
+                       name = default_ll_merge;
+       }
+       else
+               name = merge_attr;
+
+       for (fn = ll_user_merge; fn; fn = fn->next)
+               if (!strcmp(fn->name, name))
+                       return fn;
+
+       for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
+               if (!strcmp(ll_merge_drv[i].name, name))
+                       return &ll_merge_drv[i];
+
+       /* default to the 3-way */
+       return &ll_merge_drv[LL_TEXT_MERGE];
+}
+
+static const char *git_path_check_merge(const char *path)
+{
+       static struct git_attr_check attr_merge_check;
+
+       if (!attr_merge_check.attr)
+               attr_merge_check.attr = git_attr("merge", 5);
+
+       if (git_checkattr(path, 1, &attr_merge_check))
+               return NULL;
+       return attr_merge_check.value;
+}
+
+int ll_merge(mmbuffer_t *result_buf,
+            const char *path,
+            mmfile_t *ancestor,
+            mmfile_t *ours, const char *our_label,
+            mmfile_t *theirs, const char *their_label,
+            int virtual_ancestor)
+{
+       const char *ll_driver_name;
+       const struct ll_merge_driver *driver;
+
+       ll_driver_name = git_path_check_merge(path);
+       driver = find_ll_merge_driver(ll_driver_name);
+
+       if (virtual_ancestor && driver->recursive)
+               driver = find_ll_merge_driver(driver->recursive);
+       return driver->fn(driver, result_buf, path,
+                         ancestor,
+                         ours, our_label,
+                         theirs, their_label, virtual_ancestor);
+}
diff --git a/ll-merge.h b/ll-merge.h
new file mode 100644 (file)
index 0000000..5388422
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Low level 3-way in-core file merge.
+ */
+
+#ifndef LL_MERGE_H
+#define LL_MERGE_H
+
+int ll_merge(mmbuffer_t *result_buf,
+            const char *path,
+            mmfile_t *ancestor,
+            mmfile_t *ours, const char *our_label,
+            mmfile_t *theirs, const char *their_label,
+            int virtual_ancestor);
+
+#endif
index 608f697cf3860245510bff907a643d9b6143e724..5b2963998cc497177d913b6224799531d45dbb4a 100644 (file)
@@ -138,10 +138,14 @@ static int has_non_ascii(const char *s)
 }
 
 void log_write_email_headers(struct rev_info *opt, const char *name,
-                            const char **subject_p, const char **extra_headers_p)
+                            const char **subject_p,
+                            const char **extra_headers_p,
+                            int *need_8bit_cte_p)
 {
        const char *subject = NULL;
        const char *extra_headers = opt->extra_headers;
+
+       *need_8bit_cte_p = 0; /* unknown */
        if (opt->total > 0) {
                static char buffer[64];
                snprintf(buffer, sizeof(buffer),
@@ -169,6 +173,7 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
        if (opt->mime_boundary) {
                static char subject_buffer[1024];
                static char buffer[1024];
+               *need_8bit_cte_p = -1; /* NEVER */
                snprintf(subject_buffer, sizeof(subject_buffer) - 1,
                         "%s"
                         "MIME-Version: 1.0\n"
@@ -212,6 +217,7 @@ void show_log(struct rev_info *opt, const char *sep)
        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
        const char *extra;
        const char *subject = NULL, *extra_headers = opt->extra_headers;
+       int need_8bit_cte = 0;
 
        opt->loginfo = NULL;
        if (!opt->verbose_header) {
@@ -255,7 +261,8 @@ void show_log(struct rev_info *opt, const char *sep)
 
        if (opt->commit_format == CMIT_FMT_EMAIL) {
                log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
-                                       &subject, &extra_headers);
+                                       &subject, &extra_headers,
+                                       &need_8bit_cte);
        } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
                fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
                if (opt->commit_format != CMIT_FMT_ONELINE)
@@ -299,9 +306,11 @@ void show_log(struct rev_info *opt, const char *sep)
         * And then the pretty-printed message itself
         */
        strbuf_init(&msgbuf, 0);
+       if (need_8bit_cte >= 0)
+               need_8bit_cte = has_non_ascii(opt->add_signoff);
        pretty_print_commit(opt->commit_format, commit, &msgbuf,
                            abbrev, subject, extra_headers, opt->date_mode,
-                           has_non_ascii(opt->add_signoff));
+                           need_8bit_cte);
 
        if (opt->add_signoff)
                append_signoff(&msgbuf, opt->add_signoff);
index 0cc9344eabdd0046edf360958376b2037ef643dc..8946ff377ca455dd1c4efaa609ce9af382005836 100644 (file)
@@ -14,6 +14,8 @@ int log_tree_opt_parse(struct rev_info *, const char **, int);
 void show_log(struct rev_info *opt, const char *sep);
 void show_decorations(struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, const char *name,
-                            const char **subject_p, const char **extra_headers_p);
+                            const char **subject_p,
+                            const char **extra_headers_p,
+                            int *need_8bit_cte_p);
 
 #endif
index e08324686cc090fa9bd94d7f069b025454c7acdf..02fc10f7e622ba1c53065e7cf4563ff10af0c41f 100644 (file)
@@ -168,7 +168,13 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsi
        return res;
 }
 
-static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
+static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
+{
+       char *path = xmalloc(traverse_path_len(info, n) + 1);
+       return make_traverse_path(path, info, n);
+}
+
+static void resolve(const struct traverse_info *info, struct name_entry *branch1, struct name_entry *result)
 {
        struct merge_list *orig, *final;
        const char *path;
@@ -177,7 +183,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
        if (!branch1)
                return;
 
-       path = xstrdup(mkpath("%s%s", base, result->path));
+       path = traverse_path(info, result);
        orig = create_entry(2, branch1->mode, branch1->sha1, path);
        final = create_entry(0, result->mode, result->sha1, path);
 
@@ -186,9 +192,8 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
        add_merge_entry(final);
 }
 
-static int unresolved_directory(const char *base, struct name_entry n[3])
+static int unresolved_directory(const struct traverse_info *info, struct name_entry n[3])
 {
-       int baselen, pathlen;
        char *newbase;
        struct name_entry *p;
        struct tree_desc t[3];
@@ -204,13 +209,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
        }
        if (!S_ISDIR(p->mode))
                return 0;
-       baselen = strlen(base);
-       pathlen = tree_entry_len(p->path, p->sha1);
-       newbase = xmalloc(baselen + pathlen + 2);
-       memcpy(newbase, base, baselen);
-       memcpy(newbase + baselen, p->path, pathlen);
-       memcpy(newbase + baselen + pathlen, "/", 2);
-
+       newbase = traverse_path(info, p);
        buf0 = fill_tree_descriptor(t+0, n[0].sha1);
        buf1 = fill_tree_descriptor(t+1, n[1].sha1);
        buf2 = fill_tree_descriptor(t+2, n[2].sha1);
@@ -224,7 +223,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
 }
 
 
-static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry)
+static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry)
 {
        const char *path;
        struct merge_list *link;
@@ -234,17 +233,17 @@ static struct merge_list *link_entry(unsigned stage, const char *base, struct na
        if (entry)
                path = entry->path;
        else
-               path = xstrdup(mkpath("%s%s", base, n->path));
+               path = traverse_path(info, n);
        link = create_entry(stage, n->mode, n->sha1, path);
        link->link = entry;
        return link;
 }
 
-static void unresolved(const char *base, struct name_entry n[3])
+static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 {
        struct merge_list *entry = NULL;
 
-       if (unresolved_directory(base, n))
+       if (unresolved_directory(info, n))
                return;
 
        /*
@@ -252,9 +251,9 @@ static void unresolved(const char *base, struct name_entry n[3])
         * list has the stages in order - link_entry adds new
         * links at the front.
         */
-       entry = link_entry(3, base, n + 2, entry);
-       entry = link_entry(2, base, n + 1, entry);
-       entry = link_entry(1, base, n + 0, entry);
+       entry = link_entry(3, info, n + 2, entry);
+       entry = link_entry(2, info, n + 1, entry);
+       entry = link_entry(1, info, n + 0, entry);
 
        add_merge_entry(entry);
 }
@@ -288,36 +287,41 @@ static void unresolved(const char *base, struct name_entry n[3])
  * The successful merge rules are the same as for the three-way merge
  * in git-read-tree.
  */
-static void threeway_callback(int n, unsigned long mask, struct name_entry *entry, const char *base)
+static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
 {
        /* Same in both? */
        if (same_entry(entry+1, entry+2)) {
                if (entry[0].sha1) {
-                       resolve(base, NULL, entry+1);
-                       return;
+                       resolve(info, NULL, entry+1);
+                       return mask;
                }
        }
 
        if (same_entry(entry+0, entry+1)) {
                if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
-                       resolve(base, entry+1, entry+2);
-                       return;
+                       resolve(info, entry+1, entry+2);
+                       return mask;
                }
        }
 
        if (same_entry(entry+0, entry+2)) {
                if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
-                       resolve(base, NULL, entry+1);
-                       return;
+                       resolve(info, NULL, entry+1);
+                       return mask;
                }
        }
 
-       unresolved(base, entry);
+       unresolved(info, entry);
+       return mask;
 }
 
 static void merge_trees(struct tree_desc t[3], const char *base)
 {
-       traverse_trees(3, t, base, threeway_callback);
+       struct traverse_info info;
+
+       setup_traverse_info(&info, base);
+       info.fn = threeway_callback;
+       traverse_trees(3, t, &info);
 }
 
 static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
index d9562ba5047ff1c9994755a4dbec5162b551c788..8e64316fe002fe90c58de61e4d6558ee4ad87157 100644 (file)
@@ -6,7 +6,8 @@
 
 struct optparse_t {
        const char **argv;
-       int argc;
+       const char **out;
+       int argc, cpidx;
        const char *opt;
 };
 
@@ -159,6 +160,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
                        continue;
 
                rest = skip_prefix(arg, options->long_name);
+               if (options->type == OPTION_ARGUMENT) {
+                       if (!rest)
+                               continue;
+                       if (*rest == '=')
+                               return opterror(options, "takes no value", flags);
+                       if (*rest)
+                               continue;
+                       p->out[p->cpidx++] = arg - 2;
+                       return 0;
+               }
                if (!rest) {
                        /* abbreviated? */
                        if (!strncmp(options->long_name, arg, arg_end - arg)) {
@@ -242,14 +253,15 @@ static NORETURN void usage_with_options_internal(const char * const *,
 int parse_options(int argc, const char **argv, const struct option *options,
                   const char * const usagestr[], int flags)
 {
-       struct optparse_t args = { argv + 1, argc - 1, NULL };
-       int j = 0;
+       struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
 
        for (; args.argc; args.argc--, args.argv++) {
                const char *arg = args.argv[0];
 
                if (*arg != '-' || !arg[1]) {
-                       argv[j++] = args.argv[0];
+                       if (flags & PARSE_OPT_STOP_AT_NON_OPTION)
+                               break;
+                       args.out[args.cpidx++] = args.argv[0];
                        continue;
                }
 
@@ -286,9 +298,9 @@ int parse_options(int argc, const char **argv, const struct option *options,
                        usage_with_options(usagestr, options);
        }
 
-       memmove(argv + j, args.argv, args.argc * sizeof(*argv));
-       argv[j + args.argc] = NULL;
-       return j + args.argc;
+       memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
+       args.out[args.cpidx + args.argc] = NULL;
+       return args.cpidx + args.argc;
 }
 
 #define USAGE_OPTS_WIDTH 24
@@ -328,6 +340,8 @@ void usage_with_options_internal(const char * const *usagestr,
                        pos += fprintf(stderr, "--%s", opts->long_name);
 
                switch (opts->type) {
+               case OPTION_ARGUMENT:
+                       break;
                case OPTION_INTEGER:
                        if (opts->flags & PARSE_OPT_OPTARG)
                                pos += fprintf(stderr, " [<n>]");
index 102ac31fb727acfdc3c2159e1525c7bcca94e1ef..1af62b0485f2760607b7d3bc3e0ebdca09a53ea4 100644 (file)
@@ -4,6 +4,7 @@
 enum parse_opt_type {
        /* special types */
        OPTION_END,
+       OPTION_ARGUMENT,
        OPTION_GROUP,
        /* options with no arguments */
        OPTION_BIT,
@@ -18,6 +19,7 @@ enum parse_opt_type {
 
 enum parse_opt_flags {
        PARSE_OPT_KEEP_DASHDASH = 1,
+       PARSE_OPT_STOP_AT_NON_OPTION = 2,
 };
 
 enum parse_opt_option_flags {
@@ -84,6 +86,7 @@ struct option {
 };
 
 #define OPT_END()                   { OPTION_END }
+#define OPT_ARGUMENT(l, h)          { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) }
 #define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
 #define OPT_BIT(s, l, v, h, b)      { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
 #define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
index 3d83b7ba9e8c934db8d8ecc9d545cab25be89837..92e5cf20fe6e0891eb6cc8f230a0fcd238f2b9f8 100644 (file)
@@ -102,3 +102,33 @@ void print_path_list(const char *text, const struct path_list *p)
        for (i = 0; i < p->nr; i++)
                printf("%s:%p\n", p->items[i].path, p->items[i].util);
 }
+
+struct path_list_item *path_list_append(const char *path, struct path_list *list)
+{
+       ALLOC_GROW(list->items, list->nr + 1, list->alloc);
+       list->items[list->nr].path =
+               list->strdup_paths ? xstrdup(path) : (char *)path;
+       return list->items + list->nr++;
+}
+
+static int cmp_items(const void *a, const void *b)
+{
+       const struct path_list_item *one = a;
+       const struct path_list_item *two = b;
+       return strcmp(one->path, two->path);
+}
+
+void sort_path_list(struct path_list *list)
+{
+       qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
+}
+
+int unsorted_path_list_has_path(struct path_list *list, const char *path)
+{
+       int i;
+       for (i = 0; i < list->nr; i++)
+               if (!strcmp(path, list->items[i].path))
+                       return 1;
+       return 0;
+}
+
index 5931e2cc0ca23bd8b7f7f90d471cf3b8e95df456..ca2cbbaa4dccbf3359155e429231f35f55c5f7e0 100644 (file)
@@ -13,10 +13,16 @@ struct path_list
 };
 
 void print_path_list(const char *text, const struct path_list *p);
+void path_list_clear(struct path_list *list, int free_util);
 
+/* Use these functions only on sorted lists: */
 int path_list_has_path(const struct path_list *list, const char *path);
-void path_list_clear(struct path_list *list, int free_util);
 struct path_list_item *path_list_insert(const char *path, struct path_list *list);
 struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
 
+/* Use these functions only on unsorted lists: */
+struct path_list_item *path_list_append(const char *path, struct path_list *list);
+void sort_path_list(struct path_list *list);
+int unsorted_path_list_has_path(struct path_list *list, const char *path);
+
 #endif /* PATH_LIST_H */
diff --git a/path.c b/path.c
index af2716199b92e920df9fe2021035d319e8cafbc6..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;
index 703f52176bf0b5cbf52cf1f77ba55ba6d87fac94..16bfb86cd3ce6d6b471cdc313114563ca78837dc 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -636,7 +636,7 @@ void pp_title_line(enum cmit_fmt fmt,
                   const char *subject,
                   const char *after_subject,
                   const char *encoding,
-                  int plain_non_ascii)
+                  int need_8bit_cte)
 {
        struct strbuf title;
 
@@ -669,7 +669,7 @@ void pp_title_line(enum cmit_fmt fmt,
        }
        strbuf_addch(sb, '\n');
 
-       if (plain_non_ascii) {
+       if (need_8bit_cte > 0) {
                const char *header_fmt =
                        "MIME-Version: 1.0\n"
                        "Content-Type: text/plain; charset=%s\n"
@@ -718,9 +718,9 @@ void pp_remainder(enum cmit_fmt fmt,
 }
 
 void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
-                                 struct strbuf *sb, int abbrev,
-                                 const char *subject, const char *after_subject,
-                                 enum date_mode dmode, int plain_non_ascii)
+                        struct strbuf *sb, int abbrev,
+                        const char *subject, const char *after_subject,
+                        enum date_mode dmode, int need_8bit_cte)
 {
        unsigned long beginning_of_body;
        int indent = 4;
@@ -746,13 +746,11 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
        if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
                indent = 0;
 
-       /* After-subject is used to pass in Content-Type: multipart
-        * MIME header; in that case we do not have to do the
-        * plaintext content type even if the commit message has
-        * non 7-bit ASCII character.  Otherwise, check if we need
-        * to say this is not a 7-bit ASCII.
+       /*
+        * We need to check and emit Content-type: to mark it
+        * as 8-bit if we haven't done so.
         */
-       if (fmt == CMIT_FMT_EMAIL && !after_subject) {
+       if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
                int i, ch, in_body;
 
                for (in_body = i = 0; (ch = msg[i]); i++) {
@@ -765,7 +763,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
                                        in_body = 1;
                        }
                        else if (non_ascii(ch)) {
-                               plain_non_ascii = 1;
+                               need_8bit_cte = 1;
                                break;
                        }
                }
@@ -790,7 +788,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
        /* These formats treat the title line specially. */
        if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
                pp_title_line(fmt, &msg, sb, subject,
-                             after_subject, encoding, plain_non_ascii);
+                             after_subject, encoding, need_8bit_cte);
 
        beginning_of_body = sb->len;
        if (fmt != CMIT_FMT_ONELINE)
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 657f0c5894c65831b80ceee54d161d0beac1d733..a92b25b59bf0e096942bca126542a1ea411b525b 100644 (file)
@@ -255,13 +255,13 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
        return changed;
 }
 
-static int is_racy_timestamp(struct index_state *istate, struct cache_entry *ce)
+static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
 {
        return (istate->timestamp &&
                ((unsigned int)istate->timestamp) <= ce->ce_mtime);
 }
 
-int ie_match_stat(struct index_state *istate,
+int ie_match_stat(const struct index_state *istate,
                  struct cache_entry *ce, struct stat *st,
                  unsigned int options)
 {
@@ -304,7 +304,7 @@ int ie_match_stat(struct index_state *istate,
        return changed;
 }
 
-int ie_modified(struct index_state *istate,
+int ie_modified(const struct index_state *istate,
                struct cache_entry *ce, struct stat *st, unsigned int options)
 {
        int changed, changed_fs;
@@ -351,6 +351,41 @@ int base_name_compare(const char *name1, int len1, int mode1,
        return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
 }
 
+/*
+ * df_name_compare() is identical to base_name_compare(), except it
+ * compares conflicting directory/file entries as equal. Note that
+ * while a directory name compares as equal to a regular file, they
+ * then individually compare _differently_ to a filename that has
+ * a dot after the basename (because '\0' < '.' < '/').
+ *
+ * This is used by routines that want to traverse the git namespace
+ * but then handle conflicting entries together when possible.
+ */
+int df_name_compare(const char *name1, int len1, int mode1,
+                   const char *name2, int len2, int mode2)
+{
+       int len = len1 < len2 ? len1 : len2, cmp;
+       unsigned char c1, c2;
+
+       cmp = memcmp(name1, name2, len);
+       if (cmp)
+               return cmp;
+       /* Directories and files compare equal (same length, same name) */
+       if (len1 == len2)
+               return 0;
+       c1 = name1[len];
+       if (!c1 && S_ISDIR(mode1))
+               c1 = '/';
+       c2 = name2[len];
+       if (!c2 && S_ISDIR(mode2))
+               c2 = '/';
+       if (c1 == '/' && !c2)
+               return 0;
+       if (c2 == '/' && !c1)
+               return 0;
+       return c1 - c2;
+}
+
 int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
 {
        int len1 = flags1 & CE_NAMEMASK;
@@ -377,7 +412,7 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
        return 0;
 }
 
-int index_name_pos(struct index_state *istate, const char *name, int namelen)
+int index_name_pos(const struct index_state *istate, const char *name, int namelen)
 {
        int first, last;
 
@@ -1166,7 +1201,7 @@ int discard_index(struct index_state *istate)
        return 0;
 }
 
-int unmerged_index(struct index_state *istate)
+int unmerged_index(const struct index_state *istate)
 {
        int i;
        for (i = 0; i < istate->cache_nr; i++) {
@@ -1311,7 +1346,7 @@ static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)
        return ce_write(c, fd, ondisk, size);
 }
 
-int write_index(struct index_state *istate, int newfd)
+int write_index(const struct index_state *istate, int newfd)
 {
        SHA_CTX c;
        struct cache_header hdr;
index de7e18c49e5593f3e9251edd9def7639ac64a2c6..f83ae87e150ff93728da989f1d35ce0ad7c10f60 100644 (file)
@@ -10,7 +10,6 @@
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
 static int deny_non_fast_forwards = 0;
-static int receive_fsck_objects = 1;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
@@ -36,11 +35,6 @@ static int receive_pack_config(const char *var, const char *value)
                return 0;
        }
 
-       if (strcmp(var, "receive.fsckobjects") == 0) {
-               receive_fsck_objects = git_config_bool(var, value);
-               return 0;
-       }
-
        return git_default_config(var, value);
 }
 
@@ -374,13 +368,11 @@ static const char *unpack(void)
                        ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
 
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
-               int code, i = 0;
-               const char *unpacker[4];
-               unpacker[i++] = "unpack-objects";
-               if (receive_fsck_objects)
-                       unpacker[i++] = "--strict";
-               unpacker[i++] = hdr_arg;
-               unpacker[i++] = NULL;
+               int code;
+               const char *unpacker[3];
+               unpacker[0] = "unpack-objects";
+               unpacker[1] = hdr_arg;
+               unpacker[2] = NULL;
                code = run_command_v_opt(unpacker, RUN_GIT_CMD);
                switch (code) {
                case 0:
@@ -401,8 +393,8 @@ static const char *unpack(void)
                        return "unpacker exited with error code";
                }
        } else {
-               const char *keeper[7];
-               int s, status, i = 0;
+               const char *keeper[6];
+               int s, status;
                char keep_arg[256];
                struct child_process ip;
 
@@ -410,14 +402,12 @@ static const char *unpack(void)
                if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                        strcpy(keep_arg + s, "localhost");
 
-               keeper[i++] = "index-pack";
-               keeper[i++] = "--stdin";
-               if (receive_fsck_objects)
-                       keeper[i++] = "--strict";
-               keeper[i++] = "--fix-thin";
-               keeper[i++] = hdr_arg;
-               keeper[i++] = keep_arg;
-               keeper[i++] = NULL;
+               keeper[0] = "index-pack";
+               keeper[1] = "--stdin";
+               keeper[2] = "--fix-thin";
+               keeper[3] = hdr_arg;
+               keeper[4] = keep_arg;
+               keeper[5] = NULL;
                memset(&ip, 0, sizeof(ip));
                ip.argv = keeper;
                ip.out = -1;
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 7e1937286b1cabec84865c7e07e2635feb8bbc70..f3f7375eea6d8c09e4ab6f561be22b08e545efcb 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -357,7 +357,8 @@ static int handle_config(const char *key, const char *value)
                        remote->fetch_tags = -1;
        } else if (!strcmp(subkey, ".proxy")) {
                remote->http_proxy = xstrdup(value);
-       }
+       } else if (!strcmp(subkey, ".skipdefaultupdate"))
+               remote->skip_default_update = 1;
        return 0;
 }
 
index 0f6033fb258c5a44971c7479e1dfe938393ce398..f1dedf15f6ffe607c4ab123286c9c15fa5522790 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -25,6 +25,7 @@ struct remote {
         * 2 to always fetch tags
         */
        int fetch_tags;
+       int skip_default_update;
 
        const char *receivepack;
        const char *uploadpack;
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 8358ba2069746943fcf45d35222d95fab7a8231d..491d2e7ebf19d5c582adbc5ece28d931b09b8a6d 100644 (file)
@@ -407,21 +407,56 @@ static int get_nth_ancestor(const char *name, int len,
                            unsigned char *result, int generation)
 {
        unsigned char sha1[20];
-       int ret = get_sha1_1(name, len, sha1);
+       struct commit *commit;
+       int ret;
+
+       ret = get_sha1_1(name, len, sha1);
        if (ret)
                return ret;
+       commit = lookup_commit_reference(sha1);
+       if (!commit)
+               return -1;
 
        while (generation--) {
-               struct commit *commit = lookup_commit_reference(sha1);
-
-               if (!commit || parse_commit(commit) || !commit->parents)
+               if (parse_commit(commit) || !commit->parents)
                        return -1;
-               hashcpy(sha1, commit->parents->item->object.sha1);
+               commit = commit->parents->item;
        }
-       hashcpy(result, sha1);
+       hashcpy(result, commit->object.sha1);
        return 0;
 }
 
+struct object *peel_to_type(const char *name, int namelen,
+                           struct object *o, enum object_type expected_type)
+{
+       if (name && !namelen)
+               namelen = strlen(name);
+       if (!o) {
+               unsigned char sha1[20];
+               if (get_sha1_1(name, namelen, sha1))
+                       return NULL;
+               o = parse_object(sha1);
+       }
+       while (1) {
+               if (!o || (!o->parsed && !parse_object(o->sha1)))
+                       return NULL;
+               if (o->type == expected_type)
+                       return o;
+               if (o->type == OBJ_TAG)
+                       o = ((struct tag*) o)->tagged;
+               else if (o->type == OBJ_COMMIT)
+                       o = &(((struct commit *) o)->tree->object);
+               else {
+                       if (name)
+                               error("%.*s: expected %s type, but the object "
+                                     "dereferences to %s type",
+                                     namelen, name, typename(expected_type),
+                                     typename(o->type));
+                       return NULL;
+               }
+       }
+}
+
 static int peel_onion(const char *name, int len, unsigned char *sha1)
 {
        unsigned char outer[20];
@@ -473,32 +508,17 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                hashcpy(sha1, o->sha1);
        }
        else {
-               /* At this point, the syntax look correct, so
+               /*
+                * At this point, the syntax look correct, so
                 * if we do not get the needed object, we should
                 * barf.
                 */
-
-               while (1) {
-                       if (!o || (!o->parsed && !parse_object(o->sha1)))
-                               return -1;
-                       if (o->type == expected_type) {
-                               hashcpy(sha1, o->sha1);
-                               return 0;
-                       }
-                       if (o->type == OBJ_TAG)
-                               o = ((struct tag*) o)->tagged;
-                       else if (o->type == OBJ_COMMIT)
-                               o = &(((struct commit *) o)->tree->object);
-                       else
-                               return error("%.*s: expected %s type, but the object dereferences to %s type",
-                                            len, name, typename(expected_type),
-                                            typename(o->type));
-                       if (!o)
-                               return -1;
-                       if (!o->parsed)
-                               if (!parse_object(o->sha1))
-                                       return -1;
+               o = peel_to_type(name, len, o, expected_type);
+               if (o) {
+                       hashcpy(sha1, o->sha1);
+                       return 0;
                }
+               return -1;
        }
        return 0;
 }
@@ -528,9 +548,8 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
        int ret, has_suffix;
        const char *cp;
 
-       /* "name~3" is "name^^^",
-        * "name~" and "name~0" are name -- not "name^0"!
-        * "name^" is not "name^0"; it is "name^1".
+       /*
+        * "name~3" is "name^^^", "name~" is "name~1", and "name^" is "name^1".
         */
        has_suffix = 0;
        for (cp = name + len - 1; name <= cp; cp--) {
@@ -548,11 +567,10 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
                cp++;
                while (cp < name + len)
                        num = num * 10 + *cp++ - '0';
-               if (has_suffix == '^') {
-                       if (!num && len1 == len - 1)
-                               num = 1;
+               if (!num && len1 == len - 1)
+                       num = 1;
+               if (has_suffix == '^')
                        return get_parent(name, len1, sha1, num);
-               }
                /* else if (has_suffix == '~') -- goes without saying */
                return get_nth_ancestor(name, len1, sha1, num);
        }
index cb860296edfab2d117fc8b62505df00a51baed02..8fc39d77cec6168dae930beef785597dace24aa3 100755 (executable)
@@ -5,7 +5,9 @@ test_description='blob conversion via gitattributes'
 . ./test-lib.sh
 
 cat <<\EOF >rot13.sh
-tr '[a-zA-Z]' '[n-za-mN-ZA-M]'
+tr \
+  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
+  'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
 EOF
 chmod +x rot13.sh
 
index 0e2933a984fcf6a23b60e54ed43c411c699d4347..c23f0ace85c83f54fd7562d72af6ef6c1ea2eca3 100755 (executable)
@@ -21,6 +21,9 @@ string options
     --st <st>             get another string (pervert ordering)
     -o <str>              get another string
 
+magic arguments
+    --quux                means --quux
+
 EOF
 
 test_expect_success 'test help' '
@@ -114,4 +117,17 @@ test_expect_success 'detect possible typos' '
        git diff expect.err output.err
 '
 
+cat > expect <<EOF
+boolean: 0
+integer: 0
+string: (not set)
+arg 00: --quux
+EOF
+
+test_expect_success 'keep some options as arguments' '
+       test-parse-options --quux > output 2> output.err &&
+        test ! -s output.err &&
+        git diff expect output
+'
+
 test_done
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
new file mode 100755 (executable)
index 0000000..8c45564
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='read-tree -u --reset'
+
+. ./test-lib.sh
+
+# two-tree test
+
+test_expect_success 'setup' '
+  git init &&
+  mkdir df &&
+  echo content >df/file &&
+  git add df/file &&
+  git commit -m one &&
+  git ls-files >expect &&
+  rm -rf df &&
+  echo content >df &&
+  git add df &&
+  echo content >new &&
+  git add new &&
+  git commit -m two
+'
+
+test_expect_success 'reset should work' '
+  git read-tree -u --reset HEAD^ &&
+  git ls-files >actual &&
+  diff -u expect actual
+'
+
+test_done
index f959aae84630ddbb68304868b1a025b7c2d33d10..73f830db2374e751fb46e25b345e860979b9dd05 100755 (executable)
@@ -175,21 +175,30 @@ test_expect_success 'recover and check' '
 
 '
 
-test_expect_success 'prune --expire' '
-
-       before=$(git count-objects | sed "s/ .*//") &&
-       BLOB=$(echo aleph | git hash-object -w --stdin) &&
-       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-       test -f $BLOB_FILE &&
-       git reset --hard &&
-       git prune --expire=1.hour.ago &&
-       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-       test -f $BLOB_FILE &&
-       test-chmtime -86500 $BLOB_FILE &&
-       git prune --expire 1.day &&
-       test $before = $(git count-objects | sed "s/ .*//") &&
-       ! test -f $BLOB_FILE
+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
 
 '
 
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 3417138a80989620519ac7c8bb48055fd0bdbf50..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_success '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 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 67a70fadabbdcaca15832c86d76f1e194a923a75..ba43f185494630c50fc2a168df8dcd45c0b2421b 100755 (executable)
@@ -32,11 +32,19 @@ test_expect_success 'format with signoff without funny signer name' '
 
 test_expect_success 'format with non ASCII signer name' '
 
-       GIT_COMMITTER_NAME="\e$B$O$^$N\e(B \e$B$U$K$*$&\e(B" \
+       GIT_COMMITTER_NAME="はまの ふにおう" \
        git format-patch -s --stdout -1 >output &&
        grep Content-Type output
 
 '
 
+test_expect_success 'attach and signoff do not duplicate mime headers' '
+
+       GIT_COMMITTER_NAME="はまの ふにおう" \
+       git format-patch -s --stdout -1 --attach >output &&
+       test `grep -ci ^MIME-Version: output` = 1
+
+'
+
 test_done
 
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
new file mode 100755 (executable)
index 0000000..204ba67
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='format-patch mime headers and extra headers do not conflict'
+. ./test-lib.sh
+
+test_expect_success 'create commit with utf-8 body' '
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+       echo more >>file &&
+       git commit -a -m "two
+
+       utf-8 body: ñ"
+'
+
+test_expect_success 'patch has mime headers' '
+       rm -f 0001-two.patch &&
+       git format-patch HEAD^ &&
+       grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+test_expect_success 'patch has mime and extra headers' '
+       rm -f 0001-two.patch &&
+       git config format.headers "x-foo: bar" &&
+       git format-patch HEAD^ &&
+       grep -i "x-foo: bar" 0001-two.patch &&
+       grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+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
index 6d12efb74d8bedede8e476429b2fccd648eb05a3..eef4cafda99729977af9bee2eca83493814faede 100755 (executable)
@@ -47,4 +47,9 @@ EOF
 
 test_expect_success 'shortlog wrapping' 'diff -u expect out'
 
+git log HEAD > log
+GIT_DIR=non-existing git shortlog -w < log > out
+
+test_expect_success 'shortlog from non-git directory' 'diff -u expect out'
+
 test_done
index 6560af756e7b4df0c7777e818edc86854f74b973..47090c4cf528016bf864116232baf29aeb1e4bfe 100644 (file)
@@ -29,4 +29,53 @@ test_expect_success 'prune stale packs' '
 
 '
 
+test_expect_success 'prune --expire' '
+
+       before=$(git count-objects | sed "s/ .*//") &&
+       BLOB=$(echo aleph | git hash-object -w --stdin) &&
+       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test -f $BLOB_FILE &&
+       git prune --expire=1.hour.ago &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test -f $BLOB_FILE &&
+       test-chmtime -86500 $BLOB_FILE &&
+       git prune --expire 1.day &&
+       test $before = $(git count-objects | sed "s/ .*//") &&
+       ! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: implicit prune --expire' '
+
+       before=$(git count-objects | sed "s/ .*//") &&
+       BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test -f $BLOB_FILE &&
+       test-chmtime -$((86400*14-30)) $BLOB_FILE &&
+       git gc &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test -f $BLOB_FILE &&
+       test-chmtime -$((86400*14+1)) $BLOB_FILE &&
+       git gc &&
+       test $before = $(git count-objects | sed "s/ .*//") &&
+       ! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
+
+       git config gc.pruneExpire invalid &&
+       test_must_fail git gc
+
+'
+
+test_expect_success 'gc: start with ok gc.pruneExpire' '
+
+       git config gc.pruneExpire 2.days.ago &&
+       git gc
+
+'
+
 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
index 4fc62f550cbced0f1e257fa5057e43f4c54ed5dd..2822a651b5c434bb9243a6f8f9069a8fee1ad590 100755 (executable)
@@ -10,10 +10,12 @@ setup_repository () {
        git init &&
        >file &&
        git add file &&
+       test_tick &&
        git commit -m "Initial" &&
        git checkout -b side &&
        >elif &&
        git add elif &&
+       test_tick &&
        git commit -m "Second" &&
        git checkout master
        )
@@ -78,6 +80,7 @@ test_expect_success 'add another remote' '
 test_expect_success 'remove remote' '
 (
        cd test &&
+       git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
        git remote rm second
 )
 '
@@ -94,4 +97,144 @@ test_expect_success 'remove remote' '
 )
 '
 
+cat > test/expect << EOF
+* remote origin
+  URL: $(pwd)/one/.git
+  Remote branch merged with 'git pull' while on branch master
+    master
+  New remote branch (next fetch will store in remotes/origin)
+    master
+  Tracked remote branches
+    side master
+EOF
+
+test_expect_success 'show' '
+       (cd test &&
+        git config --add remote.origin.fetch \
+               refs/heads/master:refs/heads/upstream &&
+        git fetch &&
+        git branch -d -r origin/master &&
+        (cd ../one &&
+         echo 1 > file &&
+         test_tick &&
+         git commit -m update file) &&
+        git remote show origin > output &&
+        git diff expect output)
+'
+
+test_expect_success 'prune' '
+       (cd one &&
+        git branch -m side side2) &&
+       (cd test &&
+        git fetch origin &&
+        git remote prune origin &&
+        git rev-parse refs/remotes/origin/side2 &&
+        ! git rev-parse refs/remotes/origin/side)
+'
+
+test_expect_success 'add --mirror && prune' '
+       (mkdir mirror &&
+        cd mirror &&
+        git init &&
+        git remote add --mirror -f origin ../one) &&
+       (cd one &&
+        git branch -m side2 side) &&
+       (cd mirror &&
+        git rev-parse --verify refs/heads/side2 &&
+        ! git rev-parse --verify refs/heads/side &&
+        git fetch origin &&
+        git remote prune origin &&
+        ! git rev-parse --verify refs/heads/side2 &&
+        git rev-parse --verify refs/heads/side)
+'
+
+cat > one/expect << EOF
+  apis/master
+  apis/side
+  drosophila/another
+  drosophila/master
+  drosophila/side
+EOF
+
+test_expect_success 'update' '
+
+       (cd one &&
+        git remote add drosophila ../two &&
+        git remote add apis ../mirror &&
+        git remote update &&
+        git branch -r > output &&
+        git diff expect output)
+
+'
+
+cat > one/expect << EOF
+  drosophila/another
+  drosophila/master
+  drosophila/side
+  manduca/master
+  manduca/side
+  megaloprepus/master
+  megaloprepus/side
+EOF
+
+test_expect_success 'update with arguments' '
+
+       (cd one &&
+        for b in $(git branch -r)
+        do
+               git branch -r -d $b || break
+        done &&
+        git remote add manduca ../mirror &&
+        git remote add megaloprepus ../mirror &&
+        git config remotes.phobaeticus "drosophila megaloprepus" &&
+        git config remotes.titanus manduca &&
+        git remote update phobaeticus titanus &&
+        git branch -r > output &&
+        git diff expect output)
+
+'
+
+cat > one/expect << EOF
+  apis/master
+  apis/side
+  manduca/master
+  manduca/side
+  megaloprepus/master
+  megaloprepus/side
+EOF
+
+test_expect_success 'update default' '
+
+       (cd one &&
+        for b in $(git branch -r)
+        do
+               git branch -r -d $b || break
+        done &&
+        git config remote.drosophila.skipDefaultUpdate true &&
+        git remote update default &&
+        git branch -r > output &&
+        git diff expect output)
+
+'
+
+cat > one/expect << EOF
+  drosophila/another
+  drosophila/master
+  drosophila/side
+EOF
+
+test_expect_success 'update default (overridden, with funny whitespace)' '
+
+       (cd one &&
+        for b in $(git branch -r)
+        do
+               git branch -r -d $b || break
+        done &&
+        git config remotes.default "$(printf "\t drosophila  \n")" &&
+        git remote update default &&
+        git branch -r > output &&
+        git diff expect output)
+
+'
+
 test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
new file mode 100755 (executable)
index 0000000..5bb6b93
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='merge-recursive: handle file mode'
+. ./test-lib.sh
+
+test_expect_success 'mode change in one branch: keep changed version' '
+       : >file1 &&
+       git add file1 &&
+       git commit -m initial &&
+       git checkout -b a1 master &&
+       : >dummy &&
+       git add dummy &&
+       git commit -m a &&
+       git checkout -b b1 master &&
+       chmod +x file1 &&
+       git add file1 &&
+       git commit -m b1 &&
+       git checkout a1 &&
+       git merge-recursive master -- a1 b1 &&
+       test -x file1
+'
+
+test_expect_success 'mode change in both branches: expect conflict' '
+       git reset --hard HEAD &&
+       git checkout -b a2 master &&
+       : >file2 &&
+       H=$(git hash-object file2) &&
+       chmod +x file2 &&
+       git add file2 &&
+       git commit -m a2 &&
+       git checkout -b b2 master &&
+       : >file2 &&
+       git add file2 &&
+       git commit -m b2 &&
+       git checkout a2 &&
+       (
+               git merge-recursive master -- a2 b2
+               test $? = 1
+       ) &&
+       git ls-files -u >actual &&
+       (
+               echo "100755 $H 2       file2"
+               echo "100644 $H 3       file2"
+       ) >expect &&
+       diff -u actual expect &&
+       test -x file2
+'
+
+test_done
index a7557bdc79623dfc7648097985b8d09aea1801df..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";
@@ -95,5 +98,23 @@ 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 c1cec553060a2fd6a4d27191a866b3bf53ba3335..6a74b3acfda6774d7996cf5787e9858654357328 100755 (executable)
@@ -89,6 +89,33 @@ do
        '
 done
 
+test_expect_success 'editor with a space' '
+
+       if echo "echo space > \"\$1\"" > "e space.sh"
+       then
+               chmod a+x "e space.sh" &&
+               GIT_EDITOR="./e\ space.sh" git commit --amend &&
+               test space = "$(git show -s --pretty=format:%s)"
+       else
+               say "Skipping; FS does not support spaces in filenames"
+       fi
+
+'
+
+unset GIT_EDITOR
+test_expect_success 'core.editor with a space' '
+
+       if test -f "e space.sh"
+       then
+               git config core.editor \"./e\ space.sh\" &&
+               git commit --amend &&
+               test space = "$(git show -s --pretty=format:%s)"
+       else
+               say "Skipping; FS does not support spaces in filenames"
+       fi
+
+'
+
 TERM="$OLD_TERM"
 
 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 87a5ea4a6a54d712c0973cd9b8090c3ec83fb828..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
@@ -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() {
index eed8a02c6589bcb9af46188b56054eace3cd4d19..73360d75126af2f231b3cfdad26a47e18fff0a39 100644 (file)
@@ -20,6 +20,8 @@ int main(int argc, const char **argv)
                OPT_STRING(0, "string2", &string, "str", "get another string"),
                OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
                OPT_STRING('o', NULL, &string, "str", "get another string"),
+               OPT_GROUP("magic arguments"),
+               OPT_ARGUMENT("quux", "means --quux"),
                OPT_END(),
        };
        int i;
index 166c1d1d46d954653b50897ca49678bd21f6bdfb..393e0e8fe233ca51dc4c5db94c55e81ee53baa52 100644 (file)
@@ -560,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];
@@ -580,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;
@@ -628,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.
index 142205ddc3e33fb8024171daf4c6b1bee1dba476..02e2aed7737207225f1b96eed774a1b75dd6d8d9 100644 (file)
@@ -62,7 +62,7 @@ void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
 
 static int entry_compare(struct name_entry *a, struct name_entry *b)
 {
-       return base_name_compare(
+       return df_name_compare(
                        a->path, tree_entry_len(a->path, a->sha1), a->mode,
                        b->path, tree_entry_len(b->path, b->sha1), b->mode);
 }
@@ -104,12 +104,48 @@ int tree_entry(struct tree_desc *desc, struct name_entry *entry)
        return 1;
 }
 
-void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback)
+void setup_traverse_info(struct traverse_info *info, const char *base)
 {
+       int pathlen = strlen(base);
+       static struct traverse_info dummy;
+
+       memset(info, 0, sizeof(*info));
+       if (pathlen && base[pathlen-1] == '/')
+               pathlen--;
+       info->pathlen = pathlen ? pathlen + 1 : 0;
+       info->name.path = base;
+       info->name.sha1 = (void *)(base + pathlen + 1);
+       if (pathlen)
+               info->prev = &dummy;
+}
+
+char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
+{
+       int len = tree_entry_len(n->path, n->sha1);
+       int pathlen = info->pathlen;
+
+       path[pathlen + len] = 0;
+       for (;;) {
+               memcpy(path + pathlen, n->path, len);
+               if (!pathlen)
+                       break;
+               path[--pathlen] = '/';
+               n = &info->name;
+               len = tree_entry_len(n->path, n->sha1);
+               info = info->prev;
+               pathlen -= len;
+       }
+       return path;
+}
+
+int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
+{
+       int ret = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
 
        for (;;) {
                unsigned long mask = 0;
+               unsigned long dirmask = 0;
                int i, last;
 
                last = -1;
@@ -134,25 +170,35 @@ void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callb
                                        mask = 0;
                        }
                        mask |= 1ul << i;
+                       if (S_ISDIR(entry[i].mode))
+                               dirmask |= 1ul << i;
                        last = i;
                }
                if (!mask)
                        break;
+               dirmask &= mask;
 
                /*
-                * Update the tree entries we've walked, and clear
-                * all the unused name-entries.
+                * Clear all the unused name-entries.
                 */
                for (i = 0; i < n; i++) {
-                       if (mask & (1ul << i)) {
-                               update_tree_entry(t+i);
+                       if (mask & (1ul << i))
                                continue;
-                       }
                        entry_clear(entry + i);
                }
-               callback(n, mask, entry, base);
+               ret = info->fn(n, mask, dirmask, entry, info);
+               if (ret < 0)
+                       break;
+               if (ret)
+                       mask &= ret;
+               ret = 0;
+               for (i = 0; i < n; i++) {
+                       if (mask & (1ul << i))
+                               update_tree_entry(t + i);
+               }
        }
        free(entry);
+       return ret;
 }
 
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
index db0fbdc701f1ef63cdc1a8b7d5c5e72322f91426..42110a465f9a8c91d1bc643dfae7a9b9c32e3719 100644 (file)
@@ -33,10 +33,27 @@ int tree_entry(struct tree_desc *, struct name_entry *);
 
 void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
 
-typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry *entry, const char *base);
-
-void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback);
+struct traverse_info;
+typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
+int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
+
+struct traverse_info {
+       struct traverse_info *prev;
+       struct name_entry name;
+       int pathlen;
+
+       unsigned long conflicts;
+       traverse_callback_t fn;
+       void *data;
+};
 
 int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
+extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
+extern void setup_traverse_info(struct traverse_info *info, const char *base);
+
+static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
+{
+       return info->pathlen + tree_entry_len(n->path, n->sha1);
+}
 
 #endif
index 3e448d8974eb6d738fec2c35cc5a9ffbc8764411..93019abdc1192fbb4649573ad507205b26652109 100644 (file)
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
 #include "dir.h"
 #include "tree.h"
 #include "progress.h"
 #include "refs.h"
 
-#define DBRT_DEBUG 1
-
-struct tree_entry_list {
-       struct tree_entry_list *next;
-       unsigned int mode;
-       const char *name;
-       const unsigned char *sha1;
-};
-
-static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
-{
-       struct name_entry one;
-       struct tree_entry_list *ret = NULL;
-       struct tree_entry_list **list_p = &ret;
-
-       while (tree_entry(desc, &one)) {
-               struct tree_entry_list *entry;
-
-               entry = xmalloc(sizeof(struct tree_entry_list));
-               entry->name = one.path;
-               entry->sha1 = one.sha1;
-               entry->mode = one.mode;
-               entry->next = NULL;
-
-               *list_p = entry;
-               list_p = &entry->next;
-       }
-       return ret;
-}
-
-static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
+static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
+       unsigned int set, unsigned int clear)
 {
-       int len1 = strlen(name1);
-       int len2 = strlen(name2);
-       int len = len1 < len2 ? len1 : len2;
-       int ret = memcmp(name1, name2, len);
-       unsigned char c1, c2;
-       if (ret)
-               return ret;
-       c1 = name1[len];
-       c2 = name2[len];
-       if (!c1 && dir1)
-               c1 = '/';
-       if (!c2 && dir2)
-               c2 = '/';
-       ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
-       if (c1 && c2 && !ret)
-               ret = len1 - len2;
-       return ret;
-}
-
-static inline void remove_entry(int remove)
-{
-       if (remove >= 0)
-               remove_cache_entry_at(remove);
-}
-
-static int unpack_trees_rec(struct tree_entry_list **posns, int len,
-                           const char *base, struct unpack_trees_options *o,
-                           struct tree_entry_list *df_conflict_list)
-{
-       int remove;
-       int baselen = strlen(base);
-       int src_size = len + 1;
-       int retval = 0;
-
-       do {
-               int i;
-               const char *first;
-               int firstdir = 0;
-               int pathlen;
-               unsigned ce_size;
-               struct tree_entry_list **subposns;
-               struct cache_entry **src;
-               int any_files = 0;
-               int any_dirs = 0;
-               char *cache_name;
-               int ce_stage;
-               int skip_entry = 0;
-
-               /* Find the first name in the input. */
-
-               first = NULL;
-               cache_name = NULL;
-
-               /* Check the cache */
-               if (o->merge && o->pos < active_nr) {
-                       /* This is a bit tricky: */
-                       /* If the index has a subdirectory (with
-                        * contents) as the first name, it'll get a
-                        * filename like "foo/bar". But that's after
-                        * "foo", so the entry in trees will get
-                        * handled first, at which point we'll go into
-                        * "foo", and deal with "bar" from the index,
-                        * because the base will be "foo/". The only
-                        * way we can actually have "foo/bar" first of
-                        * all the things is if the trees don't
-                        * contain "foo" at all, in which case we'll
-                        * handle "foo/bar" without going into the
-                        * directory, but that's fine (and will return
-                        * an error anyway, with the added unknown
-                        * file case.
-                        */
-
-                       cache_name = active_cache[o->pos]->name;
-                       if (strlen(cache_name) > baselen &&
-                           !memcmp(cache_name, base, baselen)) {
-                               cache_name += baselen;
-                               first = cache_name;
-                       } else {
-                               cache_name = NULL;
-                       }
-               }
-
-#if DBRT_DEBUG > 1
-               if (first)
-                       fprintf(stderr, "index %s\n", first);
-#endif
-               for (i = 0; i < len; i++) {
-                       if (!posns[i] || posns[i] == df_conflict_list)
-                               continue;
-#if DBRT_DEBUG > 1
-                       fprintf(stderr, "%d %s\n", i + 1, posns[i]->name);
-#endif
-                       if (!first || entcmp(first, firstdir,
-                                            posns[i]->name,
-                                            S_ISDIR(posns[i]->mode)) > 0) {
-                               first = posns[i]->name;
-                               firstdir = S_ISDIR(posns[i]->mode);
-                       }
-               }
-               /* No name means we're done */
-               if (!first)
-                       goto leave_directory;
-
-               pathlen = strlen(first);
-               ce_size = cache_entry_size(baselen + pathlen);
-
-               src = xcalloc(src_size, sizeof(struct cache_entry *));
-
-               subposns = xcalloc(len, sizeof(struct tree_list_entry *));
-
-               remove = -1;
-               if (cache_name && !strcmp(cache_name, first)) {
-                       any_files = 1;
-                       src[0] = active_cache[o->pos];
-                       remove = o->pos;
-                       if (o->skip_unmerged && ce_stage(src[0]))
-                               skip_entry = 1;
-               }
-
-               for (i = 0; i < len; i++) {
-                       struct cache_entry *ce;
-
-                       if (!posns[i] ||
-                           (posns[i] != df_conflict_list &&
-                            strcmp(first, posns[i]->name))) {
-                               continue;
-                       }
-
-                       if (posns[i] == df_conflict_list) {
-                               src[i + o->merge] = o->df_conflict_entry;
-                               continue;
-                       }
+       unsigned int size = ce_size(ce);
+       struct cache_entry *new = xmalloc(size);
 
-                       if (S_ISDIR(posns[i]->mode)) {
-                               struct tree *tree = lookup_tree(posns[i]->sha1);
-                               struct tree_desc t;
-                               any_dirs = 1;
-                               parse_tree(tree);
-                               init_tree_desc(&t, tree->buffer, tree->size);
-                               subposns[i] = create_tree_entry_list(&t);
-                               posns[i] = posns[i]->next;
-                               src[i + o->merge] = o->df_conflict_entry;
-                               continue;
-                       }
-
-                       if (skip_entry) {
-                               subposns[i] = df_conflict_list;
-                               posns[i] = posns[i]->next;
-                               continue;
-                       }
-
-                       if (!o->merge)
-                               ce_stage = 0;
-                       else if (i + 1 < o->head_idx)
-                               ce_stage = 1;
-                       else if (i + 1 > o->head_idx)
-                               ce_stage = 3;
-                       else
-                               ce_stage = 2;
-
-                       ce = xcalloc(1, ce_size);
-                       ce->ce_mode = create_ce_mode(posns[i]->mode);
-                       ce->ce_flags = create_ce_flags(baselen + pathlen,
-                                                      ce_stage);
-                       memcpy(ce->name, base, baselen);
-                       memcpy(ce->name + baselen, first, pathlen + 1);
-
-                       any_files = 1;
-
-                       hashcpy(ce->sha1, posns[i]->sha1);
-                       src[i + o->merge] = ce;
-                       subposns[i] = df_conflict_list;
-                       posns[i] = posns[i]->next;
-               }
-               if (any_files) {
-                       if (skip_entry) {
-                               o->pos++;
-                               while (o->pos < active_nr &&
-                                      !strcmp(active_cache[o->pos]->name,
-                                              src[0]->name))
-                                       o->pos++;
-                       } else if (o->merge) {
-                               int ret;
-
-#if DBRT_DEBUG > 1
-                               fprintf(stderr, "%s:\n", first);
-                               for (i = 0; i < src_size; i++) {
-                                       fprintf(stderr, " %d ", i);
-                                       if (src[i])
-                                               fprintf(stderr, "%06x %s\n", src[i]->ce_mode, sha1_to_hex(src[i]->sha1));
-                                       else
-                                               fprintf(stderr, "\n");
-                               }
-#endif
-                               ret = o->fn(src, o, remove);
-                               if (ret < 0)
-                                       return ret;
+       clear |= CE_HASHED | CE_UNHASHED;
 
-#if DBRT_DEBUG > 1
-                               fprintf(stderr, "Added %d entries\n", ret);
-#endif
-                               o->pos += ret;
-                       } else {
-                               remove_entry(remove);
-                               for (i = 0; i < src_size; i++) {
-                                       if (src[i]) {
-                                               add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
-                                       }
-                               }
-                       }
-               }
-               if (any_dirs) {
-                       char *newbase = xmalloc(baselen + 2 + pathlen);
-                       memcpy(newbase, base, baselen);
-                       memcpy(newbase + baselen, first, pathlen);
-                       newbase[baselen + pathlen] = '/';
-                       newbase[baselen + pathlen + 1] = '\0';
-                       if (unpack_trees_rec(subposns, len, newbase, o,
-                                            df_conflict_list)) {
-                               retval = -1;
-                               goto leave_directory;
-                       }
-                       free(newbase);
-               }
-               free(subposns);
-               free(src);
-       } while (1);
-
- leave_directory:
-       return retval;
+       memcpy(new, ce, size);
+       new->next = NULL;
+       new->ce_flags = (new->ce_flags & ~clear) | set;
+       add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
 }
 
 /* Unlink the last component and attempt to remove leading
@@ -308,11 +59,12 @@ static void check_updates(struct unpack_trees_options *o)
        unsigned cnt = 0, total = 0;
        struct progress *progress = NULL;
        char last_symlink[PATH_MAX];
+       struct index_state *index = &o->result;
        int i;
 
        if (o->update && o->verbose_update) {
-               for (total = cnt = 0; cnt < active_nr; cnt++) {
-                       struct cache_entry *ce = active_cache[cnt];
+               for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
+                       struct cache_entry *ce = index->cache[cnt];
                        if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
                                total++;
                }
@@ -323,15 +75,15 @@ static void check_updates(struct unpack_trees_options *o)
        }
 
        *last_symlink = '\0';
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < index->cache_nr; i++) {
+               struct cache_entry *ce = index->cache[i];
 
                if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
                        display_progress(progress, ++cnt);
                if (ce->ce_flags & CE_REMOVE) {
                        if (o->update)
                                unlink_entry(ce->name, last_symlink);
-                       remove_cache_entry_at(i);
+                       remove_index_entry_at(&o->result, i);
                        i--;
                        continue;
                }
@@ -346,21 +98,246 @@ static void check_updates(struct unpack_trees_options *o)
        stop_progress(&progress);
 }
 
-int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
+static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
+{
+       int ret = o->fn(src, o);
+       if (ret > 0)
+               ret = 0;
+       return ret;
+}
+
+static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+{
+       struct cache_entry *src[5] = { ce, };
+
+       o->pos++;
+       if (ce_stage(ce)) {
+               if (o->skip_unmerged) {
+                       add_entry(o, ce, 0, 0);
+                       return 0;
+               }
+       }
+       return call_unpack_fn(src, o);
+}
+
+int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
 {
-       struct tree_entry_list **posns;
        int i;
-       struct tree_entry_list df_conflict_list;
+       struct tree_desc t[MAX_UNPACK_TREES];
+       struct traverse_info newinfo;
+       struct name_entry *p;
+
+       p = names;
+       while (!p->mode)
+               p++;
+
+       newinfo = *info;
+       newinfo.prev = info;
+       newinfo.name = *p;
+       newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1;
+       newinfo.conflicts |= df_conflicts;
+
+       for (i = 0; i < n; i++, dirmask >>= 1) {
+               const unsigned char *sha1 = NULL;
+               if (dirmask & 1)
+                       sha1 = names[i].sha1;
+               fill_tree_descriptor(t+i, sha1);
+       }
+       return traverse_trees(n, t, &newinfo);
+}
+
+/*
+ * Compare the traverse-path to the cache entry without actually
+ * having to generate the textual representation of the traverse
+ * path.
+ *
+ * NOTE! This *only* compares up to the size of the traverse path
+ * itself - the caller needs to do the final check for the cache
+ * entry having more data at the end!
+ */
+static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+{
+       int len, pathlen, ce_len;
+       const char *ce_name;
+
+       if (info->prev) {
+               int cmp = do_compare_entry(ce, info->prev, &info->name);
+               if (cmp)
+                       return cmp;
+       }
+       pathlen = info->pathlen;
+       ce_len = ce_namelen(ce);
+
+       /* If ce_len < pathlen then we must have previously hit "name == directory" entry */
+       if (ce_len < pathlen)
+               return -1;
+
+       ce_len -= pathlen;
+       ce_name = ce->name + pathlen;
+
+       len = tree_entry_len(n->path, n->sha1);
+       return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
+}
+
+static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+{
+       int cmp = do_compare_entry(ce, info, n);
+       if (cmp)
+               return cmp;
+
+       /*
+        * Even if the beginning compared identically, the ce should
+        * compare as bigger than a directory leading up to it!
+        */
+       return ce_namelen(ce) > traverse_path_len(info, n);
+}
+
+static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
+{
+       int len = traverse_path_len(info, n);
+       struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
+
+       ce->ce_mode = create_ce_mode(n->mode);
+       ce->ce_flags = create_ce_flags(len, stage);
+       hashcpy(ce->sha1, n->sha1);
+       make_traverse_path(ce->name, info, n);
+
+       return ce;
+}
+
+static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmask, struct cache_entry *src[5],
+       const struct name_entry *names, const struct traverse_info *info)
+{
+       int i;
+       struct unpack_trees_options *o = info->data;
+       unsigned long conflicts;
+
+       /* Do we have *only* directories? Nothing to do */
+       if (mask == dirmask && !src[0])
+               return 0;
+
+       conflicts = info->conflicts;
+       if (o->merge)
+               conflicts >>= 1;
+       conflicts |= dirmask;
+
+       /*
+        * Ok, we've filled in up to any potential index entry in src[0],
+        * now do the rest.
+        */
+       for (i = 0; i < n; i++) {
+               int stage;
+               unsigned int bit = 1ul << i;
+               if (conflicts & bit) {
+                       src[i + o->merge] = o->df_conflict_entry;
+                       continue;
+               }
+               if (!(mask & bit))
+                       continue;
+               if (!o->merge)
+                       stage = 0;
+               else if (i + 1 < o->head_idx)
+                       stage = 1;
+               else if (i + 1 > o->head_idx)
+                       stage = 3;
+               else
+                       stage = 2;
+               src[i + o->merge] = create_ce_entry(info, names + i, stage);
+       }
+
+       if (o->merge)
+               return call_unpack_fn(src, o);
+
+       n += o->merge;
+       for (i = 0; i < n; i++)
+               add_entry(o, src[i], 0, 0);
+       return 0;
+}
+
+static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
+{
+       struct cache_entry *src[5] = { NULL, };
+       struct unpack_trees_options *o = info->data;
+       const struct name_entry *p = names;
+
+       /* Find first entry with a real name (we could use "mask" too) */
+       while (!p->mode)
+               p++;
+
+       /* Are we supposed to look at the index too? */
+       if (o->merge) {
+               while (o->pos < o->src_index->cache_nr) {
+                       struct cache_entry *ce = o->src_index->cache[o->pos];
+                       int cmp = compare_entry(ce, info, p);
+                       if (cmp < 0) {
+                               if (unpack_index_entry(ce, o) < 0)
+                                       return -1;
+                               continue;
+                       }
+                       if (!cmp) {
+                               o->pos++;
+                               if (ce_stage(ce)) {
+                                       /*
+                                        * If we skip unmerged index entries, we'll skip this
+                                        * entry *and* the tree entries associated with it!
+                                        */
+                                       if (o->skip_unmerged) {
+                                               add_entry(o, ce, 0, 0);
+                                               return mask;
+                                       }
+                               }
+                               src[0] = ce;
+                       }
+                       break;
+               }
+       }
+
+       if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
+               return -1;
+
+       /* Now handle any directories.. */
+       if (dirmask) {
+               unsigned long conflicts = mask & ~dirmask;
+               if (o->merge) {
+                       conflicts <<= 1;
+                       if (src[0])
+                               conflicts |= 1;
+               }
+               if (traverse_trees_recursive(n, dirmask, conflicts,
+                                            names, info) < 0)
+                       return -1;
+               return mask;
+       }
+
+       return mask;
+}
+
+static int unpack_failed(struct unpack_trees_options *o, const char *message)
+{
+       discard_index(&o->result);
+       if (!o->gently) {
+               if (message)
+                       return error(message);
+               return -1;
+       }
+       return -1;
+}
+
+int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
+{
        static struct cache_entry *dfc;
 
-       memset(&df_conflict_list, 0, sizeof(df_conflict_list));
-       df_conflict_list.next = &df_conflict_list;
+       if (len > MAX_UNPACK_TREES)
+               die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
        memset(&state, 0, sizeof(state));
        state.base_dir = "";
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
 
+       memset(&o->result, 0, sizeof(o->result));
+       if (o->src_index)
+               o->result.timestamp = o->src_index->timestamp;
        o->merge_size = len;
 
        if (!dfc)
@@ -368,30 +345,33 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->df_conflict_entry = dfc;
 
        if (len) {
-               posns = xmalloc(len * sizeof(struct tree_entry_list *));
-               for (i = 0; i < len; i++)
-                       posns[i] = create_tree_entry_list(t+i);
-
-               if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
-                                    o, &df_conflict_list)) {
-                       if (o->gently) {
-                               discard_cache();
-                               read_cache();
-                       }
-                       return -1;
-               }
+               const char *prefix = o->prefix ? o->prefix : "";
+               struct traverse_info info;
+
+               setup_traverse_info(&info, prefix);
+               info.fn = unpack_callback;
+               info.data = o;
+
+               if (traverse_trees(len, t, &info) < 0)
+                       return unpack_failed(o, NULL);
        }
 
-       if (o->trivial_merges_only && o->nontrivial_merge) {
-               if (o->gently) {
-                       discard_cache();
-                       read_cache();
+       /* Any left-over entries in the index? */
+       if (o->merge) {
+               while (o->pos < o->src_index->cache_nr) {
+                       struct cache_entry *ce = o->src_index->cache[o->pos];
+                       if (unpack_index_entry(ce, o) < 0)
+                               return unpack_failed(o, NULL);
                }
-               return o->gently ? -1 :
-                       error("Merge requires file-level merging");
        }
 
+       if (o->trivial_merges_only && o->nontrivial_merge)
+               return unpack_failed(o, "Merge requires file-level merging");
+
+       o->src_index = NULL;
        check_updates(o);
+       if (o->dst_index)
+               *o->dst_index = o->result;
        return 0;
 }
 
@@ -427,7 +407,7 @@ static int verify_uptodate(struct cache_entry *ce,
                return 0;
 
        if (!lstat(ce->name, &st)) {
-               unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
+               unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID);
                if (!changed)
                        return 0;
                /*
@@ -447,10 +427,10 @@ static int verify_uptodate(struct cache_entry *ce,
                error("Entry '%s' not uptodate. Cannot merge.", ce->name);
 }
 
-static void invalidate_ce_path(struct cache_entry *ce)
+static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
 {
        if (ce)
-               cache_tree_invalidate_path(active_cache_tree, ce->name);
+               cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
 }
 
 /*
@@ -495,12 +475,12 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
         * in that directory.
         */
        namelen = strlen(ce->name);
-       pos = cache_name_pos(ce->name, namelen);
+       pos = index_name_pos(o->src_index, ce->name, namelen);
        if (0 <= pos)
                return cnt; /* we have it as nondirectory */
        pos = -pos - 1;
-       for (i = pos; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = pos; i < o->src_index->cache_nr; i++) {
+               struct cache_entry *ce = o->src_index->cache[i];
                int len = ce_namelen(ce);
                if (len < namelen ||
                    strncmp(ce->name, ce->name, namelen) ||
@@ -512,7 +492,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
                if (!ce_stage(ce)) {
                        if (verify_uptodate(ce, o))
                                return -1;
-                       ce->ce_flags |= CE_REMOVE;
+                       add_entry(o, ce, CE_REMOVE, 0);
                }
                cnt++;
        }
@@ -598,9 +578,9 @@ static int verify_absent(struct cache_entry *ce, const char *action,
                 * delete this path, which is in a subdirectory that
                 * is being replaced with a blob.
                 */
-               cnt = cache_name_pos(ce->name, strlen(ce->name));
+               cnt = index_name_pos(&o->result, ce->name, strlen(ce->name));
                if (0 <= cnt) {
-                       struct cache_entry *ce = active_cache[cnt];
+                       struct cache_entry *ce = o->result.cache[cnt];
                        if (ce->ce_flags & CE_REMOVE)
                                return 0;
                }
@@ -615,7 +595,6 @@ static int verify_absent(struct cache_entry *ce, const char *action,
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                struct unpack_trees_options *o)
 {
-       merge->ce_flags |= CE_UPDATE;
        if (old) {
                /*
                 * See if we can re-use the old CE directly?
@@ -629,38 +608,38 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                } else {
                        if (verify_uptodate(old, o))
                                return -1;
-                       invalidate_ce_path(old);
+                       invalidate_ce_path(old, o);
                }
        }
        else {
                if (verify_absent(merge, "overwritten", o))
                        return -1;
-               invalidate_ce_path(merge);
+               invalidate_ce_path(merge, o);
        }
 
-       merge->ce_flags &= ~CE_STAGEMASK;
-       add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+       add_entry(o, merge, CE_UPDATE, CE_STAGEMASK);
        return 1;
 }
 
 static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
                struct unpack_trees_options *o)
 {
-       if (old) {
-               if (verify_uptodate(old, o))
-                       return -1;
-       } else
+       /* Did it exist in the index? */
+       if (!old) {
                if (verify_absent(ce, "removed", o))
                        return -1;
-       ce->ce_flags |= CE_REMOVE;
-       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
-       invalidate_ce_path(ce);
+               return 0;
+       }
+       if (verify_uptodate(old, o))
+               return -1;
+       add_entry(o, ce, CE_REMOVE, 0);
+       invalidate_ce_path(ce, o);
        return 1;
 }
 
 static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+       add_entry(o, ce, 0, 0);
        return 1;
 }
 
@@ -680,9 +659,7 @@ static void show_stage_entry(FILE *o,
 }
 #endif
 
-int threeway_merge(struct cache_entry **stages,
-               struct unpack_trees_options *o,
-               int remove)
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
 {
        struct cache_entry *index;
        struct cache_entry *head;
@@ -759,10 +736,8 @@ int threeway_merge(struct cache_entry **stages,
        }
 
        /* #1 */
-       if (!head && !remote && any_anc_missing) {
-               remove_entry(remove);
+       if (!head && !remote && any_anc_missing)
                return 0;
-       }
 
        /* Under the new "aggressive" rule, we resolve mostly trivial
         * cases that we historically had git-merge-one-file resolve.
@@ -794,10 +769,9 @@ int threeway_merge(struct cache_entry **stages,
                if ((head_deleted && remote_deleted) ||
                    (head_deleted && remote && remote_match) ||
                    (remote_deleted && head && head_match)) {
-                       remove_entry(remove);
                        if (index)
                                return deleted_entry(index, index, o);
-                       else if (ce && !head_deleted) {
+                       if (ce && !head_deleted) {
                                if (verify_absent(ce, "removed", o))
                                        return -1;
                        }
@@ -820,7 +794,6 @@ int threeway_merge(struct cache_entry **stages,
                        return -1;
        }
 
-       remove_entry(remove);
        o->nontrivial_merge = 1;
 
        /* #2, #3, #4, #6, #7, #9, #10, #11. */
@@ -855,9 +828,7 @@ int threeway_merge(struct cache_entry **stages,
  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
  *
  */
-int twoway_merge(struct cache_entry **src,
-               struct unpack_trees_options *o,
-               int remove)
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 {
        struct cache_entry *current = src[0];
        struct cache_entry *oldtree = src[1];
@@ -885,7 +856,6 @@ int twoway_merge(struct cache_entry **src,
                }
                else if (oldtree && !newtree && same(current, oldtree)) {
                        /* 10 or 11 */
-                       remove_entry(remove);
                        return deleted_entry(oldtree, current, o);
                }
                else if (oldtree && newtree &&
@@ -895,7 +865,6 @@ int twoway_merge(struct cache_entry **src,
                }
                else {
                        /* all other failures */
-                       remove_entry(remove);
                        if (oldtree)
                                return o->gently ? -1 : reject_merge(oldtree);
                        if (current)
@@ -907,7 +876,6 @@ int twoway_merge(struct cache_entry **src,
        }
        else if (newtree)
                return merged_entry(newtree, current, o);
-       remove_entry(remove);
        return deleted_entry(oldtree, current, o);
 }
 
@@ -918,8 +886,7 @@ int twoway_merge(struct cache_entry **src,
  * stage0 does not have anything there.
  */
 int bind_merge(struct cache_entry **src,
-               struct unpack_trees_options *o,
-               int remove)
+               struct unpack_trees_options *o)
 {
        struct cache_entry *old = src[0];
        struct cache_entry *a = src[1];
@@ -929,7 +896,7 @@ int bind_merge(struct cache_entry **src,
                             o->merge_size);
        if (a && old)
                return o->gently ? -1 :
-                       error("Entry '%s' overlaps.  Cannot bind.", a->name);
+                       error("Entry '%s' overlaps with '%s'.  Cannot bind.", a->name, old->name);
        if (!a)
                return keep_entry(old, o);
        else
@@ -942,9 +909,7 @@ int bind_merge(struct cache_entry **src,
  * The rule is:
  * - take the stat information from stage0, take the data from stage1
  */
-int oneway_merge(struct cache_entry **src,
-               struct unpack_trees_options *o,
-               int remove)
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 {
        struct cache_entry *old = src[0];
        struct cache_entry *a = src[1];
@@ -953,18 +918,19 @@ int oneway_merge(struct cache_entry **src,
                return error("Cannot do a oneway merge of %d trees",
                             o->merge_size);
 
-       if (!a) {
-               remove_entry(remove);
+       if (!a)
                return deleted_entry(old, old, o);
-       }
+
        if (old && same(old, a)) {
+               int update = 0;
                if (o->reset) {
                        struct stat st;
                        if (lstat(old->name, &st) ||
-                           ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))
-                               old->ce_flags |= CE_UPDATE;
+                           ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
+                               update |= CE_UPDATE;
                }
-               return keep_entry(old, o);
+               add_entry(o, old, update, 0);
+               return 0;
        }
        return merged_entry(a, old, o);
 }
index a2df544d040adc21f7d854ad50c53e61cf74c9ae..50453ed20f755fea2e7138d7f01300b318f28dce 100644 (file)
@@ -1,11 +1,12 @@
 #ifndef UNPACK_TREES_H
 #define UNPACK_TREES_H
 
+#define MAX_UNPACK_TREES 8
+
 struct unpack_trees_options;
 
 typedef int (*merge_fn_t)(struct cache_entry **src,
-               struct unpack_trees_options *options,
-               int remove);
+               struct unpack_trees_options *options);
 
 struct unpack_trees_options {
        int reset;
@@ -28,14 +29,18 @@ struct unpack_trees_options {
 
        struct cache_entry *df_conflict_entry;
        void *unpack_data;
+
+       struct index_state *dst_index;
+       const struct index_state *src_index;
+       struct index_state result;
 };
 
 extern int unpack_trees(unsigned n, struct tree_desc *t,
                struct unpack_trees_options *options);
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
 
 #endif
index e5421db9c52c267eb9908f8a94d28c0c601d5128..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));
@@ -444,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];
@@ -451,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];
@@ -489,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
@@ -506,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) {
@@ -558,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)
@@ -631,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 32d780af1e4f6e701f75d9759dfd4ebec56e6425..b3fd57b79df3513d271c23caf3ab46b3ba405e40 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)
@@ -312,27 +269,14 @@ static void wt_status_print_untracked(struct wt_status *s)
 static void wt_status_print_verbose(struct wt_status *s)
 {
        struct rev_info rev;
-       int saved_stdout;
-
-       fflush(s->fp);
-
-       /* Sigh, the entire diff machinery is hardcoded to output to
-        * stdout.  Do the dup-dance...*/
-       saved_stdout = dup(STDOUT_FILENO);
-       if (saved_stdout < 0 ||dup2(fileno(s->fp), STDOUT_FILENO) < 0)
-               die("couldn't redirect stdout\n");
 
        init_revisions(&rev, NULL);
        setup_revisions(0, NULL, &rev, s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
+       rev.diffopt.file = s->fp;
+       rev.diffopt.close_file = 0;
        run_diff_index(&rev, 1);
-
-       fflush(stdout);
-
-       if (dup2(saved_stdout, STDOUT_FILENO) < 0)
-               die("couldn't restore stdout\n");
-       close(saved_stdout);
 }
 
 void wt_status_print(struct wt_status *s)
index bba236428adb0b34421b9f1b5a3a1728911ee406..61dc5c547019776b971dc89d009f628bbac134fd 100644 (file)
@@ -152,8 +152,8 @@ int read_mmfile(mmfile_t *ptr, const char *filename)
        if ((f = fopen(filename, "rb")) == NULL)
                return error("Could not open %s", filename);
        sz = xsize_t(st.st_size);
-       ptr->ptr = xmalloc(sz);
-       if (fread(ptr->ptr, sz, 1, f) != 1)
+       ptr->ptr = xmalloc(sz ? sz : 1);
+       if (sz && fread(ptr->ptr, sz, 1, f) != 1)
                return error("Could not read %s", filename);
        fclose(f);
        ptr->size = sz;