Merge branch 'lt/preload-lstat'
authorJunio C Hamano <gitster@pobox.com>
Fri, 28 Nov 2008 03:24:13 +0000 (19:24 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 28 Nov 2008 03:24:13 +0000 (19:24 -0800)
* lt/preload-lstat:
Fix index preloading for racy dirty case
Add cache preload facility

60 files changed:
Documentation/RelNotes-1.6.0.4.txt
Documentation/RelNotes-1.6.1.txt
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-cherry-pick.txt
Documentation/git-commit.txt
Documentation/git-gui.txt
Documentation/git-repack.txt
Documentation/git-send-email.txt
Documentation/git-svn.txt
Documentation/gitattributes.txt
Documentation/gitcore-tutorial.txt
Documentation/gitglossary.txt
Documentation/gitk.txt
Documentation/gittutorial-2.txt
Documentation/gittutorial.txt
Documentation/merge-config.txt
Documentation/merge-options.txt
Documentation/rev-list-options.txt
Documentation/user-manual.txt
builtin-branch.c
builtin-checkout.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch.c
builtin-ls-files.c
builtin-merge.c
builtin-remote.c
cache.h
contrib/completion/git-completion.bash
contrib/emacs/git.el
contrib/fast-import/git-p4
git-gui/git-gui.sh
git-gui/lib/diff.tcl
git-gui/lib/option.tcl
git-gui/lib/search.tcl
git-gui/lib/tools.tcl [new file with mode: 0644]
git-gui/lib/tools_dlg.tcl [new file with mode: 0644]
git-gui/po/git-gui.pot
git-pull.sh
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-svn.perl
log-tree.c
parse-options.c
parse-options.h
perl/Git.pm
revision.c
sha1_file.c
t/t4020-diff-external.sh
t/t4030-diff-textconv.sh
t/t5521-pull-options.sh [new file with mode: 0755]
t/t7507-commit-verbose.sh
t/t7701-repack-unpack-unreachable.sh
t/t9001-send-email.sh
t/t9129-git-svn-i18n-commitencoding.sh
t/t9301-fast-export.sh
wt-status.c
xdiff-interface.c
index fba3f30a89e22474ec3df358441cd79d29ff2bec..d522661d315c3aab445bf3aa123bbd0abd9db251 100644 (file)
@@ -30,7 +30,7 @@ Fixes since v1.6.0.3
 * 'git status' incorrectly reported a submodule directory as an untracked
   directory.
 
-* 'git svn' used deprecated 'git-foo' form of subcommand invocaition.
+* 'git svn' used deprecated 'git-foo' form of subcommand invocation.
 
 * 'git update-ref -d' to remove a reference did not honor --no-deref option.
 
index 7fdf83f604ae25f6177a3fc832e00c0ff9505ac3..848541a5add8604e52371ca74a5b1ee611f6c085 100644 (file)
@@ -55,9 +55,9 @@ on.
   to a non-zero value to accept the suggestion when git can uniquely
   guess.
 
-* The packfile machinery hopefully is more robust when dealilng with
+* The packfile machinery hopefully is more robust when dealing with
   corrupt packs if redundant objects involved in the corruption are
-  available elsehwere.
+  available elsewhere.
 
 * "git add -N path..." adds the named paths as an empty blob, so that
   subsequent "git diff" will show a diff as if they are creation events.
@@ -157,7 +157,7 @@ on.
 * "git log" learned "--source" to show what ref each commit was reached
   from.
 
-* "git log" also learned "--simplify-by-decration" to show the
+* "git log" also learned "--simplify-by-decoration" to show the
   birds-eye-view of the topology of the history.
 
 * "git log --pretty=format:" learned "%d" format element that inserts
index 0d96e763ac1dad30ccd42290d1163bd746a07501..b233fe53520ab755902882906f8e76957957b658 100644 (file)
@@ -581,9 +581,6 @@ color.status.<slot>::
        to red). The values of these variables may be specified as in
        color.branch.<slot>.
 
-commit.template::
-       Specify a file to use as the template for new commit messages.
-
 color.ui::
        When set to `always`, always use colors in all git commands which
        are capable of colored output. When false (or `never`), never. When
@@ -591,6 +588,9 @@ color.ui::
        terminal. When more specific variables of color.* are set, they always
        take precedence over this setting. Defaults to false.
 
+commit.template::
+       Specify a file to use as the template for new commit messages.
+
 diff.autorefreshindex::
        When using 'git-diff' to compare with work tree
        files, do not consider stat-only change as changed.
@@ -723,18 +723,6 @@ gc.rerereunresolved::
        kept for this many days when 'git-rerere gc' is run.
        The default is 15 days.  See linkgit:git-rerere[1].
 
-rerere.autoupdate::
-       When set to true, `git-rerere` updates the index with the
-       resulting contents after it cleanly resolves conflicts using
-       previously recorded resolution.  Defaults to false.
-
-rerere.enabled::
-       Activate recording of resolved conflicts, so that identical
-       conflict hunks can be resolved automatically, should they
-       be encountered again.  linkgit:git-rerere[1] command is by
-       default enabled if you create `rr-cache` directory under
-       `$GIT_DIR`, but can be disabled by setting this option to false.
-
 gitcvs.enabled::
        Whether the CVS server interface is enabled for this repository.
        See linkgit:git-cvsserver[1].
@@ -805,6 +793,14 @@ gui.diffcontext::
        Specifies how many context lines should be used in calls to diff
        made by the linkgit:git-gui[1]. The default is "5".
 
+gui.encoding::
+       Specifies the default encoding to use for displaying of
+       file contents in linkgit:git-gui[1] and linkgit:gitk[1].
+       It can be overridden by setting the 'encoding' attribute
+       for relevant files (see linkgit:gitattributes[5]).
+       If this option is not set, the tools default to the
+       locale encoding.
+
 gui.matchtrackingbranch::
        Determines if new branches created with linkgit:git-gui[1] should
        default to tracking remote branches with matching names or
@@ -827,6 +823,22 @@ gui.spellingdictionary::
        the linkgit:git-gui[1]. When set to "none" spell checking is turned
        off.
 
+gui.fastcopyblame::
+       If true, 'git gui blame' uses '-C' instead of '-C -C' for original
+       location detection. It makes blame significantly faster on huge
+       repositories at the expense of less thorough copy detection.
+
+gui.copyblamethreshold::
+       Specifies the threshold to use in 'git gui blame' original location
+       detection, measured in alphanumeric characters. See the
+       linkgit:git-blame[1] manual for more information on copy detection.
+
+gui.blamehistoryctx::
+       Specifies the radius of history context in days to show in
+       linkgit:gitk[1] for the selected commit, when the `Show History
+       Context` menu item is invoked from 'git gui blame'. If this
+       variable is set to zero, the whole history is shown.
+
 help.browser::
        Specify the browser that will be used to display help in the
        'web' format. See linkgit:git-help[1].
@@ -902,6 +914,10 @@ i18n.logOutputEncoding::
        Character encoding the commit messages are converted to when
        running 'git-log' and friends.
 
+imap::
+       The configuration variables in the 'imap' section are described
+       in linkgit:git-imap-send[1].
+
 instaweb.browser::
        Specify the program that will be used to browse your working
        repository in gitweb. See linkgit:git-instaweb[1].
@@ -937,8 +953,6 @@ man.viewer::
        Specify the programs that may be used to display help in the
        'man' format. See linkgit:git-help[1].
 
-include::merge-config.txt[]
-
 man.<tool>.cmd::
        Specify the command to invoke the specified man viewer. The
        specified command is evaluated in shell with the man page
@@ -948,13 +962,7 @@ man.<tool>.path::
        Override the path for the given tool that may be used to
        display help in the 'man' format. See linkgit:git-help[1].
 
-merge.conflictstyle::
-       Specify the style in which conflicted hunks are written out to
-       working tree files upon merge.  The default is "merge", which
-       shows `<<<<<<<` conflict marker, change made by one side,
-       `=======` marker, change made by the other side, and then
-       `>>>>>>>` marker.  An alternate style, "diff3", adds `|||||||`
-       marker and the original text before `=======` marker.
+include::merge-config.txt[]
 
 mergetool.<tool>.path::
        Override the path for the given tool.  This is useful in case
@@ -1064,6 +1072,41 @@ pull.octopus::
 pull.twohead::
        The default merge strategy to use when pulling a single branch.
 
+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.
+       Defaults to false.
+
+receive.unpackLimit::
+       If the number of objects received in a push is below this
+       limit then the objects will be unpacked into loose object
+       files. However if the number of received objects equals or
+       exceeds this limit then the received pack will be stored as
+       a pack, after adding any missing delta bases.  Storing the
+       pack from a push can make the push operation complete faster,
+       especially on slow filesystems.  If not set, the value of
+       `transfer.unpackLimit` is used instead.
+
+receive.denyDeletes::
+       If set to true, git-receive-pack will deny a ref update that deletes
+       the ref. Use this to prevent such a ref deletion via a push.
+
+receive.denyCurrentBranch::
+       If set to true or "refuse", receive-pack will deny a ref update
+       to the currently checked out branch of a non-bare repository.
+       Such a push is potentially dangerous because it brings the HEAD
+       out of sync with the index and working tree. If set to "warn",
+       print a warning of such a push to stderr, but allow the push to
+       proceed. If set to false or "ignore", allow such pushes with no
+       message. Defaults to "warn".
+
+receive.denyNonFastForwards::
+       If set to true, git-receive-pack will deny a ref update which is
+       not a fast forward. Use this to prevent such an update via a push,
+       even if that push is forced. This configuration variable is
+       set when initializing a shared repository.
+
 remote.<name>.url::
        The URL of a remote repository.  See linkgit:git-fetch[1] or
        linkgit:git-push[1].
@@ -1113,6 +1156,18 @@ repack.usedeltabaseoffset::
        "false" and repack. Access from old git versions over the
        native protocol are unaffected by this option.
 
+rerere.autoupdate::
+       When set to true, `git-rerere` updates the index with the
+       resulting contents after it cleanly resolves conflicts using
+       previously recorded resolution.  Defaults to false.
+
+rerere.enabled::
+       Activate recording of resolved conflicts, so that identical
+       conflict hunks can be resolved automatically, should they
+       be encountered again.  linkgit:git-rerere[1] command is by
+       default enabled if you create `rr-cache` directory under
+       `$GIT_DIR`, but can be disabled by setting this option to false.
+
 showbranch.default::
        The default set of branches for linkgit:git-show-branch[1].
        See linkgit:git-show-branch[1].
@@ -1149,6 +1204,11 @@ tar.umask::
        archiving user's umask will be used instead.  See umask(2) and
        linkgit:git-archive[1].
 
+transfer.unpackLimit::
+       When `fetch.unpackLimit` or `receive.unpackLimit` are
+       not set, the value of this variable is used instead.
+       The default value is 100.
+
 url.<base>.insteadOf::
        Any URL that starts with this value will be rewritten to
        start, instead, with <base>. In cases where some site serves a
@@ -1177,50 +1237,6 @@ user.signingkey::
        unchanged to gpg's --local-user parameter, so you may specify a key
        using any method that gpg supports.
 
-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.
-       Defaults to false.
-
-receive.unpackLimit::
-       If the number of objects received in a push is below this
-       limit then the objects will be unpacked into loose object
-       files. However if the number of received objects equals or
-       exceeds this limit then the received pack will be stored as
-       a pack, after adding any missing delta bases.  Storing the
-       pack from a push can make the push operation complete faster,
-       especially on slow filesystems.  If not set, the value of
-       `transfer.unpackLimit` is used instead.
-
-receive.denyDeletes::
-       If set to true, git-receive-pack will deny a ref update that deletes
-       the ref. Use this to prevent such a ref deletion via a push.
-
-receive.denyNonFastForwards::
-       If set to true, git-receive-pack will deny a ref update which is
-       not a fast forward. Use this to prevent such an update via a push,
-       even if that push is forced. This configuration variable is
-       set when initializing a shared repository.
-
-receive.denyCurrentBranch::
-       If set to true or "refuse", receive-pack will deny a ref update
-       to the currently checked out branch of a non-bare repository.
-       Such a push is potentially dangerous because it brings the HEAD
-       out of sync with the index and working tree. If set to "warn",
-       print a warning of such a push to stderr, but allow the push to
-       proceed. If set to false or "ignore", allow such pushes with no
-       message. Defaults to "warn".
-
-transfer.unpackLimit::
-       When `fetch.unpackLimit` or `receive.unpackLimit` are
-       not set, the value of this variable is used instead.
-       The default value is 100.
-
 web.browser::
        Specify a web browser that may be used by some commands.
        Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
index 6fc20b0baf1592c88deecc37636b1c644483616b..7c129cb24f0e1c902a81b996ce84ab7e614079c4 100644 (file)
@@ -98,7 +98,7 @@ OPTIONS
        Record only the fact that the path will be added later. An entry
        for the path is placed in the index with no content. This is
        useful for, among other things, showing the unstaged content of
-       such files with 'git diff' and commiting them with 'git commit
+       such files with 'git diff' and committing them with 'git commit
        -a'.
 
 --refresh::
index 837fb08b7971a8b948dd7d039bab4a3c2e54cac7..b764130d26eb750d2b5173dcc98366828e413046 100644 (file)
@@ -55,13 +55,12 @@ OPTIONS
 
 -n::
 --no-commit::
-       Usually the command automatically creates a commit with
-       a commit log message stating which commit was
-       cherry-picked.  This flag applies the change necessary
-       to cherry-pick the named commit to your working tree
-       and the index, but does not make the commit.  In addition,
-       when this option is used, your index does not have to match
-       the HEAD commit.  The cherry-pick is done against the
+       Usually the command automatically creates a commit.
+       This flag applies the change necessary to cherry-pick
+       the named commit to your working tree and the index,
+       but does not make the commit.  In addition, when this
+       option is used, your index does not have to match the
+       HEAD commit.  The cherry-pick is done against the
        beginning state of your index.
 +
 This is useful when cherry-picking more than one commits'
index 2e62165fa9f48e1bff506b75895c081eb922b4ec..6203461f41865b2205520d86d9f3bb85629709cc 100644 (file)
@@ -29,7 +29,8 @@ The content to be added can be specified in several ways:
 
 3. by listing files as arguments to the 'commit' command, in which
    case the commit will ignore changes staged in the index, and instead
-   record the current content of the listed files;
+   record the current content of the listed files (which must already
+   be known to git);
 
 4. by using the -a switch with the 'commit' command to automatically
    "add" changes from all known files (i.e. all files that are already
@@ -94,7 +95,7 @@ OPTIONS
 
 -s::
 --signoff::
-       Add Signed-off-by line by the commiter at the end of the commit
+       Add Signed-off-by line by the committer at the end of the commit
        log message.
 
 -n::
index 0e650f497bd456e633334a91bd929053a08eb0d3..d0bc98b85289b42727afc903a50e9e83d37def35 100644 (file)
@@ -65,9 +65,28 @@ git gui blame v0.99.8 Makefile::
        example the file is read from the object database and not
        the working directory.
 
+git gui blame --line=100 Makefile::
+
+       Loads annotations as described above and automatically
+       scrolls the view to center on line '100'.
+
 git gui citool::
 
        Make one commit and return to the shell when it is complete.
+       This command returns a non-zero exit code if the window was
+       closed in any way other than by making a commit.
+
+git gui citool --amend::
+
+       Automatically enter the 'Amend Last Commit' mode of
+       the interface.
+
+git gui citool --nocommit::
+
+       Behave as normal citool, but instead of making a commit
+       simply terminate with a zero exit code. It still checks
+       that the index does not contain any unmerged entries, so
+       you can use it as a GUI version of linkgit:git-mergetool[1]
 
 git citool::
 
index bbe1485a97979830f883377c2dd55c6a90eb8a09..aaa88526291a26db55c7ebb0833faefda9c7e5a4 100644 (file)
@@ -38,12 +38,11 @@ OPTIONS
        dangling.
 
 -A::
-       Same as `-a`, but any unreachable objects in a previous
-       pack become loose, unpacked objects, instead of being
-       left in the old pack.  Unreachable objects are never
-       intentionally added to a pack, even when repacking.
-       When used with '-d', this option
-       prevents unreachable objects from being immediately
+       Same as `-a`, unless '-d' is used.  Then any unreachable
+       objects in a previous pack become loose, unpacked objects,
+       instead of being left in the old pack.  Unreachable objects
+       are never intentionally added to a pack, even when repacking.
+       This option prevents unreachable objects from being immediately
        deleted by way of being left in the old pack and then
        removed.  Instead, the loose unreachable objects
        will be pruned according to normal expiry rules
index 82f505686e998d36b87bfe2c1a29031449fbdbc6..acf8bf41d6d68ff991f552b5010628cd26a0ac6d 100644 (file)
@@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails
 
 SYNOPSIS
 --------
-'git send-email' [options] <file|directory> [... file|directory]
+'git send-email' [options] <file|directory|rev-list options>...
 
 
 DESCRIPTION
@@ -37,9 +37,23 @@ The --bcc option must be repeated for each user you want on the bcc list.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
+--annotate::
+       Review each patch you're about to send in an editor. The setting
+       'sendemail.multiedit' defines if this will spawn one editor per patch
+       or one for all of them at once.
+
 --compose::
        Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
        introductory message for the patch series.
++
+When compose is in used, git send-email gets less interactive will use the
+values of the headers you set there. If the body of the email (what you type
+after the headers and a blank line) only contains blank (or GIT: prefixed)
+lines, the summary won't be sent, but git-send-email will still use the
+Headers values if you don't removed them.
++
+If it wasn't able to see a header in the summary it will ask you about it
+interactively after quitting your editor.
 
 --from::
        Specify the sender of the emails.  This will default to
@@ -183,6 +197,12 @@ Administering
 --[no-]validate::
        Perform sanity checks on patches.
        Currently, validation means the following:
+
+--[no-]format-patch::
+       When an argument may be understood either as a reference or as a file name,
+       choose to understand it as a format-patch argument ('--format-patch')
+       or as a file name ('--no-format-patch'). By default, when such a conflict
+       occurs, git send-email will fail.
 +
 --
                *       Warn of patches that contain lines longer than 998 characters; this
@@ -204,6 +224,12 @@ sendemail.aliasfiletype::
        Format of the file(s) specified in sendemail.aliasesfile. Must be
        one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
+sendemail.multiedit::
+       If true (default), a single editor instance will be spawned to edit
+       files you have to edit (patches when '--annotate' is used, and the
+       summary when '--compose' is used). If false, files will be edited one
+       after the other, spawning a new editor each time.
+
 
 Author
 ------
index 84c8f3cde0b781ef1579b38a17430dc405115190..8d0c421b80b5ff110d33583cb1bc5a3b417cad90 100644 (file)
@@ -109,7 +109,7 @@ COMMANDS
 
 This works similarly to `svn update` or 'git-pull' except that
 it preserves linear history with 'git-rebase' instead of
-'git-merge' for ease of dcommiting with 'git-svn'.
+'git-merge' for ease of dcommitting with 'git-svn'.
 
 This accepts all options that 'git-svn fetch' and 'git-rebase'
 accept.  However, '--fetch-all' only fetches from the current
@@ -544,6 +544,8 @@ have each person clone that repository with 'git-clone':
        git remote add origin server:/pub/project
        git config --add remote.origin.fetch '+refs/remotes/*:refs/remotes/*'
        git fetch
+# Create a local branch from one of the branches just fetched
+       git checkout -b master FETCH_HEAD
 # Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
        git svn init http://svn.example.com/project
 # Pull the latest changes from Subversion
index a172baf993e5171932533ad713a5d0b34cb6850b..8af22eccac85492416d36259baa60594c9b8382f 100644 (file)
@@ -535,6 +535,23 @@ in the file.  E.g. the string `$Format:%H$` will be replaced by the
 commit hash.
 
 
+Viewing files in GUI tools
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`encoding`
+^^^^^^^^^^
+
+The value of this attribute specifies the character encoding that should
+be used by GUI tools (e.g. linkgit:gitk[1] and linkgit:git-gui[1]) to
+display the contents of the relevant file. Note that due to performance
+considerations linkgit:gitk[1] does not use this attribute unless you
+manually enable per-file encodings in its options.
+
+If this attribute is not set or has an invalid value, the value of the
+`gui.encoding` configuration variable is used instead
+(See linkgit:git-config[1]).
+
+
 USING ATTRIBUTE MACROS
 ----------------------
 
index a417e592ac6a7ed72186dcfc241592ccdc25c9d2..96bf353d13663c33fcd072796f4a65a531f201b2 100644 (file)
@@ -1690,8 +1690,11 @@ to follow, not easier.
 
 SEE ALSO
 --------
-linkgit:gittutorial[7], linkgit:gittutorial-2[7],
-linkgit:everyday[7], linkgit:gitcvs-migration[7],
+linkgit:gittutorial[7],
+linkgit:gittutorial-2[7],
+linkgit:gitcvs-migration[7],
+linkgit:git-help[1],
+link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
 GIT
index 565719ed5f8516e17229ec11bbec5e1354580397..d77a45aed678522344d58498f1ee20ca14e0895e 100644 (file)
@@ -16,8 +16,10 @@ include::glossary-content.txt[]
 
 SEE ALSO
 --------
-linkgit:gittutorial[7], linkgit:gittutorial-2[7],
-linkgit:everyday[7], linkgit:gitcvs-migration[7],
+linkgit:gittutorial[7],
+linkgit:gittutorial-2[7],
+linkgit:gitcvs-migration[7],
+link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
 GIT
index ae29a00d591289b0b2534402599fa7a6fe1e3266..317f6317c254f0ec51c42c5485123aeb91ccd7c6 100644 (file)
@@ -56,6 +56,11 @@ frequently used options.
        Use this instead of explicitly specifying <revs> if the set of
        commits to show may vary between refreshes.
 
+--select-commit=<ref>::
+
+       Automatically select the specified commit after loading the graph.
+       Default behavior is equivalent to specifying '--select-commit=HEAD'.
+
 <revs>::
 
        Limit the revisions to show. This can be either a single revision
index bab0f34b452bfab7174d216700b46220e3a4d2c3..a057b50b2bbfe9740cbd3dc5f112f0d8c3d7d60b 100644 (file)
@@ -425,6 +425,7 @@ linkgit:gittutorial[7],
 linkgit:gitcvs-migration[7],
 linkgit:gitcore-tutorial[7],
 linkgit:gitglossary[7],
+linkgit:git-help[1],
 link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
index 384972cb9bb4a04c55bd8c4796bcd3408e44d5e9..7892244ef19d507379489de0cc81ad18b936c6af 100644 (file)
@@ -26,6 +26,15 @@ First, note that you can get documentation for a command such as
 $ man git-log
 ------------------------------------------------
 
+or:
+
+------------------------------------------------
+$ git help log
+------------------------------------------------
+
+With the latter, you can use the manual viewer of your choice; see
+linkgit:git-help[1] for more information.
+
 It is a good idea to introduce yourself to git with your name and
 public email address before doing any operation.  The easiest
 way to do so is:
@@ -653,6 +662,7 @@ linkgit:gittutorial-2[7],
 linkgit:gitcvs-migration[7],
 linkgit:gitcore-tutorial[7],
 linkgit:gitglossary[7],
+linkgit:git-help[1],
 link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
index c735788b0f1c8efb0b250d4810be420be6c62f89..86d5b26ab2c67940727423fb119d0f446d734db9 100644 (file)
@@ -1,6 +1,10 @@
-merge.stat::
-       Whether to print the diffstat between ORIG_HEAD and the merge result
-       at the end of the merge.  True by default.
+merge.conflictstyle::
+       Specify the style in which conflicted hunks are written out to
+       working tree files upon merge.  The default is "merge", which
+       shows `<<<<<<<` conflict marker, change made by one side,
+       `=======` marker, change made by the other side, and then
+       `>>>>>>>` marker.  An alternate style, "diff3", adds `|||||||`
+       marker and the original text before `=======` marker.
 
 merge.log::
        Whether to include summaries of merged commits in newly created
@@ -11,6 +15,10 @@ merge.renameLimit::
        during a merge; if not specified, defaults to the value of
        diff.renameLimit.
 
+merge.stat::
+       Whether to print the diffstat between ORIG_HEAD and the merge result
+       at the end of the merge.  True by default.
+
 merge.tool::
        Controls which merge resolution program is used by
        linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
index 007909a82fe77325e46c54799d00dc78493a47f9..427cdefd01b7cffad3d00efe029c012709b3a389 100644 (file)
@@ -1,3 +1,11 @@
+-q::
+--quiet::
+       Operate quietly.
+
+-v::
+--verbose::
+       Be verbose.
+
 --stat::
        Show a diffstat at the end of the merge. The diffstat is also
        controlled by the configuration option merge.stat.
index 6d7cf6d51fc5c2511c8ed411ed36aefd93d4ad60..b9f6e4d1b7564480fac9c4e355fdc4936f6fa3db 100644 (file)
@@ -222,6 +222,21 @@ endif::git-rev-list[]
        Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
        command line as '<commit>'.
 
+--branches::
+
+       Pretend as if all the refs in `$GIT_DIR/refs/heads` are listed
+       on the command line as '<commit>'.
+
+--tags::
+
+       Pretend as if all the refs in `$GIT_DIR/refs/tags` are listed
+       on the command line as '<commit>'.
+
+--remotes::
+
+       Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
+       on the command line as '<commit>'.
+
 ifdef::git-rev-list[]
 --stdin::
 
index 645d752c5c26d8724e8ded0abe1f207bf3ff6854..da9c6b2999c28501511b8d31e705176ddc9cc3c1 100644 (file)
@@ -18,12 +18,22 @@ People needing to do actual development will also want to read
 Further chapters cover more specialized topics.
 
 Comprehensive reference documentation is available through the man
-pages.  For a command such as "git clone <repo>", just use
+pages, or linkgit:git-help[1] command.  For example, for the command
+"git clone <repo>", you can either use:
 
 ------------------------------------------------
 $ man git-clone
 ------------------------------------------------
 
+or:
+
+------------------------------------------------
+$ git help clone
+------------------------------------------------
+
+With the latter, you can use the manual viewer of your choice; see
+linkgit:git-help[1] for more information.
+
 See also <<git-quick-start>> for a brief overview of git commands,
 without any explanation.
 
@@ -536,7 +546,7 @@ $ git bisect skip
 -------------------------------------------------
 
 In this case, though, git may not eventually be able to tell the first
-bad one between some first skipped commits and a latter bad commit.
+bad one between some first skipped commits and a later bad commit.
 
 There are also ways to automate the bisecting process if you have a
 test script that can tell a good from a bad commit. See
index 2b3613fea2d7799b7d20e9e30da20527d811ed64..494cbac0057e8b145d54eed8713adc7203d97efb 100644 (file)
@@ -97,7 +97,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
        unsigned char sha1[20];
        char *name = NULL;
        const char *fmt, *remote;
-       char section[PATH_MAX];
        int i;
        int ret = 0;
 
@@ -165,11 +164,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                               argv[i]);
                        ret = 1;
                } else {
+                       struct strbuf buf = STRBUF_INIT;
                        printf("Deleted %sbranch %s.\n", remote, argv[i]);
-                       snprintf(section, sizeof(section), "branch.%s",
-                                argv[i]);
-                       if (git_config_rename_section(section, NULL) < 0)
+                       strbuf_addf(&buf, "branch.%s", argv[i]);
+                       if (git_config_rename_section(buf.buf, NULL) < 0)
                                warning("Update of config-file failed");
+                       strbuf_release(&buf);
                }
        }
 
@@ -279,7 +279,7 @@ static int ref_cmp(const void *r1, const void *r2)
        return strcmp(c1->name, c2->name);
 }
 
-static void fill_tracking_info(char *stat, const char *branch_name)
+static void fill_tracking_info(struct strbuf *stat, const char *branch_name)
 {
        int ours, theirs;
        struct branch *branch = branch_get(branch_name);
@@ -287,11 +287,11 @@ static void fill_tracking_info(char *stat, const char *branch_name)
        if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
                return;
        if (!ours)
-               sprintf(stat, "[behind %d] ", theirs);
+               strbuf_addf(stat, "[behind %d] ", theirs);
        else if (!theirs)
-               sprintf(stat, "[ahead %d] ", ours);
+               strbuf_addf(stat, "[ahead %d] ", ours);
        else
-               sprintf(stat, "[ahead %d, behind %d] ", ours, theirs);
+               strbuf_addf(stat, "[ahead %d, behind %d] ", ours, theirs);
 }
 
 static int matches_merge_filter(struct commit *commit)
@@ -334,11 +334,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
        }
 
        if (verbose) {
-               struct strbuf subject = STRBUF_INIT;
+               struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
                const char *sub = " **** invalid ref ****";
-               char stat[128];
-
-               stat[0] = '\0';
 
                commit = item->commit;
                if (commit && !parse_commit(commit)) {
@@ -348,13 +345,14 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
                }
 
                if (item->kind == REF_LOCAL_BRANCH)
-                       fill_tracking_info(stat, item->name);
+                       fill_tracking_info(&stat, item->name);
 
                printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
                       maxwidth, item->name,
                       branch_get_color(COLOR_BRANCH_RESET),
                       find_unique_abbrev(item->commit->object.sha1, abbrev),
-                      stat, sub);
+                      stat.buf, sub);
+               strbuf_release(&stat);
                strbuf_release(&subject);
        } else {
                printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
@@ -426,42 +424,45 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
 
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
-       char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
+       struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
        unsigned char sha1[20];
-       char oldsection[PATH_MAX], newsection[PATH_MAX];
+       struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
 
        if (!oldname)
                die("cannot rename the current branch while not on any.");
 
-       if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
-               die("Old branchname too long");
+       strbuf_addf(&oldref, "refs/heads/%s", oldname);
 
-       if (check_ref_format(oldref))
-               die("Invalid branch name: %s", oldref);
+       if (check_ref_format(oldref.buf))
+               die("Invalid branch name: %s", oldref.buf);
 
-       if (snprintf(newref, sizeof(newref), "refs/heads/%s", newname) > sizeof(newref))
-               die("New branchname too long");
+       strbuf_addf(&newref, "refs/heads/%s", newname);
 
-       if (check_ref_format(newref))
-               die("Invalid branch name: %s", newref);
+       if (check_ref_format(newref.buf))
+               die("Invalid branch name: %s", newref.buf);
 
-       if (resolve_ref(newref, sha1, 1, NULL) && !force)
+       if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
                die("A branch named '%s' already exists.", newname);
 
-       snprintf(logmsg, sizeof(logmsg), "Branch: renamed %s to %s",
-                oldref, newref);
+       strbuf_addf(&logmsg, "Branch: renamed %s to %s",
+                oldref.buf, newref.buf);
 
-       if (rename_ref(oldref, newref, logmsg))
+       if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
                die("Branch rename failed");
+       strbuf_release(&logmsg);
 
        /* no need to pass logmsg here as HEAD didn't really move */
-       if (!strcmp(oldname, head) && create_symref("HEAD", newref, NULL))
+       if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
                die("Branch renamed to %s, but HEAD is not updated!", newname);
 
-       snprintf(oldsection, sizeof(oldsection), "branch.%s", oldref + 11);
-       snprintf(newsection, sizeof(newsection), "branch.%s", newref + 11);
-       if (git_config_rename_section(oldsection, newsection) < 0)
+       strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
+       strbuf_release(&oldref);
+       strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
+       strbuf_release(&newref);
+       if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
                die("Branch is renamed, but update of config-file failed");
+       strbuf_release(&oldsection);
+       strbuf_release(&newsection);
 }
 
 static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
index 464fd2570735485a35d34c4aa8d50e5a1930228c..7f3bd7bb1cb8f211c78067df9376541e0aa3f923 100644 (file)
@@ -553,7 +553,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
                describe_detached_head("Previous HEAD position was", old.commit);
 
-       if (!old.commit) {
+       if (!old.commit && !opts->force) {
                if (!opts->quiet) {
                        fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
                        fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
index 1447c1d6baf99c405676a54f0527b254a44ef22e..dddcf697d7447b11d618ac5983516fd1789b0d39 100644 (file)
@@ -290,6 +290,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        /* Otherwise, we are doing the usual "git" diff */
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
 
+       /* Default to let external be used */
+       DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
+
        if (nongit)
                die("Not a git repository");
        argc = setup_revisions(argc, argv, &rev, NULL);
@@ -298,7 +301,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                if (diff_setup_done(&rev.diffopt) < 0)
                        die("diff_setup_done failed");
        }
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
+
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
        DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
 
index 7c93eb878d7da87b39959fb9599e4b7b6b81570b..7d5d57ad7568b8c0bf7396cdbd581c67ab4600e2 100644 (file)
@@ -354,7 +354,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
                case OBJ_TAG:
                        tag = (struct tag *)e->item;
                        while (tag && tag->object.type == OBJ_TAG) {
-                               string_list_insert(full_name, extra_refs)->util = tag;
+                               string_list_append(full_name, extra_refs)->util = tag;
                                tag = (struct tag *)tag->tagged;
                        }
                        if (!tag)
@@ -374,7 +374,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
                }
                if (commit->util)
                        /* more than one name for the same object */
-                       string_list_insert(full_name, extra_refs)->util = commit;
+                       string_list_append(full_name, extra_refs)->util = commit;
                else
                        commit->util = full_name;
        }
index f151cfa2fd028dd37ad0cc3b6b35b2017417fffa..7568163af24df630c215e05b6082ed764150a315 100644 (file)
@@ -22,7 +22,7 @@ enum {
        TAGS_SET = 2
 };
 
-static int append, force, keep, update_head_ok, verbose, quiet;
+static int append, force, keep, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -30,8 +30,7 @@ static struct strbuf default_rla = STRBUF_INIT;
 static struct transport *transport;
 
 static struct option builtin_fetch_options[] = {
-       OPT__QUIET(&quiet),
-       OPT__VERBOSE(&verbose),
+       OPT__VERBOSITY(&verbosity),
        OPT_BOOLEAN('a', "append", &append,
                    "append to .git/FETCH_HEAD instead of overwriting"),
        OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
@@ -192,7 +191,6 @@ static int s_update_ref(const char *action,
 
 static int update_local_ref(struct ref *ref,
                            const char *remote,
-                           int verbose,
                            char *display)
 {
        struct commit *current = NULL, *updated;
@@ -210,7 +208,7 @@ static int update_local_ref(struct ref *ref,
                die("object %s not found", sha1_to_hex(ref->new_sha1));
 
        if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
-               if (verbose)
+               if (verbosity > 0)
                        sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
                                "[up to date]", REFCOL_WIDTH, remote,
                                pretty_ref);
@@ -366,18 +364,19 @@ static int store_updated_refs(const char *url, const char *remote_name,
                        note);
 
                if (ref)
-                       rc |= update_local_ref(ref, what, verbose, note);
+                       rc |= update_local_ref(ref, what, note);
                else
                        sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
                                SUMMARY_WIDTH, *kind ? kind : "branch",
                                 REFCOL_WIDTH, *what ? what : "HEAD");
                if (*note) {
-                       if (!shown_url) {
+                       if (verbosity >= 0 && !shown_url) {
                                fprintf(stderr, "From %.*s\n",
                                                url_len, url);
                                shown_url = 1;
                        }
-                       fprintf(stderr, " %s\n", note);
+                       if (verbosity >= 0)
+                               fprintf(stderr, " %s\n", note);
                }
        }
        fclose(fp);
@@ -637,9 +636,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                remote = remote_get(argv[0]);
 
        transport = transport_get(remote, remote->url[0]);
-       if (verbose >= 2)
+       if (verbosity >= 2)
                transport->verbose = 1;
-       if (quiet)
+       if (verbosity < 0)
                transport->verbose = -1;
        if (upload_pack)
                set_option(TRANS_OPT_UPLOADPACK, upload_pack);
index b48327db950e5d8a54045f4956fa92b7879227bc..f72eb854756f602e4d114964f4585bc5a8c55e20 100644 (file)
@@ -227,6 +227,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                        int dtype = ce_to_dtype(ce);
                        if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
                                continue;
+                       if (ce->ce_flags & CE_UPDATE)
+                               continue;
                        err = lstat(ce->name, &st);
                        if (show_deleted && err)
                                show_ce_entry(tag_removed, ce);
@@ -329,7 +331,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
        if (prefix) {
                static const char *(matchbuf[2]);
                matchbuf[0] = prefix;
-               matchbuf [1] = NULL;
+               matchbuf[1] = NULL;
                match = matchbuf;
        } else
                match = NULL;
index 5e7910bd8da0a603dd82e7502c28a603bf80fa82..7c2b90c70baed2c7f008c2b98ad26135ef12db00 100644 (file)
@@ -50,6 +50,7 @@ static unsigned char head[20], stash[20];
 static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
 static const char *branch;
+static int verbosity;
 
 static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -171,6 +172,7 @@ static struct option builtin_merge_options[] = {
        OPT_CALLBACK('m', "message", &merge_msg, "message",
                "message to be used for the merge commit (if any)",
                option_parse_message),
+       OPT__VERBOSITY(&verbosity),
        OPT_END()
 };
 
@@ -250,7 +252,8 @@ static void restore_state(void)
 /* This is called when no merge was necessary. */
 static void finish_up_to_date(const char *msg)
 {
-       printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+       if (verbosity >= 0)
+               printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
        drop_save();
 }
 
@@ -331,14 +334,15 @@ static void finish(const unsigned char *new_head, const char *msg)
        if (!msg)
                strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
        else {
-               printf("%s\n", msg);
+               if (verbosity >= 0)
+                       printf("%s\n", msg);
                strbuf_addf(&reflog_message, "%s: %s",
                        getenv("GIT_REFLOG_ACTION"), msg);
        }
        if (squash) {
                squash_message();
        } else {
-               if (!merge_msg.len)
+               if (verbosity >= 0 && !merge_msg.len)
                        printf("No merge message -- not updating HEAD\n");
                else {
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
@@ -872,6 +876,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, builtin_merge_options,
                        builtin_merge_usage, 0);
+       if (verbosity < 0)
+               show_diffstat = 0;
 
        if (squash) {
                if (!allow_fast_forward)
@@ -1013,10 +1019,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
                strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
 
-               printf("Updating %s..%s\n",
-                       hex,
-                       find_unique_abbrev(remoteheads->item->object.sha1,
-                       DEFAULT_ABBREV));
+               if (verbosity >= 0)
+                       printf("Updating %s..%s\n",
+                               hex,
+                               find_unique_abbrev(remoteheads->item->object.sha1,
+                               DEFAULT_ABBREV));
                strbuf_addstr(&msg, "Fast forward");
                if (have_message)
                        strbuf_addstr(&msg,
index 71696b50d333f24f1c7a6a7ace946cf3392ca7d0..14774e36c40cb079b52259b300cb58f6b94d0588 100644 (file)
@@ -8,12 +8,12 @@
 #include "refs.h"
 
 static const char * const builtin_remote_usage[] = {
-       "git remote",
-       "git remote add <name> <url>",
+       "git remote [-v | --verbose]",
+       "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
        "git remote rename <old> <new>",
        "git remote rm <name>",
-       "git remote show <name>",
-       "git remote prune <name>",
+       "git remote show [-n] <name>",
+       "git remote prune [-n | --dry-run] <name>",
        "git remote update [group]",
        NULL
 };
@@ -770,7 +770,7 @@ static int get_one_remote_for_update(struct remote *remote, void *priv)
 {
        struct string_list *list = priv;
        if (!remote->skip_default_update)
-               string_list_append(xstrdup(remote->name), list);
+               string_list_append(remote->name, list);
        return 0;
 }
 
diff --git a/cache.h b/cache.h
index 685a8666b64967e4c76514dc3bcf391fd168681e..487e5e1b1bf66007a2d2d753daaf97ddb879b85a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -533,6 +533,12 @@ static inline void hashclr(unsigned char *hash)
 }
 extern int is_empty_blob_sha1(const unsigned char *sha1);
 
+#define EMPTY_TREE_SHA1_HEX \
+       "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
+#define EMPTY_TREE_SHA1_BIN \
+        "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
+        "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
+
 int git_mkstemp(char *path, size_t n, const char *template);
 
 /*
index de193ba7c1caf69367410d763f3541555a64746f..c79c98ffec58f81b7592ba3fc359fc0d1c8d1d22 100755 (executable)
@@ -188,11 +188,22 @@ __git_tags ()
 
 __git_refs ()
 {
-       local cmd i is_hash=y dir="$(__gitdir "$1")"
+       local i is_hash=y dir="$(__gitdir "$1")"
+       local cur="${COMP_WORDS[COMP_CWORD]}" format refs
        if [ -d "$dir" ]; then
-               if [ -e "$dir/HEAD" ]; then echo HEAD; fi
-               git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
-                       refs/tags refs/heads refs/remotes
+               case "$cur" in
+               refs|refs/*)
+                       format="refname"
+                       refs="${cur%/*}"
+                       ;;
+               *)
+                       if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+                       format="refname:short"
+                       refs="refs/tags refs/heads refs/remotes"
+                       ;;
+               esac
+               git --git-dir="$dir" for-each-ref --format="%($format)" \
+                       $refs
                return
        fi
        for i in $(git ls-remote "$dir" 2>/dev/null); do
@@ -636,21 +647,12 @@ _git_branch ()
 
 _git_bundle ()
 {
-       local mycword="$COMP_CWORD"
-       case "${COMP_WORDS[0]}" in
-       git)
-               local cmd="${COMP_WORDS[2]}"
-               mycword="$((mycword-1))"
-               ;;
-       git-bundle*)
-               local cmd="${COMP_WORDS[1]}"
-               ;;
-       esac
-       case "$mycword" in
-       1)
+       local cmd="${COMP_WORDS[2]}"
+       case "$COMP_CWORD" in
+       2)
                __gitcomp "create list-heads verify unbundle"
                ;;
-       2)
+       3)
                # looking for a file
                ;;
        *)
@@ -798,12 +800,7 @@ _git_fetch ()
                        __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
                        ;;
                *)
-                       local remote
-                       case "${COMP_WORDS[0]}" in
-                       git-fetch) remote="${COMP_WORDS[1]}" ;;
-                       git)       remote="${COMP_WORDS[2]}" ;;
-                       esac
-                       __gitcomp "$(__git_refs2 "$remote")"
+                       __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
                        ;;
                esac
        fi
@@ -1047,12 +1044,7 @@ _git_pull ()
        if [ "$COMP_CWORD" = 2 ]; then
                __gitcomp "$(__git_remotes)"
        else
-               local remote
-               case "${COMP_WORDS[0]}" in
-               git-pull)  remote="${COMP_WORDS[1]}" ;;
-               git)       remote="${COMP_WORDS[2]}" ;;
-               esac
-               __gitcomp "$(__git_refs "$remote")"
+               __gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
        fi
 }
 
@@ -1065,19 +1057,13 @@ _git_push ()
        else
                case "$cur" in
                *:*)
-                       local remote
-                       case "${COMP_WORDS[0]}" in
-                       git-push)  remote="${COMP_WORDS[1]}" ;;
-                       git)       remote="${COMP_WORDS[2]}" ;;
-                       esac
-
                        local pfx=""
                        case "$COMP_WORDBREAKS" in
                        *:*) : great ;;
                        *)   pfx="${cur%%:*}:" ;;
                        esac
 
-                       __gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}"
+                       __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
                        ;;
                +*)
                        __gitcomp "$(__git_refs)" + "${cur#+}"
@@ -1357,7 +1343,7 @@ _git_revert ()
                return
                ;;
        esac
-       COMPREPLY=()
+       __gitcomp "$(__git_refs)"
 }
 
 _git_rm ()
@@ -1579,7 +1565,7 @@ _git_tag ()
        -m|-F)
                COMPREPLY=()
                ;;
-       -*|tag|git-tag)
+       -*|tag)
                if [ $f = 1 ]; then
                        __gitcomp "$(__git_tags)"
                else
index c1cf1cbcc014e5d6c01a1c33efa2d7bd3b76df88..09e8bae3a41827a20f6f0693c28f91a98adcb8a4 100644 (file)
@@ -173,7 +173,7 @@ if there is already one that displays the same directory."
 (defconst git-log-msg-separator "--- log message follows this line ---")
 
 (defvar git-log-edit-font-lock-keywords
-  `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$"
+  `(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$"
      (1 font-lock-keyword-face)
      (2 font-lock-function-name-face))
     (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
@@ -183,11 +183,9 @@ if there is already one that displays the same directory."
   "Build a list of NAME=VALUE strings from a list of environment strings."
   (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env))
 
-(defun git-call-process-env (buffer env &rest args)
+(defun git-call-process (buffer &rest args)
   "Wrapper for call-process that sets environment strings."
-  (let ((process-environment (append (git-get-env-strings env)
-                                     process-environment)))
-    (apply #'call-process "git" nil buffer nil args)))
+  (apply #'call-process "git" nil buffer nil args))
 
 (defun git-call-process-display-error (&rest args)
   "Wrapper for call-process that displays error messages."
@@ -197,17 +195,26 @@ if there is already one that displays the same directory."
                (let ((default-directory dir)
                      (buffer-read-only nil))
                  (erase-buffer)
-                 (eq 0 (apply 'call-process "git" nil (list buffer t) nil args))))))
+                 (eq 0 (apply #'git-call-process (list buffer t) args))))))
     (unless ok (display-message-or-buffer buffer))
     ok))
 
-(defun git-call-process-env-string (env &rest args)
-  "Wrapper for call-process that sets environment strings,
-and returns the process output as a string, or nil if the git failed."
+(defun git-call-process-string (&rest args)
+  "Wrapper for call-process that returns the process output as a string,
+or nil if the git command failed."
   (with-temp-buffer
-    (and (eq 0 (apply #' git-call-process-env t env args))
+    (and (eq 0 (apply #'git-call-process t args))
          (buffer-string))))
 
+(defun git-call-process-string-display-error (&rest args)
+  "Wrapper for call-process that displays error message and returns
+the process output as a string, or nil if the git command failed."
+  (with-temp-buffer
+    (if (eq 0 (apply #'git-call-process (list t t) args))
+        (buffer-string)
+      (display-message-or-buffer (current-buffer))
+      nil)))
+
 (defun git-run-process-region (buffer start end program args)
   "Run a git process with a buffer region as input."
   (let ((output-buffer (current-buffer))
@@ -226,7 +233,7 @@ and returns the process output as a string, or nil if the git failed."
       (let ((default-directory dir)
             (buffer-read-only nil))
         (erase-buffer)
-        (apply #'git-call-process-env buffer nil args)))
+        (apply #'git-call-process buffer args)))
     (message "Running git %s...done" (car args))
     buffer))
 
@@ -327,7 +334,7 @@ and returns the process output as a string, or nil if the git failed."
   (let ((cdup (with-output-to-string
                 (with-current-buffer standard-output
                   (cd dir)
-                  (unless (eq 0 (call-process "git" nil t nil "rev-parse" "--show-cdup"))
+                  (unless (eq 0 (git-call-process t "rev-parse" "--show-cdup"))
                     (error "cannot find top-level git tree for %s." dir))))))
     (expand-file-name (concat (file-name-as-directory dir)
                               (car (split-string cdup "\n"))))))
@@ -348,8 +355,8 @@ and returns the process output as a string, or nil if the git failed."
     (sort-lines nil (point-min) (point-max))
     (save-buffer))
   (when created
-    (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
-  (git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
+    (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name)))
+  (git-update-status-files (list (file-relative-name ignore-name)))))
 
 ; propertize definition for XEmacs, stolen from erc-compat
 (eval-when-compile
@@ -367,38 +374,41 @@ and returns the process output as a string, or nil if the git failed."
 (defun git-rev-parse (rev)
   "Parse a revision name and return its SHA1."
   (git-get-string-sha1
-   (git-call-process-env-string nil "rev-parse" rev)))
+   (git-call-process-string "rev-parse" rev)))
 
 (defun git-config (key)
   "Retrieve the value associated to KEY in the git repository config file."
-  (let ((str (git-call-process-env-string nil "config" key)))
+  (let ((str (git-call-process-string "config" key)))
     (and str (car (split-string str "\n")))))
 
 (defun git-symbolic-ref (ref)
   "Wrapper for the git-symbolic-ref command."
-  (let ((str (git-call-process-env-string nil "symbolic-ref" ref)))
+  (let ((str (git-call-process-string "symbolic-ref" ref)))
     (and str (car (split-string str "\n")))))
 
 (defun git-update-ref (ref newval &optional oldval reason)
   "Update a reference by calling git-update-ref."
   (let ((args (and oldval (list oldval))))
-    (push newval args)
+    (when newval (push newval args))
     (push ref args)
     (when reason
      (push reason args)
      (push "-m" args))
+    (unless newval (push "-d" args))
     (apply 'git-call-process-display-error "update-ref" args)))
 
 (defun git-read-tree (tree &optional index-file)
   "Read a tree into the index file."
-  (apply #'git-call-process-env nil
-         (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil)
-         "read-tree" (if tree (list tree))))
+  (let ((process-environment
+         (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
+    (apply 'git-call-process-display-error "read-tree" (if tree (list tree)))))
 
 (defun git-write-tree (&optional index-file)
   "Call git-write-tree and return the resulting tree SHA1 as a string."
-  (git-get-string-sha1
-   (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree")))
+  (let ((process-environment
+         (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
+    (git-get-string-sha1
+     (git-call-process-string-display-error "write-tree"))))
 
 (defun git-commit-tree (buffer tree head)
   "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
@@ -424,11 +434,11 @@ and returns the process output as a string, or nil if the git failed."
             (when (re-search-forward "^Date: +\\(.*\\)$" nil t)
               (setq author-date (match-string 1)))
             (goto-char (point-min))
-            (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t)
-              (unless (string-equal head (match-string 1))
-                (setq subject "commit (merge): ")
+            (when (re-search-forward "^Merge: +\\(.*\\)" nil t)
+              (setq subject "commit (merge): ")
+              (dolist (parent (split-string (match-string 1) " +" t))
                 (push "-p" args)
-                (push (match-string 1) args))))
+                (push parent args))))
         (setq log-start (point-min)))
       (setq log-end (point-max))
       (goto-char log-start)
@@ -452,7 +462,7 @@ and returns the process output as a string, or nil if the git failed."
 
 (defun git-empty-db-p ()
   "Check if the git db is empty (no commit done yet)."
-  (not (eq 0 (call-process "git" nil nil nil "rev-parse" "--verify" "HEAD"))))
+  (not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD"))))
 
 (defun git-get-merge-heads ()
   "Retrieve the merge heads from the MERGE_HEAD file if present."
@@ -468,7 +478,7 @@ and returns the process output as a string, or nil if the git failed."
 (defun git-get-commit-description (commit)
   "Get a one-line description of COMMIT."
   (let ((coding-system-for-read (git-get-logoutput-coding-system)))
-    (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit)))
+    (let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit)))
       (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr))
           (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr))
         descr))))
@@ -487,14 +497,11 @@ and returns the process output as a string, or nil if the git failed."
   old-perm new-perm   ;; permission flags
   rename-state        ;; rename or copy state
   orig-name           ;; original name for renames or copies
+  needs-update        ;; whether file needs to be updated
   needs-refresh)      ;; whether file needs to be refreshed
 
 (defvar git-status nil)
 
-(defun git-clear-status (status)
-  "Remove everything from the status list."
-  (ewoc-filter status (lambda (info) nil)))
-
 (defun git-set-fileinfo-state (info state)
   "Set the state of a file info."
   (unless (eq (git-fileinfo->state info) state)
@@ -502,6 +509,7 @@ and returns the process output as a string, or nil if the git failed."
          (git-fileinfo->new-perm info) (git-fileinfo->old-perm info)
           (git-fileinfo->rename-state info) nil
           (git-fileinfo->orig-name info) nil
+          (git-fileinfo->needs-update info) nil
           (git-fileinfo->needs-refresh info) t)))
 
 (defun git-status-filenames-map (status func files &rest args)
@@ -511,10 +519,11 @@ and returns the process output as a string, or nil if the git failed."
     (let ((file (pop files))
           (node (ewoc-nth status 0)))
       (while (and file node)
-        (let ((info (ewoc-data node)))
-          (if (string-lessp (git-fileinfo->name info) file)
+        (let* ((info (ewoc-data node))
+               (name (git-fileinfo->name info)))
+          (if (string-lessp name file)
               (setq node (ewoc-next status node))
-            (if (string-equal (git-fileinfo->name info) file)
+            (if (string-equal name file)
                 (apply func info args))
             (setq file (pop files))))))))
 
@@ -612,39 +621,52 @@ and returns the process output as a string, or nil if the git failed."
                    (git-file-type-as-string old-perm new-perm)
                    (git-rename-as-string info)))))
 
-(defun git-insert-info-list (status infolist)
-  "Insert a list of file infos in the status buffer, replacing existing ones if any."
-  (setq infolist (sort infolist
-                       (lambda (info1 info2)
-                         (string-lessp (git-fileinfo->name info1)
-                                       (git-fileinfo->name info2)))))
-  (let ((info (pop infolist))
-        (node (ewoc-nth status 0)))
+(defun git-update-node-fileinfo (node info)
+  "Update the fileinfo of the specified node. The names are assumed to match already."
+  (let ((data (ewoc-data node)))
+    (setf
+     ;; preserve the marked flag
+     (git-fileinfo->marked info) (git-fileinfo->marked data)
+     (git-fileinfo->needs-update data) nil)
+    (when (not (equal info data))
+      (setf (git-fileinfo->needs-refresh info) t
+            (ewoc-data node) info))))
+
+(defun git-insert-info-list (status infolist files)
+  "Insert a sorted list of file infos in the status buffer, replacing existing ones if any."
+  (let* ((info (pop infolist))
+         (node (ewoc-nth status 0))
+         (name (and info (git-fileinfo->name info)))
+         remaining)
     (while info
-      (cond ((not node)
-            (setq node (ewoc-enter-last status info))
-             (setq info (pop infolist)))
-            ((string-lessp (git-fileinfo->name (ewoc-data node))
-                           (git-fileinfo->name info))
-             (setq node (ewoc-next status node)))
-            ((string-equal (git-fileinfo->name (ewoc-data node))
-                           (git-fileinfo->name info))
-              ;; preserve the marked flag
-              (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
-             (setf (git-fileinfo->needs-refresh info) t)
-              (setf (ewoc-data node) info)
-              (setq info (pop infolist)))
-            (t
-            (setq node (ewoc-enter-before status node info))
-             (setq info (pop infolist)))))))
+      (let ((nodename (and node (git-fileinfo->name (ewoc-data node)))))
+        (while (and files (string-lessp (car files) name))
+          (push (pop files) remaining))
+        (when (and files (string-equal (car files) name))
+          (setq files (cdr files)))
+        (cond ((not nodename)
+               (setq node (ewoc-enter-last status info))
+               (setq info (pop infolist))
+               (setq name (and info (git-fileinfo->name info))))
+              ((string-lessp nodename name)
+               (setq node (ewoc-next status node)))
+              ((string-equal nodename name)
+               ;; preserve the marked flag
+               (git-update-node-fileinfo node info)
+               (setq info (pop infolist))
+               (setq name (and info (git-fileinfo->name info))))
+              (t
+               (setq node (ewoc-enter-before status node info))
+               (setq info (pop infolist))
+               (setq name (and info (git-fileinfo->name info)))))))
+    (nconc (nreverse remaining) files)))
 
 (defun git-run-diff-index (status files)
   "Run git-diff-index on FILES and parse the results into STATUS.
 Return the list of files that haven't been handled."
-  (let ((remaining (copy-sequence files))
-       infolist)
+  (let (infolist)
     (with-temp-buffer
-      (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files)
+      (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files)
       (goto-char (point-min))
       (while (re-search-forward
              ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
@@ -659,11 +681,12 @@ Return the list of files that haven't been handled."
                   (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
                 (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
                 (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
-            (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
-         (setq remaining (delete name remaining))
-         (when new-name (setq remaining (delete new-name remaining))))))
-    (git-insert-info-list status infolist)
-    remaining))
+            (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)))))
+    (setq infolist (sort (nreverse infolist)
+                         (lambda (info1 info2)
+                           (string-lessp (git-fileinfo->name info1)
+                                         (git-fileinfo->name info2)))))
+    (git-insert-info-list status infolist files)))
 
 (defun git-find-status-file (status file)
   "Find a given file in the status ewoc and return its node."
@@ -677,38 +700,35 @@ Return the list of files that haven't been handled."
 Return the list of files that haven't been handled."
   (let (infolist)
     (with-temp-buffer
-      (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
+      (apply #'git-call-process t "ls-files" "-z" (append options (list "--") files))
       (goto-char (point-min))
       (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1)
         (let ((name (match-string 1)))
           (push (git-create-fileinfo default-state name 0
                                      (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0))
-                infolist)
-          (setq files (delete name files)))))
-    (git-insert-info-list status infolist)
-    files))
+                infolist))))
+    (setq infolist (nreverse infolist))  ;; assume it is sorted already
+    (git-insert-info-list status infolist files)))
 
 (defun git-run-ls-files-cached (status files default-state)
   "Run git-ls-files -c on FILES and parse the results into STATUS.
 Return the list of files that haven't been handled."
-  (let ((remaining (copy-sequence files))
-       infolist)
+  (let (infolist)
     (with-temp-buffer
-      (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files)
+      (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files)
       (goto-char (point-min))
       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
        (let* ((new-perm (string-to-number (match-string 1) 8))
               (old-perm (if (eq default-state 'added) 0 new-perm))
               (name (match-string 2)))
-         (push (git-create-fileinfo default-state name old-perm new-perm) infolist)
-         (setq remaining (delete name remaining)))))
-    (git-insert-info-list status infolist)
-    remaining))
+         (push (git-create-fileinfo default-state name old-perm new-perm) infolist))))
+    (setq infolist (nreverse infolist))  ;; assume it is sorted already
+    (git-insert-info-list status infolist files)))
 
 (defun git-run-ls-unmerged (status files)
   "Run git-ls-files -u on FILES and parse the results into STATUS."
   (with-temp-buffer
-    (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files)
+    (apply #'git-call-process t "ls-files" "-z" "-u" "--" files)
     (goto-char (point-min))
     (let (unmerged-files)
       (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
@@ -732,11 +752,17 @@ Return the list of files that haven't been handled."
            (concat "--exclude-per-directory=" git-per-dir-ignore-file)
            (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
 
-(defun git-update-status-files (files &optional default-state)
+(defun git-update-status-files (&optional files mark-files)
   "Update the status of FILES from the index."
   (unless git-status (error "Not in git-status buffer."))
-  (when (or git-show-uptodate files)
-    (git-run-ls-files-cached git-status files 'uptodate))
+  ;; set the needs-update flag on existing files
+  (if (setq files (sort files #'string-lessp))
+      (git-status-filenames-map
+       git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files)
+    (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status)
+    (git-call-process nil "update-index" "--refresh")
+    (when git-show-uptodate
+      (git-run-ls-files-cached git-status nil 'uptodate)))
   (let* ((remaining-files
           (if (git-empty-db-p) ; we need some special handling for an empty db
              (git-run-ls-files-cached git-status files 'added)
@@ -746,13 +772,17 @@ Return the list of files that haven't been handled."
       (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o")))
     (when (or remaining-files (and git-show-ignored (not files)))
       (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i")))
-    (git-set-filenames-state git-status remaining-files default-state)
+    (unless files
+      (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update))))
+    (when remaining-files
+      (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate)))
+    (git-set-filenames-state git-status remaining-files nil)
+    (when mark-files (git-mark-files git-status files))
     (git-refresh-files)
     (git-refresh-ewoc-hf git-status)))
 
 (defun git-mark-files (status files)
   "Mark all the specified FILES, and unmark the others."
-  (setq files (sort files #'string-lessp))
   (let ((file (and files (pop files)))
         (node (ewoc-nth status 0)))
     (while node
@@ -824,19 +854,18 @@ Return the list of files that haven't been handled."
 
 (defun git-update-index (index-file files)
   "Run git-update-index on a list of files."
-  (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file))))
+  (let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file)))
+                                     process-environment))
         added deleted modified)
     (dolist (info files)
       (case (git-fileinfo->state info)
         ('added (push info added))
         ('deleted (push info deleted))
         ('modified (push info modified))))
-    (when added
-      (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added)))
-    (when deleted
-      (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
-    (when modified
-      (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified)))))
+    (and
+     (or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added)))
+     (or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted)))
+     (or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified))))))
 
 (defun git-run-pre-commit-hook ()
   "Run the pre-commit hook if any."
@@ -862,33 +891,30 @@ Return the list of files that haven't been handled."
           (message "You cannot commit unmerged files, resolve them first.")
         (unwind-protect
             (let ((files (git-marked-files-state 'added 'deleted 'modified))
-                  head head-tree)
+                  head tree head-tree)
               (unless (git-empty-db-p)
                 (setq head (git-rev-parse "HEAD")
                       head-tree (git-rev-parse "HEAD^{tree}")))
-              (if files
-                  (progn
-                    (message "Running git commit...")
-                    (git-read-tree head-tree index-file)
-                    (git-update-index nil files)         ;update both the default index
-                    (git-update-index index-file files)  ;and the temporary one
-                    (let ((tree (git-write-tree index-file)))
-                      (if (or (not (string-equal tree head-tree))
-                              (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
-                          (let ((commit (git-commit-tree buffer tree head)))
-                            (when commit
-                              (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
-                              (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
-                              (with-current-buffer buffer (erase-buffer))
-                              (git-update-status-files (git-get-filenames files) 'uptodate)
-                              (git-call-process-env nil nil "rerere")
-                              (git-call-process-env nil nil "gc" "--auto")
-                              (git-refresh-files)
-                              (git-refresh-ewoc-hf git-status)
-                              (message "Committed %s." commit)
-                              (git-run-hook "post-commit" nil)))
-                        (message "Commit aborted."))))
-                (message "No files to commit.")))
+              (message "Running git commit...")
+              (when
+                  (and
+                   (git-read-tree head-tree index-file)
+                   (git-update-index nil files)         ;update both the default index
+                   (git-update-index index-file files)  ;and the temporary one
+                   (setq tree (git-write-tree index-file)))
+                (if (or (not (string-equal tree head-tree))
+                        (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
+                    (let ((commit (git-commit-tree buffer tree head)))
+                      (when commit
+                        (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
+                        (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
+                        (with-current-buffer buffer (erase-buffer))
+                        (git-update-status-files (git-get-filenames files))
+                        (git-call-process nil "rerere")
+                        (git-call-process nil "gc" "--auto")
+                        (message "Committed %s." commit)
+                        (git-run-hook "post-commit" nil)))
+                  (message "Commit aborted."))))
           (delete-file index-file))))))
 
 
@@ -990,6 +1016,11 @@ Return the list of files that haven't been handled."
       (setq node (ewoc-prev git-status node)))
     (ewoc-goto-node git-status last)))
 
+(defun git-insert-file (file)
+  "Insert file(s) into the git-status buffer."
+  (interactive "fInsert file: ")
+  (git-update-status-files (list (file-relative-name file))))
+
 (defun git-add-file ()
   "Add marked file(s) to the index cache."
   (interactive)
@@ -998,7 +1029,7 @@ Return the list of files that haven't been handled."
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
     (when (apply 'git-call-process-display-error "update-index" "--add" "--" files)
-      (git-update-status-files files 'uptodate)
+      (git-update-status-files files)
       (git-success-message "Added" files))))
 
 (defun git-ignore-file ()
@@ -1008,7 +1039,7 @@ Return the list of files that haven't been handled."
     (unless files
       (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
     (dolist (f files) (git-append-to-ignore f))
-    (git-update-status-files files 'ignored)
+    (git-update-status-files files)
     (git-success-message "Ignored" files)))
 
 (defun git-remove-file ()
@@ -1026,7 +1057,7 @@ Return the list of files that haven't been handled."
                   (delete-directory name)
                 (delete-file name))))
           (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files)
-            (git-update-status-files files nil)
+            (git-update-status-files files)
             (git-success-message "Removed" files)))
       (message "Aborting"))))
 
@@ -1054,7 +1085,7 @@ Return the list of files that haven't been handled."
                      (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added))
                  (or (not modified)
                      (apply 'git-call-process-display-error "checkout" "HEAD" modified)))))
-        (git-update-status-files (append added modified) 'uptodate)
+        (git-update-status-files (append added modified))
         (when ok
           (dolist (file modified)
             (let ((buffer (get-file-buffer file)))
@@ -1067,7 +1098,7 @@ Return the list of files that haven't been handled."
   (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
     (when files
       (when (apply 'git-call-process-display-error "update-index" "--" files)
-        (git-update-status-files files 'uptodate)
+        (git-update-status-files files)
         (git-success-message "Resolved" files)))))
 
 (defun git-remove-handled ()
@@ -1225,11 +1256,10 @@ Return the list of files that haven't been handled."
       (goto-char (point-max))
       (insert sign-off "\n"))))
 
-(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg)
+(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg)
   "Setup the log buffer for a commit."
   (unless git-status (error "Not in git-status buffer."))
-  (let ((merge-heads (git-get-merge-heads))
-        (dir default-directory)
+  (let ((dir default-directory)
         (committer-name (git-get-committer-name))
         (committer-email (git-get-committer-email))
         (sign-off git-append-signed-off-by))
@@ -1243,9 +1273,8 @@ Return the list of files that haven't been handled."
                 (or author-email committer-email)
                 (if date (format "Date: %s\n" date) "")
                 (if merge-heads
-                    (format "Parent: %s\n%s\n"
-                            (git-rev-parse "HEAD")
-                            (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n"))
+                    (format "Merge: %s\n"
+                            (mapconcat 'identity merge-heads " "))
                   ""))
         'face 'git-header-face)
        (propertize git-log-msg-separator 'face 'git-separator-face)
@@ -1285,7 +1314,7 @@ Return the list of files that haven't been handled."
             (goto-char (point-min))
             (when (re-search-forward "^Date: \\(.*\\)$" nil t)
               (setq date (match-string 1)))))
-        (git-setup-log-buffer buffer author-name author-email subject date))
+        (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date))
       (if (boundp 'log-edit-diff-function)
          (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)
                                         (log-edit-diff-function . git-log-edit-diff)) buffer)
@@ -1296,11 +1325,13 @@ Return the list of files that haven't been handled."
 
 (defun git-setup-commit-buffer (commit)
   "Setup the commit buffer with the contents of COMMIT."
-  (let (author-name author-email subject date msg)
+  (let (parents 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" "--pretty=medium" commit)
+        (git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit)
         (goto-char (point-min))
+        (when (re-search-forward "^Merge: *\\(.*\\)$" nil t)
+          (setq parents (cdr (split-string (match-string 1) " +"))))
         (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
           (setq author-name (match-string 1))
           (setq author-email (match-string 2)))
@@ -1312,14 +1343,14 @@ Return the list of files that haven't been handled."
         (setq subject (pop msg))
         (while (and msg (zerop (length (car msg))) (pop msg)))))
     (git-setup-log-buffer (get-buffer-create "*git-commit*")
-                          author-name author-email subject date
+                          parents author-name author-email subject date
                           (mapconcat #'identity msg "\n"))))
 
 (defun git-get-commit-files (commit)
   "Retrieve the list of files modified by COMMIT."
   (let (files)
     (with-temp-buffer
-      (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit)
+      (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit)
       (goto-char (point-min))
       (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
         (push (match-string 1) files)))
@@ -1333,10 +1364,11 @@ amended version of it."
   (when (git-empty-db-p) (error "No commit to amend."))
   (let* ((commit (git-rev-parse "HEAD"))
          (files (git-get-commit-files commit)))
-    (when (git-call-process-display-error "reset" "--soft" "HEAD^")
-      (git-update-status-files (copy-sequence files) 'uptodate)
-      (git-mark-files git-status files)
-      (git-refresh-files)
+    (when (if (git-rev-parse "HEAD^")
+              (git-call-process-display-error "reset" "--soft" "HEAD^")
+            (and (git-update-ref "ORIG_HEAD" commit)
+                 (git-update-ref "HEAD" nil commit)))
+      (git-update-status-files files t)
       (git-setup-commit-buffer commit)
       (git-commit-file))))
 
@@ -1377,27 +1409,10 @@ amended version of it."
 (defun git-refresh-status ()
   "Refresh the git status buffer."
   (interactive)
-  (let* ((status git-status)
-         (pos (ewoc-locate status))
-         (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info)))))
-         (cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
-    (unless status (error "Not in git-status buffer."))
-    (message "Refreshing git status...")
-    (git-call-process-env nil nil "update-index" "--refresh")
-    (git-clear-status status)
-    (git-update-status-files nil)
-    ; restore file marks
-    (when marked-files
-      (git-status-filenames-map status
-                                (lambda (info)
-                                        (setf (git-fileinfo->marked info) t)
-                                        (setf (git-fileinfo->needs-refresh info) t))
-                                marked-files)
-      (git-refresh-files))
-    ; move point to the current file name if any
-    (message "Refreshing git status...done")
-    (let ((node (and cur-name (git-find-status-file status cur-name))))
-      (when node (ewoc-goto-node status node)))))
+  (unless git-status (error "Not in git-status buffer."))
+  (message "Refreshing git status...")
+  (git-update-status-files)
+  (message "Refreshing git status...done"))
 
 (defun git-status-quit ()
   "Quit git-status mode."
@@ -1434,6 +1449,7 @@ amended version of it."
     (define-key map "\r"  'git-find-file)
     (define-key map "g"   'git-refresh-status)
     (define-key map "i"   'git-ignore-file)
+    (define-key map "I"   'git-insert-file)
     (define-key map "l"   'git-log-file)
     (define-key map "m"   'git-mark-file)
     (define-key map "M"   'git-mark-all)
@@ -1490,6 +1506,7 @@ amended version of it."
       ["Revert File" git-revert-file t]
       ["Ignore File" git-ignore-file t]
       ["Remove File" git-remove-file t]
+      ["Insert File" git-insert-file t]
       "--------"
       ["Find File" git-find-file t]
       ["View File" git-view-file t]
@@ -1576,8 +1593,8 @@ Meant to be used in `after-save-hook'."
         (let ((filename (file-relative-name file dir)))
           ; skip files located inside the .git directory
           (unless (string-match "^\\.git/" filename)
-            (git-call-process-env nil nil "add" "--refresh" "--" filename)
-            (git-update-status-files (list filename) 'uptodate)))))))
+            (git-call-process nil "add" "--refresh" "--" filename)
+            (git-update-status-files (list filename))))))))
 
 (defun git-help ()
   "Display help for Git mode."
index 9f0a5f92c1f01f82aba14fbf3b92fd56a1e0674b..b44fbfc9b3988ba848aafe109682006254e46b21 100755 (executable)
@@ -981,7 +981,7 @@ class P4Sync(Command):
             if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
                 text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
             elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
-                text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text)
+                text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
 
             contents[stat['depotFile']] = text
 
index cf9ef6ee07244b28ab7725aec8fb595ee613644a..8a4b42dbd732170798bcca4cc79152e760598252 100755 (executable)
@@ -597,6 +597,28 @@ if {[is_Windows]} {
        if {![info exists env(DISPLAY)]} {
                set env(DISPLAY) :9999
        }
+} else {
+       catch {
+               image create photo gitlogo -width 16 -height 16
+
+               gitlogo put #33CC33 -to  7  0  9  2
+               gitlogo put #33CC33 -to  4  2 12  4
+               gitlogo put #33CC33 -to  7  4  9  6
+               gitlogo put #CC3333 -to  4  6 12  8
+               gitlogo put gray26  -to  4  9  6 10
+               gitlogo put gray26  -to  3 10  6 12
+               gitlogo put gray26  -to  8  9 13 11
+               gitlogo put gray26  -to  8 11 10 12
+               gitlogo put gray26  -to 11 11 13 14
+               gitlogo put gray26  -to  3 12  5 14
+               gitlogo put gray26  -to  5 13
+               gitlogo put gray26  -to 10 13
+               gitlogo put gray26  -to  4 14 12 15
+               gitlogo put gray26  -to  5 15 11 16
+               gitlogo redither
+
+               wm iconphoto . -default gitlogo
+       }
 }
 
 ######################################################################
@@ -918,19 +940,25 @@ git-version proc _parse_config {arr_name args} {
 }
 
 proc load_config {include_global} {
-       global repo_config global_config default_config
+       global repo_config global_config system_config default_config
 
        if {$include_global} {
+               _parse_config system_config --system
                _parse_config global_config --global
        }
        _parse_config repo_config
 
        foreach name [array names default_config] {
+               if {[catch {set v $system_config($name)}]} {
+                       set system_config($name) $default_config($name)
+               }
+       }
+       foreach name [array names system_config] {
                if {[catch {set v $global_config($name)}]} {
-                       set global_config($name) $default_config($name)
+                       set global_config($name) $system_config($name)
                }
                if {[catch {set v $repo_config($name)}]} {
-                       set repo_config($name) $default_config($name)
+                       set repo_config($name) $system_config($name)
                }
        }
 }
@@ -1463,10 +1491,8 @@ proc rescan_done {fd buf after} {
        prune_selection
        unlock_index
        display_all_files
-       if {$current_diff_path ne {}} reshow_diff
-       if {$current_diff_path eq {}} select_first_diff
-
-       uplevel #0 $after
+       if {$current_diff_path ne {}} { reshow_diff $after }
+       if {$current_diff_path eq {}} { select_first_diff $after }
 }
 
 proc prune_selection {} {
@@ -1978,16 +2004,16 @@ proc do_rescan {} {
 }
 
 proc ui_do_rescan {} {
-       rescan {force_first_diff; ui_ready}
+       rescan {force_first_diff ui_ready}
 }
 
 proc do_commit {} {
        commit_tree
 }
 
-proc next_diff {} {
+proc next_diff {{after {}}} {
        global next_diff_p next_diff_w next_diff_i
-       show_diff $next_diff_p $next_diff_w {}
+       show_diff $next_diff_p $next_diff_w {} {} $after
 }
 
 proc find_anchor_pos {lst name} {
@@ -2072,25 +2098,42 @@ proc next_diff_after_action {w path {lno {}} {mmask {}}} {
        }
 }
 
-proc select_first_diff {} {
+proc select_first_diff {after} {
        global ui_workdir
 
        if {[find_next_diff $ui_workdir {} 1 {^_?U}] ||
            [find_next_diff $ui_workdir {} 1 {[^O]$}]} {
-               next_diff
+               next_diff $after
+       } else {
+               uplevel #0 $after
        }
 }
 
-proc force_first_diff {} {
-       global current_diff_path
+proc force_first_diff {after} {
+       global ui_workdir current_diff_path file_states
 
        if {[info exists file_states($current_diff_path)]} {
                set state [lindex $file_states($current_diff_path) 0]
+       } else {
+               set state {OO}
+       }
 
-               if {[string index $state 1] ne {O}} return
+       set reselect 0
+       if {[string first {U} $state] >= 0} {
+               # Already a conflict, do nothing
+       } elseif {[find_next_diff $ui_workdir $current_diff_path {} {^_?U}]} {
+               set reselect 1
+       } elseif {[string index $state 1] ne {O}} {
+               # Already a diff & no conflicts, do nothing
+       } elseif {[find_next_diff $ui_workdir $current_diff_path {} {[^O]$}]} {
+               set reselect 1
        }
 
-       select_first_diff
+       if {$reselect} {
+               next_diff $after
+       } else {
+               uplevel #0 $after
+       }
 }
 
 proc toggle_or_diff {w x y} {
@@ -2246,6 +2289,9 @@ if {[is_enabled transport]} {
        .mbar add cascade -label [mc Merge] -menu .mbar.merge
        .mbar add cascade -label [mc Remote] -menu .mbar.remote
 }
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+       .mbar add cascade -label [mc Tools] -menu .mbar.tools
+}
 . configure -menu .mbar
 
 # -- Repository Menu
@@ -2520,6 +2566,20 @@ if {[is_MacOSX]} {
                -command do_options
 }
 
+# -- Tools Menu
+#
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+       set tools_menubar .mbar.tools
+       menu $tools_menubar
+       $tools_menubar add separator
+       $tools_menubar add command -label [mc "Add..."] -command tools_add::dialog
+       $tools_menubar add command -label [mc "Remove..."] -command tools_remove::dialog
+       set tools_tailcnt 3
+       if {[array names repo_config guitool.*.cmd] ne {}} {
+               tools_populate_all
+       }
+}
+
 # -- Help Menu
 #
 .mbar add cascade -label [mc Help] -menu .mbar.help
index 94ee38cccc1ee9a43f8dd1309353609b283d8af5..bbbf15c875f437f486f652d204bcebf1e5f1a8e4 100644 (file)
@@ -16,7 +16,7 @@ proc clear_diff {} {
        $ui_workdir tag remove in_diff 0.0 end
 }
 
-proc reshow_diff {} {
+proc reshow_diff {{after {}}} {
        global file_states file_lists
        global current_diff_path current_diff_side
        global ui_diff
@@ -30,13 +30,13 @@ proc reshow_diff {} {
                || [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
 
                if {[find_next_diff $current_diff_side $p {} {[^O]}]} {
-                       next_diff
+                       next_diff $after
                } else {
                        clear_diff
                }
        } else {
                set save_pos [lindex [$ui_diff yview] 0]
-               show_diff $p $current_diff_side {} $save_pos
+               show_diff $p $current_diff_side {} $save_pos $after
        }
 }
 
index c80c9398786baa63a5023b3e0123a228d148ce34..1d55b49c9bd8182cad2066ecdb81630ca6ad24a6 100644 (file)
@@ -25,7 +25,7 @@ proc config_check_encodings {} {
 
 proc save_config {} {
        global default_config font_descs
-       global repo_config global_config
+       global repo_config global_config system_config
        global repo_config_new global_config_new
        global ui_comm_spell
 
@@ -49,7 +49,7 @@ proc save_config {} {
        foreach name [array names default_config] {
                set value $global_config_new($name)
                if {$value ne $global_config($name)} {
-                       if {$value eq $default_config($name)} {
+                       if {$value eq $system_config($name)} {
                                catch {git config --global --unset $name}
                        } else {
                                regsub -all "\[{}\]" $value {"} value
@@ -284,17 +284,17 @@ proc do_options {} {
 }
 
 proc do_restore_defaults {} {
-       global font_descs default_config repo_config
+       global font_descs default_config repo_config system_config
        global repo_config_new global_config_new
 
        foreach name [array names default_config] {
-               set repo_config_new($name) $default_config($name)
-               set global_config_new($name) $default_config($name)
+               set repo_config_new($name) $system_config($name)
+               set global_config_new($name) $system_config($name)
        }
 
        foreach option $font_descs {
                set name [lindex $option 0]
-               set repo_config(gui.$name) $default_config(gui.$name)
+               set repo_config(gui.$name) $system_config(gui.$name)
        }
        apply_config
 
index 32c8656fc9b15498073a09e516777f71827dc3b1..b371e9a30a00fda0ced979c03a1a5d7856654baa 100644 (file)
@@ -35,7 +35,7 @@ constructor new {i_w i_text args} {
 
        trace add variable searchstring write [cb _incrsearch_cb]
        
-       bind $w <Destroy> [cb delete_this]
+       bind $w <Destroy> [list delete_this $this]
        return $this
 }
 
diff --git a/git-gui/lib/tools.tcl b/git-gui/lib/tools.tcl
new file mode 100644 (file)
index 0000000..6ae63b6
--- /dev/null
@@ -0,0 +1,159 @@
+# git-gui Tools menu implementation
+
+proc tools_list {} {
+       global repo_config
+
+       set names {}
+       foreach item [array names repo_config guitool.*.cmd] {
+               lappend names [string range $item 8 end-4]
+       }
+       return [lsort $names]
+}
+
+proc tools_populate_all {} {
+       global tools_menubar tools_menutbl
+       global tools_tailcnt
+
+       set mbar_end [$tools_menubar index end]
+       set mbar_base [expr {$mbar_end - $tools_tailcnt}]
+       if {$mbar_base >= 0} {
+               $tools_menubar delete 0 $mbar_base
+       }
+
+       array unset tools_menutbl
+
+       foreach fullname [tools_list] {
+               tools_populate_one $fullname
+       }
+}
+
+proc tools_create_item {parent args} {
+       global tools_menubar tools_tailcnt
+       if {$parent eq $tools_menubar} {
+               set pos [expr {[$parent index end]-$tools_tailcnt+1}]
+               eval [list $parent insert $pos] $args
+       } else {
+               eval [list $parent add] $args
+       }
+}
+
+proc tools_populate_one {fullname} {
+       global tools_menubar tools_menutbl tools_id
+
+       if {![info exists tools_id]} {
+               set tools_id 0
+       }
+
+       set names [split $fullname '/']
+       set parent $tools_menubar
+       for {set i 0} {$i < [llength $names]-1} {incr i} {
+               set subname [join [lrange $names 0 $i] '/']
+               if {[info exists tools_menutbl($subname)]} {
+                       set parent $tools_menutbl($subname)
+               } else {
+                       set subid $parent.t$tools_id
+                       tools_create_item $parent cascade \
+                                       -label [lindex $names $i] -menu $subid
+                       menu $subid
+                       set tools_menutbl($subname) $subid
+                       set parent $subid
+                       incr tools_id
+               }
+       }
+
+       tools_create_item $parent command \
+               -label [lindex $names end] \
+               -command [list tools_exec $fullname]
+}
+
+proc tools_exec {fullname} {
+       global repo_config env current_diff_path
+       global current_branch is_detached
+
+       if {[is_config_true "guitool.$fullname.needsfile"]} {
+               if {$current_diff_path eq {}} {
+                       error_popup [mc "Running %s requires a selected file." $fullname]
+                       return
+               }
+       }
+
+       catch { unset env(ARGS) }
+       catch { unset env(REVISION) }
+
+       if {[get_config "guitool.$fullname.revprompt"] ne {} ||
+           [get_config "guitool.$fullname.argprompt"] ne {}} {
+               set dlg [tools_askdlg::dialog $fullname]
+               if {![tools_askdlg::execute $dlg]} {
+                       return
+               }
+       } elseif {[is_config_true "guitool.$fullname.confirm"]} {
+               if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} {
+                       return
+               }
+       }
+
+       set env(GIT_GUITOOL) $fullname
+       set env(FILENAME) $current_diff_path
+       if {$is_detached} {
+               set env(CUR_BRANCH) ""
+       } else {
+               set env(CUR_BRANCH) $current_branch
+       }
+
+       set cmdline $repo_config(guitool.$fullname.cmd)
+       if {[is_config_true "guitool.$fullname.noconsole"]} {
+               tools_run_silent [list sh -c $cmdline] \
+                                [list tools_complete $fullname {}]
+       } else {
+               regsub {/} $fullname { / } title
+               set w [console::new \
+                       [mc "Tool: %s" $title] \
+                       [mc "Running: %s" $cmdline]]
+               console::exec $w [list sh -c $cmdline] \
+                                [list tools_complete $fullname $w]
+       }
+
+       unset env(GIT_GUITOOL)
+       unset env(FILENAME)
+       unset env(CUR_BRANCH)
+       catch { unset env(ARGS) }
+       catch { unset env(REVISION) }
+}
+
+proc tools_run_silent {cmd after} {
+       lappend cmd 2>@1
+       set fd [_open_stdout_stderr $cmd]
+
+       fconfigure $fd -blocking 0 -translation binary
+       fileevent $fd readable [list tools_consume_input $fd $after]
+}
+
+proc tools_consume_input {fd after} {
+       read $fd
+       if {[eof $fd]} {
+               fconfigure $fd -blocking 1
+               if {[catch {close $fd}]} {
+                       uplevel #0 $after 0
+               } else {
+                       uplevel #0 $after 1
+               }
+       }
+}
+
+proc tools_complete {fullname w {ok 1}} {
+       if {$w ne {}} {
+               console::done $w $ok
+       }
+
+       if {$ok} {
+               set msg [mc "Tool completed succesfully: %s" $fullname]
+       } else {
+               set msg [mc "Tool failed: %s" $fullname]
+       }
+
+       if {[is_config_true "guitool.$fullname.norescan"]} {
+               ui_status $msg
+       } else {
+               rescan [list ui_status $msg]
+       }
+}
diff --git a/git-gui/lib/tools_dlg.tcl b/git-gui/lib/tools_dlg.tcl
new file mode 100644 (file)
index 0000000..5f7f08e
--- /dev/null
@@ -0,0 +1,421 @@
+# git-gui Tools menu dialogs
+
+class tools_add {
+
+field w              ; # widget path
+field w_name         ; # new remote name widget
+field w_cmd          ; # new remote location widget
+
+field name         {}; # name of the tool
+field command      {}; # command to execute
+field add_global    0; # add to the --global config
+field no_console    0; # disable using the console
+field needs_file    0; # ensure filename is set
+field confirm       0; # ask for confirmation
+field ask_branch    0; # ask for a revision
+field ask_args      0; # ask for additional args
+
+constructor dialog {} {
+       global repo_config
+
+       make_toplevel top w
+       wm title $top [append "[appname] ([reponame]): " [mc "Add Tool"]]
+       if {$top ne {.}} {
+               wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+               wm transient $top .
+       }
+
+       label $w.header -text [mc "Add New Tool Command"] -font font_uibold
+       pack $w.header -side top -fill x
+
+       frame $w.buttons
+       checkbutton $w.buttons.global \
+               -text [mc "Add globally"] \
+               -variable @add_global
+       pack $w.buttons.global -side left -padx 5
+       button $w.buttons.create -text [mc Add] \
+               -default active \
+               -command [cb _add]
+       pack $w.buttons.create -side right
+       button $w.buttons.cancel -text [mc Cancel] \
+               -command [list destroy $w]
+       pack $w.buttons.cancel -side right -padx 5
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       labelframe $w.desc -text [mc "Tool Details"]
+
+       label $w.desc.name_cmnt -anchor w\
+               -text [mc "Use '/' separators to create a submenu tree:"]
+       grid x $w.desc.name_cmnt -sticky we -padx {0 5} -pady {0 2}
+       label $w.desc.name_l -text [mc "Name:"]
+       set w_name $w.desc.name_t
+       entry $w_name \
+               -borderwidth 1 \
+               -relief sunken \
+               -width 40 \
+               -textvariable @name \
+               -validate key \
+               -validatecommand [cb _validate_name %d %S]
+       grid $w.desc.name_l $w_name -sticky we -padx {0 5}
+
+       label $w.desc.cmd_l -text [mc "Command:"]
+       set w_cmd $w.desc.cmd_t
+       entry $w_cmd \
+               -borderwidth 1 \
+               -relief sunken \
+               -width 40 \
+               -textvariable @command
+       grid $w.desc.cmd_l $w_cmd -sticky we -padx {0 5} -pady {0 3}
+
+       grid columnconfigure $w.desc 1 -weight 1
+       pack $w.desc -anchor nw -fill x -pady 5 -padx 5
+
+       checkbutton $w.confirm \
+               -text [mc "Show a dialog before running"] \
+               -variable @confirm -command [cb _check_enable_dlg]
+
+       labelframe $w.dlg -labelwidget $w.confirm
+
+       checkbutton $w.dlg.askbranch \
+               -text [mc "Ask the user to select a revision (sets \$REVISION)"] \
+               -variable @ask_branch -state disabled
+       pack $w.dlg.askbranch -anchor w -padx 15
+
+       checkbutton $w.dlg.askargs \
+               -text [mc "Ask the user for additional arguments (sets \$ARGS)"] \
+               -variable @ask_args -state disabled
+       pack $w.dlg.askargs -anchor w -padx 15
+
+       pack $w.dlg -anchor nw -fill x -pady {0 8} -padx 5
+
+       checkbutton $w.noconsole \
+               -text [mc "Don't show the command output window"] \
+               -variable @no_console
+       pack $w.noconsole -anchor w -padx 5
+
+       checkbutton $w.needsfile \
+               -text [mc "Run only if a diff is selected (\$FILENAME not empty)"] \
+               -variable @needs_file
+       pack $w.needsfile -anchor w -padx 5
+
+       bind $w <Visibility> [cb _visible]
+       bind $w <Key-Escape> [list destroy $w]
+       bind $w <Key-Return> [cb _add]\;break
+       tkwait window $w
+}
+
+method _check_enable_dlg {} {
+       if {$confirm} {
+               $w.dlg.askbranch configure -state normal
+               $w.dlg.askargs configure -state normal
+       } else {
+               $w.dlg.askbranch configure -state disabled
+               $w.dlg.askargs configure -state disabled
+       }
+}
+
+method _add {} {
+       global repo_config
+
+       if {$name eq {}} {
+               error_popup [mc "Please supply a name for the tool."]
+               focus $w_name
+               return
+       }
+
+       set item "guitool.$name.cmd"
+
+       if {[info exists repo_config($item)]} {
+               error_popup [mc "Tool '%s' already exists." $name]
+               focus $w_name
+               return
+       }
+
+       set cmd [list git config]
+       if {$add_global} { lappend cmd --global }
+       set items {}
+       if {$no_console} { lappend items "guitool.$name.noconsole" }
+       if {$needs_file} { lappend items "guitool.$name.needsfile" }
+       if {$confirm} {
+               if {$ask_args}   { lappend items "guitool.$name.argprompt" }
+               if {$ask_branch} { lappend items "guitool.$name.revprompt" }
+               if {!$ask_args && !$ask_branch} {
+                       lappend items "guitool.$name.confirm"
+               }
+       }
+
+       if {[catch {
+               eval $cmd [list $item $command]
+               foreach citem $items { eval $cmd [list $citem yes] }
+           } err]} {
+               error_popup [mc "Could not add tool:\n%s" $err]
+       } else {
+               set repo_config($item) $command
+               foreach citem $items { set repo_config($citem) yes }
+
+               tools_populate_all
+       }
+
+       destroy $w
+}
+
+method _validate_name {d S} {
+       if {$d == 1} {
+               if {[regexp {[~?*&\[\0\"\\\{]} $S]} {
+                       return 0
+               }
+       }
+       return 1
+}
+
+method _visible {} {
+       grab $w
+       $w_name icursor end
+       focus $w_name
+}
+
+}
+
+class tools_remove {
+
+field w              ; # widget path
+field w_names        ; # name list
+
+constructor dialog {} {
+       global repo_config global_config system_config
+
+       load_config 1
+
+       make_toplevel top w
+       wm title $top [append "[appname] ([reponame]): " [mc "Remove Tool"]]
+       if {$top ne {.}} {
+               wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+               wm transient $top .
+       }
+
+       label $w.header -text [mc "Remove Tool Commands"] -font font_uibold
+       pack $w.header -side top -fill x
+
+       frame $w.buttons
+       button $w.buttons.create -text [mc Remove] \
+               -default active \
+               -command [cb _remove]
+       pack $w.buttons.create -side right
+       button $w.buttons.cancel -text [mc Cancel] \
+               -command [list destroy $w]
+       pack $w.buttons.cancel -side right -padx 5
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       frame $w.list
+       set w_names $w.list.l
+       listbox $w_names \
+               -height 10 \
+               -width 30 \
+               -selectmode extended \
+               -exportselection false \
+               -yscrollcommand [list $w.list.sby set]
+       scrollbar $w.list.sby -command [list $w.list.l yview]
+       pack $w.list.sby -side right -fill y
+       pack $w.list.l -side left -fill both -expand 1
+       pack $w.list -fill both -expand 1 -pady 5 -padx 5
+
+       set local_cnt 0
+       foreach fullname [tools_list] {
+               # Cannot delete system tools
+               if {[info exists system_config(guitool.$fullname.cmd)]} continue
+
+               $w_names insert end $fullname
+               if {![info exists global_config(guitool.$fullname.cmd)]} {
+                       $w_names itemconfigure end -foreground blue
+                       incr local_cnt
+               }
+       }
+
+       if {$local_cnt > 0} {
+               label $w.colorlbl -foreground blue \
+                       -text [mc "(Blue denotes repository-local tools)"]
+               pack $w.colorlbl -fill x -pady 5 -padx 5
+       }
+
+       bind $w <Visibility> [cb _visible]
+       bind $w <Key-Escape> [list destroy $w]
+       bind $w <Key-Return> [cb _remove]\;break
+       tkwait window $w
+}
+
+method _remove {} {
+       foreach i [$w_names curselection] {
+               set name [$w_names get $i]
+
+               catch { git config --remove-section guitool.$name }
+               catch { git config --global --remove-section guitool.$name }
+       }
+
+       load_config 0
+       tools_populate_all
+
+       destroy $w
+}
+
+method _visible {} {
+       grab $w
+       focus $w_names
+}
+
+}
+
+class tools_askdlg {
+
+field w              ; # widget path
+field w_rev        {}; # revision browser
+field w_args       {}; # arguments
+
+field is_ask_args   0; # has arguments field
+field is_ask_revs   0; # has revision browser
+
+field is_ok         0; # ok to start
+field argstr       {}; # arguments
+
+constructor dialog {fullname} {
+       global M1B
+
+       set title [get_config "guitool.$fullname.title"]
+       if {$title eq {}} {
+               regsub {/} $fullname { / } title
+       }
+
+       make_toplevel top w -autodelete 0
+       wm title $top [append "[appname] ([reponame]): " $title]
+       if {$top ne {.}} {
+               wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+               wm transient $top .
+       }
+
+       set prompt [get_config "guitool.$fullname.prompt"]
+       if {$prompt eq {}} {
+               set command [get_config "guitool.$fullname.cmd"]
+               set prompt [mc "Run Command: %s" $command]
+       }
+
+       label $w.header -text $prompt -font font_uibold
+       pack $w.header -side top -fill x
+
+       set argprompt [get_config "guitool.$fullname.argprompt"]
+       set revprompt [get_config "guitool.$fullname.revprompt"]
+
+       set is_ask_args [expr {$argprompt ne {}}]
+       set is_ask_revs [expr {$revprompt ne {}}]
+
+       if {$is_ask_args} {
+               if {$argprompt eq {yes} || $argprompt eq {true} || $argprompt eq {1}} {
+                       set argprompt [mc "Arguments"]
+               }
+
+               labelframe $w.arg -text $argprompt
+
+               set w_args $w.arg.txt
+               entry $w_args \
+                       -borderwidth 1 \
+                       -relief sunken \
+                       -width 40 \
+                       -textvariable @argstr
+               pack $w_args -padx 5 -pady 5 -fill both
+               pack $w.arg -anchor nw -fill both -pady 5 -padx 5
+       }
+
+       if {$is_ask_revs} {
+               if {$revprompt eq {yes} || $revprompt eq {true} || $revprompt eq {1}} {
+                       set revprompt [mc "Revision"]
+               }
+
+               if {[is_config_true "guitool.$fullname.revunmerged"]} {
+                       set w_rev [::choose_rev::new_unmerged $w.rev $revprompt]
+               } else {
+                       set w_rev [::choose_rev::new $w.rev $revprompt]
+               }
+
+               pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
+       }
+
+       frame $w.buttons
+       if {$is_ask_revs} {
+               button $w.buttons.visualize \
+                       -text [mc Visualize] \
+                       -command [cb _visualize]
+               pack $w.buttons.visualize -side left
+       }
+       button $w.buttons.ok \
+               -text [mc OK] \
+               -command [cb _start]
+       pack $w.buttons.ok -side right
+       button $w.buttons.cancel \
+               -text [mc "Cancel"] \
+               -command [cb _cancel]
+       pack $w.buttons.cancel -side right -padx 5
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       bind $w <$M1B-Key-Return> [cb _start]
+       bind $w <Key-Return> [cb _start]
+       bind $w <Key-Escape> [cb _cancel]
+       wm protocol $w WM_DELETE_WINDOW [cb _cancel]
+
+       bind $w <Visibility> [cb _visible]
+       return $this
+}
+
+method execute {} {
+       tkwait window $w
+       set rv $is_ok
+       delete_this
+       return $rv
+}
+
+method _visible {} {
+       grab $w
+       if {$is_ask_args} {
+               focus $w_args
+       } elseif {$is_ask_revs} {
+               $w_rev focus_filter
+       }
+}
+
+method _cancel {} {
+       wm protocol $w WM_DELETE_WINDOW {}
+       destroy $w
+}
+
+method _rev {} {
+       if {[catch {$w_rev commit_or_die}]} {
+               return {}
+       }
+       return [$w_rev get]
+}
+
+method _visualize {} {
+       global current_branch
+       set rev [_rev $this]
+       if {$rev ne {}} {
+               do_gitk [list --left-right "$current_branch...$rev"]
+       }
+}
+
+method _start {} {
+       global env
+
+       if {$is_ask_revs} {
+               set name [_rev $this]
+               if {$name eq {}} {
+                       return
+               }
+               set env(REVISION) $name
+       }
+
+       if {$is_ask_args} {
+               set env(ARGS) $argstr
+       }
+
+       set is_ok 1
+       _cancel $this
+}
+
+}
index e295000e778afaaf5ddf3fcbaf067fa0dfb10fbb..58db67c217e00ee1571d0a182969ce162fad4216 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-02 14:45-0700\n"
+"POT-Creation-Date: 2008-11-16 13:56-0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,33 +16,33 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:688 git-gui.sh:702 git-gui.sh:715 git-gui.sh:798
-#: git-gui.sh:817
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr ""
 
-#: git-gui.sh:644
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr ""
 
-#: git-gui.sh:674
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr ""
 
-#: git-gui.sh:675
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr ""
 
-#: git-gui.sh:689
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr ""
 
-#: git-gui.sh:716
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr ""
 
-#: git-gui.sh:734
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -54,379 +54,444 @@ msgid ""
 "Assume '%s' is version 1.5.0?\n"
 msgstr ""
 
-#: git-gui.sh:972
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr ""
 
-#: git-gui.sh:979
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr ""
 
-#: git-gui.sh:986
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
 msgstr ""
 
-#: git-gui.sh:991
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr ""
 
-#: git-gui.sh:1138 lib/checkout_op.tcl:305
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr ""
 
-#: git-gui.sh:1194
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr ""
 
-#: git-gui.sh:1369 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr ""
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr ""
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr ""
 
-#: git-gui.sh:1635
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr ""
 
-#: git-gui.sh:1637
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr ""
 
-#: git-gui.sh:1638 git-gui.sh:1643
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr ""
 
-#: git-gui.sh:1639 git-gui.sh:1644
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr ""
 
-#: git-gui.sh:1640 git-gui.sh:1645
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr ""
 
-#: git-gui.sh:1642
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr ""
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr ""
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr ""
 
-#: git-gui.sh:1647
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr ""
 
-#: git-gui.sh:1648
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr ""
 
-#: git-gui.sh:1649
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr ""
 
-#: git-gui.sh:1651 git-gui.sh:1652 git-gui.sh:1653 git-gui.sh:1654
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
 msgstr ""
 
-#: git-gui.sh:1689
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
 msgstr ""
 
-#: git-gui.sh:1698
+#: git-gui.sh:1887
 msgid "Couldn't find gitk in PATH"
 msgstr ""
 
-#: git-gui.sh:1948 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr ""
 
-#: git-gui.sh:1949
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr ""
 
-#: git-gui.sh:1951 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr ""
 
-#: git-gui.sh:1954 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr ""
 
-#: git-gui.sh:1957 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr ""
 
-#: git-gui.sh:1958 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr ""
 
-#: git-gui.sh:1967
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr ""
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr ""
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr ""
 
-#: git-gui.sh:1971
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr ""
 
-#: git-gui.sh:1976
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
 msgstr ""
 
-#: git-gui.sh:1980
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
 msgstr ""
 
-#: git-gui.sh:1987
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr ""
 
-#: git-gui.sh:1989
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr ""
 
-#: git-gui.sh:1994 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr ""
 
-#: git-gui.sh:1997 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr ""
 
-#: git-gui.sh:2000
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr ""
 
-#: git-gui.sh:2007 git-gui.sh:2011 git-gui.sh:2015 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr ""
 
-#: git-gui.sh:2023 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr ""
 
-#: git-gui.sh:2031
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr ""
 
-#: git-gui.sh:2034
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr ""
 
-#: git-gui.sh:2038 git-gui.sh:2545
+#: git-gui.sh:2378 git-gui.sh:2923
 msgid "Cut"
 msgstr ""
 
-#: git-gui.sh:2041 git-gui.sh:2548 git-gui.sh:2622 git-gui.sh:2715
+#: git-gui.sh:2381 git-gui.sh:2926 git-gui.sh:3000 git-gui.sh:3082
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr ""
 
-#: git-gui.sh:2044 git-gui.sh:2551
+#: git-gui.sh:2384 git-gui.sh:2929
 msgid "Paste"
 msgstr ""
 
-#: git-gui.sh:2047 git-gui.sh:2554 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2932 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr ""
 
-#: git-gui.sh:2051 git-gui.sh:2558 git-gui.sh:2719 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2936 git-gui.sh:3086 lib/console.tcl:71
 msgid "Select All"
 msgstr ""
 
-#: git-gui.sh:2060
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr ""
 
-#: git-gui.sh:2066
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr ""
 
-#: git-gui.sh:2072
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr ""
 
-#: git-gui.sh:2077 git-gui.sh:2187
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr ""
 
-#: git-gui.sh:2082
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr ""
 
-#: git-gui.sh:2094 git-gui.sh:2491
+#: git-gui.sh:2432
+msgid "Done"
+msgstr ""
+
+#: git-gui.sh:2434
+msgid "Commit@@verb"
+msgstr ""
+
+#: git-gui.sh:2443 git-gui.sh:2864
 msgid "New Commit"
 msgstr ""
 
-#: git-gui.sh:2102 git-gui.sh:2498
+#: git-gui.sh:2451 git-gui.sh:2871
 msgid "Amend Last Commit"
 msgstr ""
 
-#: git-gui.sh:2111 git-gui.sh:2458 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2461 git-gui.sh:2825 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr ""
 
-#: git-gui.sh:2117
+#: git-gui.sh:2467
 msgid "Stage To Commit"
 msgstr ""
 
-#: git-gui.sh:2123
+#: git-gui.sh:2473
 msgid "Stage Changed Files To Commit"
 msgstr ""
 
-#: git-gui.sh:2129
+#: git-gui.sh:2479
 msgid "Unstage From Commit"
 msgstr ""
 
-#: git-gui.sh:2134 lib/index.tcl:395
+#: git-gui.sh:2484 lib/index.tcl:410
 msgid "Revert Changes"
 msgstr ""
 
-#: git-gui.sh:2141 git-gui.sh:2702
+#: git-gui.sh:2491 git-gui.sh:3069
 msgid "Show Less Context"
 msgstr ""
 
-#: git-gui.sh:2145 git-gui.sh:2706
+#: git-gui.sh:2495 git-gui.sh:3073
 msgid "Show More Context"
 msgstr ""
 
-#: git-gui.sh:2151 git-gui.sh:2470 git-gui.sh:2569
+#: git-gui.sh:2502 git-gui.sh:2838 git-gui.sh:2947
 msgid "Sign Off"
 msgstr ""
 
-#: git-gui.sh:2155 git-gui.sh:2474
-msgid "Commit@@verb"
-msgstr ""
-
-#: git-gui.sh:2166
+#: git-gui.sh:2518
 msgid "Local Merge..."
 msgstr ""
 
-#: git-gui.sh:2171
+#: git-gui.sh:2523
 msgid "Abort Merge..."
 msgstr ""
 
-#: git-gui.sh:2183
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr ""
+
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr ""
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr ""
+
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr ""
 
-#: git-gui.sh:2201
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr ""
 
-#: git-gui.sh:2209 git-gui.sh:2740
+#: git-gui.sh:2565 git-gui.sh:3115
 msgid "Options..."
 msgstr ""
 
-#: git-gui.sh:2215 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr ""
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr ""
 
-#: git-gui.sh:2256
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr ""
 
-#: git-gui.sh:2340
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr ""
+
+#: git-gui.sh:2707
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 
-#: git-gui.sh:2373
+#: git-gui.sh:2740
 msgid "Current Branch:"
 msgstr ""
 
-#: git-gui.sh:2394
+#: git-gui.sh:2761
 msgid "Staged Changes (Will Commit)"
 msgstr ""
 
-#: git-gui.sh:2414
+#: git-gui.sh:2781
 msgid "Unstaged Changes"
 msgstr ""
 
-#: git-gui.sh:2464
+#: git-gui.sh:2831
 msgid "Stage Changed"
 msgstr ""
 
-#: git-gui.sh:2480 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2850 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr ""
 
-#: git-gui.sh:2510
+#: git-gui.sh:2885
 msgid "Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2511
+#: git-gui.sh:2886
 msgid "Amended Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2512
+#: git-gui.sh:2887
 msgid "Amended Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2513
+#: git-gui.sh:2888
 msgid "Amended Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2514
+#: git-gui.sh:2889
 msgid "Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2515
+#: git-gui.sh:2890
 msgid "Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2561 git-gui.sh:2723 lib/console.tcl:73
+#: git-gui.sh:2939 git-gui.sh:3090 lib/console.tcl:73
 msgid "Copy All"
 msgstr ""
 
-#: git-gui.sh:2585 lib/blame.tcl:100
+#: git-gui.sh:2963 lib/blame.tcl:104
 msgid "File:"
 msgstr ""
 
-#: git-gui.sh:2691
+#: git-gui.sh:3078
+msgid "Refresh"
+msgstr ""
+
+#: git-gui.sh:3099
+msgid "Decrease Font Size"
+msgstr ""
+
+#: git-gui.sh:3103
+msgid "Increase Font Size"
+msgstr ""
+
+#: git-gui.sh:3111 lib/blame.tcl:281
+msgid "Encoding"
+msgstr ""
+
+#: git-gui.sh:3122
 msgid "Apply/Reverse Hunk"
 msgstr ""
 
-#: git-gui.sh:2696
+#: git-gui.sh:3127
 msgid "Apply/Reverse Line"
 msgstr ""
 
-#: git-gui.sh:2711
-msgid "Refresh"
+#: git-gui.sh:3137
+msgid "Run Merge Tool"
 msgstr ""
 
-#: git-gui.sh:2732
-msgid "Decrease Font Size"
+#: git-gui.sh:3142
+msgid "Use Remote Version"
 msgstr ""
 
-#: git-gui.sh:2736
-msgid "Increase Font Size"
+#: git-gui.sh:3146
+msgid "Use Local Version"
+msgstr ""
+
+#: git-gui.sh:3150
+msgid "Revert To Base"
 msgstr ""
 
-#: git-gui.sh:2747
+#: git-gui.sh:3169
 msgid "Unstage Hunk From Commit"
 msgstr ""
 
-#: git-gui.sh:2748
+#: git-gui.sh:3170
 msgid "Unstage Line From Commit"
 msgstr ""
 
-#: git-gui.sh:2750
+#: git-gui.sh:3172
 msgid "Stage Hunk For Commit"
 msgstr ""
 
-#: git-gui.sh:2751
+#: git-gui.sh:3173
 msgid "Stage Line For Commit"
 msgstr ""
 
-#: git-gui.sh:2771
+#: git-gui.sh:3196
 msgid "Initializing..."
 msgstr ""
 
-#: git-gui.sh:2876
+#: git-gui.sh:3301
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -437,14 +502,14 @@ msgid ""
 "\n"
 msgstr ""
 
-#: git-gui.sh:2906
+#: git-gui.sh:3331
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
 "Tcl binary distributed by Cygwin."
 msgstr ""
 
-#: git-gui.sh:2911
+#: git-gui.sh:3336
 #, tcl-format
 msgid ""
 "\n"
@@ -459,80 +524,108 @@ msgstr ""
 msgid "git-gui - a graphical user interface for Git."
 msgstr ""
 
-#: lib/blame.tcl:70
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr ""
 
-#: lib/blame.tcl:74
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr ""
 
-#: lib/blame.tcl:257
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr ""
 
-#: lib/blame.tcl:260
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr ""
+
+#: lib/blame.tcl:284
 msgid "Do Full Copy Detection"
 msgstr ""
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr ""
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr ""
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr ""
 
-#: lib/blame.tcl:492
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr ""
 
-#: lib/blame.tcl:512
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr ""
 
-#: lib/blame.tcl:704
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr ""
 
-#: lib/blame.tcl:707
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr ""
 
-#: lib/blame.tcl:737
+#: lib/blame.tcl:802
 msgid "Busy"
 msgstr ""
 
-#: lib/blame.tcl:738
+#: lib/blame.tcl:803
 msgid "Annotation process is already running."
 msgstr ""
 
-#: lib/blame.tcl:777
+#: lib/blame.tcl:842
 msgid "Running thorough copy detection..."
 msgstr ""
 
-#: lib/blame.tcl:827
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr ""
 
-#: lib/blame.tcl:883
+#: lib/blame.tcl:964
 msgid "Author:"
 msgstr ""
 
-#: lib/blame.tcl:887
+#: lib/blame.tcl:968
 msgid "Committer:"
 msgstr ""
 
-#: lib/blame.tcl:892
+#: lib/blame.tcl:973
 msgid "Original File:"
 msgstr ""
 
-#: lib/blame.tcl:1006
+#: lib/blame.tcl:1021
+msgid "Cannot find HEAD commit:"
+msgstr ""
+
+#: lib/blame.tcl:1076
+msgid "Cannot find parent commit:"
+msgstr ""
+
+#: lib/blame.tcl:1091
+msgid "Unable to display parent"
+msgstr ""
+
+#: lib/blame.tcl:1092 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr ""
+
+#: lib/blame.tcl:1232
 msgid "Originally By:"
 msgstr ""
 
-#: lib/blame.tcl:1012
+#: lib/blame.tcl:1238
 msgid "In File:"
 msgstr ""
 
-#: lib/blame.tcl:1017
+#: lib/blame.tcl:1243
 msgid "Copied Or Moved Here By:"
 msgstr ""
 
@@ -546,16 +639,18 @@ msgstr ""
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:97
 msgid "Cancel"
 msgstr ""
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr ""
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:244
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr ""
 
@@ -575,7 +670,7 @@ msgstr ""
 msgid "Create New Branch"
 msgstr ""
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr ""
 
@@ -583,7 +678,7 @@ msgstr ""
 msgid "Branch Name"
 msgstr ""
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr ""
 
@@ -723,9 +818,9 @@ msgstr ""
 msgid "Browse Branch Files"
 msgstr ""
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:472 lib/choose_repository.tcl:482
-#: lib/choose_repository.tcl:985
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr ""
 
@@ -740,6 +835,7 @@ msgid "fatal: Cannot resolve %s"
 msgstr ""
 
 #: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr ""
 
@@ -836,7 +932,7 @@ msgstr ""
 msgid "Reset '%s'?"
 msgstr ""
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr ""
 
@@ -877,221 +973,225 @@ msgstr ""
 msgid "Git Gui"
 msgstr ""
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr ""
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:458
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr ""
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:974
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr ""
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr ""
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr ""
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr ""
 
-#: lib/choose_repository.tcl:410 lib/choose_repository.tcl:535
-#: lib/choose_repository.tcl:1007
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:435
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:453
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr ""
 
-#: lib/choose_repository.tcl:466
-msgid "URL:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
 msgstr ""
 
-#: lib/choose_repository.tcl:487
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr ""
+
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr ""
 
-#: lib/choose_repository.tcl:493
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr ""
 
-#: lib/choose_repository.tcl:499
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:505
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:541 lib/choose_repository.tcl:588
-#: lib/choose_repository.tcl:734 lib/choose_repository.tcl:804
-#: lib/choose_repository.tcl:1013 lib/choose_repository.tcl:1021
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:577
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:581
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:602
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:613
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr ""
 
-#: lib/choose_repository.tcl:625
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:650
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:686
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr ""
 
-#: lib/choose_repository.tcl:688 lib/choose_repository.tcl:902
-#: lib/choose_repository.tcl:914
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr ""
 
-#: lib/choose_repository.tcl:701
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr ""
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:744
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:745
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr ""
 
-#: lib/choose_repository.tcl:769
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:780
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:843
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:878
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:893
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:900
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr ""
 
-#: lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr ""
 
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr ""
 
-#: lib/choose_repository.tcl:924 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr ""
 
-#: lib/choose_repository.tcl:953
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:969
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr ""
 
-#: lib/choose_repository.tcl:979
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr ""
@@ -1176,7 +1276,7 @@ msgid ""
 "The rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:156
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1185,7 +1285,7 @@ msgid ""
 "before committing.\n"
 msgstr ""
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:164
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1193,14 +1293,14 @@ msgid ""
 "File %s cannot be committed by this program.\n"
 msgstr ""
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:172
 msgid ""
 "No changes to commit.\n"
 "\n"
 "You must stage at least 1 file before you can commit.\n"
 msgstr ""
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:187
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1211,45 +1311,45 @@ msgid ""
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:211
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr ""
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:227
 msgid "Calling pre-commit hook..."
 msgstr ""
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:242
 msgid "Commit declined by pre-commit hook."
 msgstr ""
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:265
 msgid "Calling commit-msg hook..."
 msgstr ""
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:280
 msgid "Commit declined by commit-msg hook."
 msgstr ""
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:293
 msgid "Committing changes..."
 msgstr ""
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:309
 msgid "write-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:310 lib/commit.tcl:354 lib/commit.tcl:374
 msgid "Commit failed."
 msgstr ""
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:327
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr ""
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:332
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1258,19 +1358,19 @@ msgid ""
 "A rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:339
 msgid "No changes to commit."
 msgstr ""
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:353
 msgid "commit-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:373
 msgid "update-ref failed:"
 msgstr ""
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:461
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr ""
@@ -1339,7 +1439,7 @@ msgstr ""
 msgid "Invalid date from Git: %s"
 msgstr ""
 
-#: lib/diff.tcl:44
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1353,48 +1453,92 @@ msgid ""
 "the same state."
 msgstr ""
 
-#: lib/diff.tcl:83
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr ""
 
-#: lib/diff.tcl:116 lib/diff.tcl:190
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr ""
 
-#: lib/diff.tcl:117
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr ""
 
-#: lib/diff.tcl:124
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr ""
 
-#: lib/diff.tcl:136
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr ""
 
-#: lib/diff.tcl:191
-msgid "Error loading diff:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
 msgstr ""
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr ""
 
-#: lib/diff.tcl:320
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr ""
 
-#: lib/diff.tcl:386
+#: lib/diff.tcl:509
 msgid "Failed to unstage selected line."
 msgstr ""
 
-#: lib/diff.tcl:394
+#: lib/diff.tcl:517
 msgid "Failed to stage selected line."
 msgstr ""
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr ""
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr ""
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr ""
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr ""
@@ -1429,38 +1573,47 @@ msgstr ""
 msgid "Unlock Index"
 msgstr ""
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr ""
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr ""
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr ""
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr ""
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr ""
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr ""
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr ""
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr ""
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1478,7 +1631,7 @@ msgid ""
 "The rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1489,7 +1642,7 @@ msgid ""
 "merge.  Only then can you begin another merge.\n"
 msgstr ""
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1500,41 +1653,41 @@ msgid ""
 "will help you abort a failed merge, should the need arise.\n"
 msgstr ""
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr ""
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr ""
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr ""
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr ""
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr ""
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr ""
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
 "You must finish amending this commit.\n"
 msgstr ""
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1543,7 +1696,7 @@ msgid ""
 "Continue with aborting the current merge?"
 msgstr ""
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1552,130 +1705,312 @@ msgid ""
 "Continue with resetting the current changes?"
 msgstr ""
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr ""
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr ""
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr ""
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr ""
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr ""
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr ""
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr ""
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr ""
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr ""
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr ""
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr ""
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr ""
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr ""
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr ""
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr ""
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr ""
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr ""
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr ""
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr ""
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr ""
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr ""
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr ""
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr ""
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr ""
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr ""
 
-#: lib/option.tcl:123
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr ""
+
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr ""
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr ""
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr ""
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
 msgid "Blame Copy Only On Changed Files"
 msgstr ""
 
-#: lib/option.tcl:127
+#: lib/option.tcl:150
 msgid "Minimum Letters To Blame Copy On"
 msgstr ""
 
-#: lib/option.tcl:128
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr ""
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr ""
 
-#: lib/option.tcl:129
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr ""
 
-#: lib/option.tcl:130
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr ""
 
-#: lib/option.tcl:194
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr ""
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr ""
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr ""
 
-#: lib/option.tcl:218
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr ""
 
-#: lib/option.tcl:222
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr ""
 
-#: lib/option.tcl:228
+#: lib/option.tcl:264
 msgid "pt."
 msgstr ""
 
-#: lib/option.tcl:242
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr ""
 
-#: lib/option.tcl:277
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr ""
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr ""
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr ""
 
-#: lib/remote.tcl:170
+#: lib/remote.tcl:173
 msgid "Fetch from"
 msgstr ""
 
-#: lib/remote.tcl:213
+#: lib/remote.tcl:215
 msgid "Push to"
 msgstr ""
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr ""
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr ""
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr ""
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr ""
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr ""
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr ""
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr ""
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr ""
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr ""
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr ""
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr ""
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr ""
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr ""
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr ""
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr ""
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#, tcl-format
+msgid "push %s"
+msgstr ""
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr ""
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
+msgid "Delete Branch Remotely"
 msgstr ""
 
 #: lib/remote_branch_delete.tcl:47
@@ -1687,7 +2022,7 @@ msgid "Remote:"
 msgstr ""
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
+msgid "Arbitrary Location:"
 msgstr ""
 
 #: lib/remote_branch_delete.tcl:84
@@ -1750,6 +2085,22 @@ msgstr ""
 msgid "Scanning %s..."
 msgstr ""
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr ""
+
+#: lib/search.tcl:23
+msgid "Next"
+msgstr ""
+
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr ""
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr ""
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr ""
@@ -1787,22 +2138,182 @@ msgstr ""
 msgid "No Suggestions"
 msgstr ""
 
-#: lib/spellcheck.tcl:387
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr ""
 
-#: lib/spellcheck.tcl:391
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr ""
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr ""
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr ""
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr ""
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr ""
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr ""
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr ""
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr ""
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr ""
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr ""
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr ""
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
+msgid "Running %s requires a selected file."
+msgstr ""
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr ""
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr ""
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr ""
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr ""
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr ""
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr ""
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr ""
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr ""
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr ""
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr ""
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr ""
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr ""
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr ""
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr ""
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr ""
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr ""
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr ""
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr ""
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr ""
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr ""
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
 msgstr ""
 
 #: lib/transport.tcl:7
@@ -1820,11 +2331,6 @@ msgstr ""
 msgid "Pruning tracking branches deleted from %s"
 msgstr ""
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr ""
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
index 664fe34419e9aeef1af8fac820cc423a641f5525..1cac898a2443538144d45006b9adb3c0aaf2d1fa 100755 (executable)
@@ -16,13 +16,17 @@ cd_to_toplevel
 test -z "$(git ls-files -u)" ||
        die "You are in the middle of a conflicted merge."
 
-strategy_args= no_stat= no_commit= squash= no_ff= log_arg=
+strategy_args= no_stat= no_commit= squash= no_ff= log_arg= verbosity=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
 while :
 do
        case "$1" in
+       -q|--quiet)
+               verbosity="$verbosity -q" ;;
+       -v|--verbose)
+               verbosity="$verbosity -v" ;;
        -n|--no-stat|--no-summary)
                no_stat=-n ;;
        --stat|--summary)
@@ -121,7 +125,7 @@ test true = "$rebase" && {
                "refs/remotes/$origin/$reflist" 2>/dev/null)"
 }
 orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
-git fetch --update-head-ok "$@" || exit 1
+git fetch $verbosity --update-head-ok "$@" || exit 1
 
 curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
 if test -n "$orig_head" && test "$curr_head" != "$orig_head"
@@ -182,4 +186,4 @@ test true = "$rebase" &&
        exec git-rebase $strategy_args --onto $merge_head \
        ${oldremoteref:-$merge_head}
 exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \
-       "$merge_name" HEAD $merge_head
+       "$merge_name" HEAD $merge_head $verbosity
index 4d313d136e64678b99179b2e1dce7a1beaa36d04..458a497af810c7bb188a5aafb80c32aa0bc05264 100755 (executable)
@@ -71,7 +71,8 @@ case ",$all_into_one," in
                                existing="$existing $e"
                        fi
                done
-               if test -n "$args" -a -n "$unpack_unreachable"
+               if test -n "$args" -a -n "$unpack_unreachable" -a \
+                       -n "$remove_redundant"
                then
                        args="$args $unpack_unreachable"
                fi
index 073a314c8043e0ff30afde65e012e356ff0d186f..a2cf5b82150a77fd9ddb775fea1bc8d5e8ee7392 100755 (executable)
@@ -4,9 +4,9 @@
 # This file is licensed under the GPL v2, or a later version
 # at the discretion of Linus Torvalds.
 
-USAGE='<commit> <url> [<head>]'
-LONG_USAGE='Summarizes the changes since <commit> to the standard output,
-and includes <url> in the message generated.'
+USAGE='<start> <url> [<end>]'
+LONG_USAGE='Summarizes the changes between two commits to the standard output,
+and includes the given URL in the generated summary.'
 SUBDIRECTORY_OK='Yes'
 OPTIONS_SPEC=
 . git-sh-setup
index 94ca5c89ad086bac754fb6b6b907fd4e968e8024..7508f8ff242854462d0b5b505621dd840b551641 100755 (executable)
 use Getopt::Long;
 use Data::Dumper;
 use Term::ANSIColor;
+use File::Temp qw/ tempdir /;
+use Error qw(:try);
 use Git;
 
+Getopt::Long::Configure qw/ pass_through /;
+
 package FakeTerm;
 sub new {
        my ($class, $reason) = @_;
@@ -38,7 +42,7 @@ package main;
 
 sub usage {
        print <<EOT;
-git send-email [options] <file | directory>...
+git send-email [options] <file | directory | rev-list options >
 
   Composing:
     --from                  <str>  * Email From:
@@ -47,6 +51,7 @@ sub usage {
     --bcc                   <str>  * Email Bcc:
     --subject               <str>  * Email "Subject:"
     --in-reply-to           <str>  * Email "In-Reply-To:"
+    --annotate                     * Review each patch that will be sent in an editor.
     --compose                      * Open an editor for introduction.
 
   Sending:
@@ -73,6 +78,8 @@ sub usage {
     --quiet                        * Output one line of info per email.
     --dry-run                      * Don't actually send the emails.
     --[no-]validate                * Perform patch sanity checks. Default on.
+    --[no-]format-patch            * understand any non optional arguments as
+                                     `git format-patch` ones.
 
 EOT
        exit(1);
@@ -124,12 +131,10 @@ sub format_2822_time {
 sub unique_email_list(@);
 sub cleanup_compose_files();
 
-# Constants (essentially)
-my $compose_filename = ".msg.$$";
-
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-       $initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
+       $initial_reply_to,$initial_subject,@files,
+       $author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
 my $envelope_sender;
 
@@ -149,6 +154,27 @@ sub format_2822_time {
 
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
+my $format_patch;
+my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
+
+# Handle interactive edition of files.
+my $multiedit;
+my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+sub do_edit {
+       if (defined($multiedit) && !$multiedit) {
+               map {
+                       system('sh', '-c', $editor.' "$@"', $editor, $_);
+                       if (($? & 127) || ($? >> 8)) {
+                               die("the editor exited uncleanly, aborting everything");
+                       }
+               } @_;
+       } else {
+               system('sh', '-c', $editor.' "$@"', $editor, @_);
+               if (($? & 127) || ($? >> 8)) {
+                       die("the editor exited uncleanly, aborting everything");
+               }
+       }
+}
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
@@ -179,6 +205,7 @@ sub format_2822_time {
     "aliasesfile" => \@alias_files,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
+    "multiedit" => \$multiedit,
 );
 
 # Handle Uncouth Termination
@@ -221,6 +248,7 @@ sub signal_handler {
                    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
                    "smtp-encryption=s" => \$smtp_encryption,
                    "identity=s" => \$identity,
+                   "annotate" => \$annotate,
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
@@ -231,6 +259,7 @@ sub signal_handler {
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
                    "validate!" => \$validate,
+                   "format-patch!" => \$format_patch,
         );
 
 unless ($rc) {
@@ -345,10 +374,13 @@ sub read_config {
                        # spaces delimit multiple addresses
                        $aliases{$1} = [ split(/\s+/, $2) ];
                }}},
-       pine => sub { my $fh = shift; while (<$fh>) {
-               if (/^(\S+)\t.*\t(.*)$/) {
+       pine => sub { my $fh = shift; my $f='\t[^\t]*';
+               for (my $x = ''; defined($x); $x = $_) {
+                       chomp $x;
+                       $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);
+                       $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
                        $aliases{$1} = [ split(/\s*,\s*/, $2) ];
-               }}},
+               }},
        gnus => sub { my $fh = shift; while (<$fh>) {
                if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
                        $aliases{$1} = [ $2 ];
@@ -365,23 +397,52 @@ sub read_config {
 
 ($sender) = expand_aliases($sender) if defined $sender;
 
+# returns 1 if the conflict must be solved using it as a format-patch argument
+sub check_file_rev_conflict($) {
+       my $f = shift;
+       try {
+               $repo->command('rev-parse', '--verify', '--quiet', $f);
+               if (defined($format_patch)) {
+                       print "foo\n";
+                       return $format_patch;
+               }
+               die(<<EOF);
+File '$f' exists but it could also be the range of commits
+to produce patches for.  Please disambiguate by...
+
+    * Saying "./$f" if you mean a file; or
+    * Giving --format-patch option if you mean a range.
+EOF
+       } catch Git::Error::Command with {
+               return 0;
+       }
+}
+
 # Now that all the defaults are set, process the rest of the command line
 # arguments and collect up the files that need to be processed.
-for my $f (@ARGV) {
-       if (-d $f) {
+my @rev_list_opts;
+while (my $f = pop @ARGV) {
+       if ($f eq "--") {
+               push @rev_list_opts, "--", @ARGV;
+               @ARGV = ();
+       } elsif (-d $f and !check_file_rev_conflict($f)) {
                opendir(DH,$f)
                        or die "Failed to opendir $f: $!";
 
                push @files, grep { -f $_ } map { +$f . "/" . $_ }
                                sort readdir(DH);
                closedir(DH);
-       } elsif (-f $f or -p $f) {
+       } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
                push @files, $f;
        } else {
-               print STDERR "Skipping $f - not found.\n";
+               push @rev_list_opts, $f;
        }
 }
 
+if (@rev_list_opts) {
+       push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
+}
+
 if ($validate) {
        foreach my $f (@files) {
                unless (-p $f) {
@@ -400,6 +461,108 @@ sub read_config {
        usage();
 }
 
+sub get_patch_subject($) {
+       my $fn = shift;
+       open (my $fh, '<', $fn);
+       while (my $line = <$fh>) {
+               next unless ($line =~ /^Subject: (.*)$/);
+               close $fh;
+               return "GIT: $1\n";
+       }
+       close $fh;
+       die "No subject line in $fn ?";
+}
+
+if ($compose) {
+       # Note that this does not need to be secure, but we will make a small
+       # effort to have it be unique
+       open(C,">",$compose_filename)
+               or die "Failed to open for writing $compose_filename: $!";
+
+
+       my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
+       my $tpl_subject = $initial_subject || '';
+       my $tpl_reply_to = $initial_reply_to || '';
+
+       print C <<EOT;
+From $tpl_sender # This line is ignored.
+GIT: Lines beginning in "GIT: " will be removed.
+GIT: Consider including an overall diffstat or table of contents
+GIT: for the patch you are writing.
+GIT:
+GIT: Clear the body content if you don't wish to send a summary.
+From: $tpl_sender
+Subject: $tpl_subject
+In-Reply-To: $tpl_reply_to
+
+EOT
+       for my $f (@files) {
+               print C get_patch_subject($f);
+       }
+       close(C);
+
+       my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+
+       if ($annotate) {
+               do_edit($compose_filename, @files);
+       } else {
+               do_edit($compose_filename);
+       }
+
+       open(C2,">",$compose_filename . ".final")
+               or die "Failed to open $compose_filename.final : " . $!;
+
+       open(C,"<",$compose_filename)
+               or die "Failed to open $compose_filename : " . $!;
+
+       my $need_8bit_cte = file_has_nonascii($compose_filename);
+       my $in_body = 0;
+       my $summary_empty = 1;
+       while(<C>) {
+               next if m/^GIT: /;
+               if ($in_body) {
+                       $summary_empty = 0 unless (/^\n$/);
+               } elsif (/^\n$/) {
+                       $in_body = 1;
+                       if ($need_8bit_cte) {
+                               print C2 "MIME-Version: 1.0\n",
+                                        "Content-Type: text/plain; ",
+                                          "charset=utf-8\n",
+                                        "Content-Transfer-Encoding: 8bit\n";
+                       }
+               } elsif (/^MIME-Version:/i) {
+                       $need_8bit_cte = 0;
+               } elsif (/^Subject:\s*(.+)\s*$/i) {
+                       $initial_subject = $1;
+                       my $subject = $initial_subject;
+                       $_ = "Subject: " .
+                               ($subject =~ /[^[:ascii:]]/ ?
+                                quote_rfc2047($subject) :
+                                $subject) .
+                               "\n";
+               } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+                       $initial_reply_to = $1;
+                       next;
+               } elsif (/^From:\s*(.+)\s*$/i) {
+                       $sender = $1;
+                       next;
+               } elsif (/^(?:To|Cc|Bcc):/i) {
+                       print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
+                       next;
+               }
+               print C2 $_;
+       }
+       close(C);
+       close(C2);
+
+       if ($summary_empty) {
+               print "Summary email is empty, skipping it\n";
+               $compose = -1;
+       }
+} elsif ($annotate) {
+       do_edit(@files);
+}
+
 my $prompting = 0;
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
@@ -444,17 +607,6 @@ sub expand_aliases {
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
-if (!defined $initial_subject && $compose) {
-       while (1) {
-               $_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
-               last if defined $_;
-               print "\n";
-       }
-
-       $initial_subject = $_;
-       $prompting++;
-}
-
 if ($thread && !defined $initial_reply_to && $prompting) {
        while (1) {
                $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
@@ -481,59 +633,6 @@ sub expand_aliases {
 }
 
 if ($compose) {
-       # Note that this does not need to be secure, but we will make a small
-       # effort to have it be unique
-       open(C,">",$compose_filename)
-               or die "Failed to open for writing $compose_filename: $!";
-       print C "From $sender # This line is ignored.\n";
-       printf C "Subject: %s\n\n", $initial_subject;
-       printf C <<EOT;
-GIT: Please enter your email below.
-GIT: Lines beginning in "GIT: " will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-
-EOT
-       close(C);
-
-       my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-       system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
-
-       open(C2,">",$compose_filename . ".final")
-               or die "Failed to open $compose_filename.final : " . $!;
-
-       open(C,"<",$compose_filename)
-               or die "Failed to open $compose_filename : " . $!;
-
-       my $need_8bit_cte = file_has_nonascii($compose_filename);
-       my $in_body = 0;
-       while(<C>) {
-               next if m/^GIT: /;
-               if (!$in_body && /^\n$/) {
-                       $in_body = 1;
-                       if ($need_8bit_cte) {
-                               print C2 "MIME-Version: 1.0\n",
-                                        "Content-Type: text/plain; ",
-                                          "charset=utf-8\n",
-                                        "Content-Transfer-Encoding: 8bit\n";
-                       }
-               }
-               if (!$in_body && /^MIME-Version:/i) {
-                       $need_8bit_cte = 0;
-               }
-               if (!$in_body && /^Subject: ?(.*)/i) {
-                       my $subject = $1;
-                       $_ = "Subject: " .
-                               ($subject =~ /[^[:ascii:]]/ ?
-                                quote_rfc2047($subject) :
-                                $subject) .
-                               "\n";
-               }
-               print C2 $_;
-       }
-       close(C);
-       close(C2);
-
        while (1) {
                $_ = $term->readline("Send this email? (y|n) ");
                last if defined $_;
@@ -545,7 +644,9 @@ sub expand_aliases {
                exit(0);
        }
 
-       @files = ($compose_filename . ".final", @files);
+       if ($compose > 0) {
+               @files = ($compose_filename . ".final", @files);
+       }
 }
 
 # Variables we set as part of the loop over files
index 86075ec616418f77e42dd72593a7efd00e1200c3..914c707a90e5755879574b2717c064fd9364d054 100755 (executable)
@@ -3332,11 +3332,11 @@ sub change_file_prop {
 
 sub apply_textdelta {
        my ($self, $fb, $exp) = @_;
-       my $fh = Git::temp_acquire('svn_delta');
+       my $fh = $::_repository->temp_acquire('svn_delta');
        # $fh gets auto-closed() by SVN::TxDelta::apply(),
        # (but $base does not,) so dup() it for reading in close_file
        open my $dup, '<&', $fh or croak $!;
-       my $base = Git::temp_acquire('git_blob');
+       my $base = $::_repository->temp_acquire('git_blob');
        if ($fb->{blob}) {
                print $base 'link ' if ($fb->{mode_a} == 120000);
                my $size = $::_repository->cat_blob($fb->{blob}, $base);
@@ -3377,7 +3377,8 @@ sub close_file {
                                warn "$path has mode 120000",
                                                " but is not a link\n";
                        } else {
-                               my $tmp_fh = Git::temp_acquire('svn_hash');
+                               my $tmp_fh = $::_repository->temp_acquire(
+                                       'svn_hash');
                                my $res;
                                while ($res = sysread($fh, my $str, 1024)) {
                                        my $out = syswrite($tmp_fh, $str, $res);
@@ -3765,7 +3766,7 @@ sub change_file_prop {
 
 sub _chg_file_get_blob ($$$$) {
        my ($self, $fbat, $m, $which) = @_;
-       my $fh = Git::temp_acquire("git_blob_$which");
+       my $fh = $::_repository->temp_acquire("git_blob_$which");
        if ($m->{"mode_$which"} =~ /^120/) {
                print $fh 'link ' or croak $!;
                $self->change_file_prop($fbat,'svn:special','*');
index 5444f0860b2150d440dfbd5a4be4aa2daa1316fc..194ddb13da09668334cd9e1f6eeef517c3346ee9 100644 (file)
@@ -58,7 +58,7 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
        struct name_decoration *decoration;
 
        if (opt->show_source && commit->util)
-               printf(" %s", (char *) commit->util);
+               printf("\t%s", (char *) commit->util);
        if (!opt->show_decorations)
                return;
        decoration = lookup_decoration(&name_decoration, &commit->object);
index fd08bb425c241a0861588ec0aedd15041095a95f..9eb55cc8b5182495b687e133a938937db1bc3949 100644 (file)
@@ -484,6 +484,28 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
        return 0;
 }
 
+int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
+                          int unset)
+{
+       int *target = opt->value;
+
+       if (unset)
+               /* --no-quiet, --no-verbose */
+               *target = 0;
+       else if (opt->short_name == 'v') {
+               if (*target >= 0)
+                       (*target)++;
+               else
+                       *target = 1;
+       } else {
+               if (*target <= 0)
+                       (*target)--;
+               else
+                       *target = -1;
+       }
+       return 0;
+}
+
 /*
  * This should really be OPTION_FILENAME type as a part of
  * parse_options that take prefix to do this while parsing.
index 5199950c006df4625355ce227970cc3e8a72ed41..034162ec6975cfca8dedadc4eb541212b0d97e43 100644 (file)
@@ -150,9 +150,15 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx);
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 
 #define OPT__VERBOSE(var)  OPT_BOOLEAN('v', "verbose", (var), "be verbose")
 #define OPT__QUIET(var)    OPT_BOOLEAN('q', "quiet",   (var), "be quiet")
+#define OPT__VERBOSITY(var) \
+       { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
+         PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
+       { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
+         PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
 #define OPT__DRY_RUN(var)  OPT_BOOLEAN('n', "dry-run", (var), "dry run")
 #define OPT__ABBREV(var)  \
        { OPTION_CALLBACK, 0, "abbrev", (var), "n", \
index ba94453781c98a97a95c65999873fc28b013340d..dde9105df8464911451830321e4da1cbae924955 100644 (file)
@@ -961,9 +961,7 @@ sub _close_cat_blob {
 =cut
 
 sub temp_acquire {
-       my ($self, $name) = _maybe_self(@_);
-
-       my $temp_fd = _temp_cache($name);
+       my $temp_fd = _temp_cache(@_);
 
        $TEMP_FILES{$temp_fd}{locked} = 1;
        $temp_fd;
@@ -1005,7 +1003,7 @@ sub temp_release {
 }
 
 sub _temp_cache {
-       my ($name) = @_;
+       my ($self, $name) = _maybe_self(@_);
 
        _verify_require();
 
@@ -1022,9 +1020,16 @@ sub _temp_cache {
                                "' was closed. Opening replacement.";
                }
                my $fname;
+
+               my $tmpdir;
+               if (defined $self) {
+                       $tmpdir = $self->repo_path();
+               }
+
                ($$temp_fd, $fname) = File::Temp->tempfile(
-                       'Git_XXXXXX', UNLINK => 1
+                       'Git_XXXXXX', UNLINK => 1, DIR => $tmpdir,
                        ) or throw Error::Simple("couldn't open new temp file");
+
                $$temp_fd->autoflush;
                binmode $$temp_fd;
                $TEMP_FILES{$$temp_fd}{fname} = $fname;
index 9dc55d4003301c0ededbd6059e16cabf5684766b..db60f06c98137f6e6e95727450d2842a0d4fb2a6 100644 (file)
@@ -995,7 +995,7 @@ static void add_ignore_packed(struct rev_info *revs, const char *name)
        int num = ++revs->num_ignore_packed;
 
        revs->ignore_packed = xrealloc(revs->ignore_packed,
-                                      sizeof(const char **) * (num + 1));
+                                      sizeof(const char *) * (num + 1));
        revs->ignore_packed[num-1] = name;
        revs->ignore_packed[num] = NULL;
 }
index 0fa65baa59de4db058ca6a41bba29ac78eab2643..6c0e251e02e20aed1eaa3bc1737e3e606cb4e23e 100644 (file)
@@ -1749,6 +1749,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
                        error("bad packed object CRC for %s",
                              sha1_to_hex(sha1));
                        mark_bad_packed_object(p, sha1);
+                       unuse_pack(&w_curs);
                        return NULL;
                }
        }
@@ -2055,9 +2056,7 @@ static struct cached_object {
 static int cached_object_nr, cached_object_alloc;
 
 static struct cached_object empty_tree = {
-       /* empty tree sha1: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 */
-       "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60"
-       "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04",
+       EMPTY_TREE_SHA1_BIN,
        OBJ_TREE,
        "",
        0
@@ -2290,7 +2289,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
        memcpy(buffer, filename, dirlen);
        strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
        fd = mkstemp(buffer);
-       if (fd < 0 && dirlen) {
+       if (fd < 0 && dirlen && errno == ENOENT) {
                /* Make sure the directory exists */
                memcpy(buffer, filename, dirlen);
                buffer[dirlen-1] = 0;
@@ -2316,7 +2315,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
        filename = sha1_file_name(sha1);
        fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
-               if (errno == EPERM)
+               if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
                else
                        return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno));
index dfe3fbc74b96fff50160c032f0a65ba54f9ad1b2..caea292f15437f50fd324496ff511971cc9d1d0c 100755 (executable)
@@ -43,6 +43,13 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
 
 '
 
+test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
+
+       GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff |
+       grep "^diff --git a/file b/file"
+
+'
+
 test_expect_success 'diff attribute' '
 
        git config diff.parrot.command echo &&
@@ -68,6 +75,13 @@ test_expect_success 'diff attribute should apply only to diff' '
 
 '
 
+test_expect_success 'diff attribute and --no-ext-diff' '
+
+       git diff --no-ext-diff |
+       grep "^diff --git a/file b/file"
+
+'
+
 test_expect_success 'diff attribute' '
 
        git config --unset diff.parrot.command &&
@@ -94,6 +108,13 @@ test_expect_success 'diff attribute should apply only to diff' '
 
 '
 
+test_expect_success 'diff attribute and --no-ext-diff' '
+
+       git diff --no-ext-diff |
+       grep "^diff --git a/file b/file"
+
+'
+
 test_expect_success 'no diff with -diff' '
        echo >.gitattributes "file -diff" &&
        git diff | grep Binary
index 03ba26a0de7d1bf41269cfb1f1b9f33b733482e1..0b76e7c97a511e5d5bbedaf54d4bab10a15caeb2 100755 (executable)
@@ -21,7 +21,7 @@ EOF
 
 cat >hexdump <<'EOF'
 #!/bin/sh
-perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' "$1"
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' "$1"
 EOF
 chmod +x hexdump
 
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
new file mode 100755 (executable)
index 0000000..83e2e8a
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='pull options'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+test_expect_success 'setup' '
+       mkdir parent &&
+       (cd parent && git init &&
+        echo one >file && git add file &&
+        git commit -m one)
+'
+
+cd "$D"
+
+test_expect_success 'git pull -q' '
+       mkdir clonedq &&
+       cd clonedq &&
+       git pull -q "$D/parent" >out 2>err &&
+       test ! -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull' '
+       mkdir cloned &&
+       cd cloned &&
+       git pull "$D/parent" >out 2>err &&
+       test -s out
+'
+cd "$D"
+
+test_expect_success 'git pull -v' '
+       mkdir clonedv &&
+       cd clonedv &&
+       git pull -v "$D/parent" >out 2>err &&
+       test -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull -v -q' '
+       mkdir clonedvq &&
+       cd clonedvq &&
+       git pull -v -q "$D/parent" >out 2>err &&
+       test ! -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull -q -v' '
+       mkdir clonedqv &&
+       cd clonedqv &&
+       git pull -q -v "$D/parent" >out 2>err &&
+       test -s out
+'
+
+test_done
index 519adba80b4e5bc23a68c4bdcae607cf2d403c23..da5bd3b5a585667dad97481df27049f0aa7415eb 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success 'setup' '
        git commit -F message
 '
 
-test_expect_failure 'initial commit shows verbose diff' '
+test_expect_success 'initial commit shows verbose diff' '
        git commit --amend -v
 '
 
index b48046e2614fa3926b49a7c5cb379d7f6359e881..63a8225ae5c7ffc265e28c9bf17bbec87ab339d8 100755 (executable)
@@ -8,7 +8,7 @@ fsha1=
 csha1=
 tsha1=
 
-test_expect_success '-A option leaves unreachable objects unpacked' '
+test_expect_success '-A with -d option leaves unreachable objects unpacked' '
        echo content > file1 &&
        git add . &&
        git commit -m initial_commit &&
@@ -58,7 +58,7 @@ compare_mtimes ()
                ' -- "$@"
 }
 
-test_expect_success 'unpacked objects receive timestamp of pack file' '
+test_expect_success '-A without -d option leaves unreachable objects packed' '
        fsha1path=$(echo "$fsha1" | sed -e "s|\(..\)|\1/|") &&
        fsha1path=".git/objects/$fsha1path" &&
        csha1path=$(echo "$csha1" | sed -e "s|\(..\)|\1/|") &&
@@ -75,7 +75,19 @@ test_expect_success 'unpacked objects receive timestamp of pack file' '
        git branch -D transient_branch &&
        sleep 1 &&
        git repack -A -l &&
-       compare_mtimes "$packfile" "$fsha1path" "$csha1path" "$tsha1path"
+       test ! -f "$fsha1path" &&
+       test ! -f "$csha1path" &&
+       test ! -f "$tsha1path" &&
+       git show $fsha1 &&
+       git show $csha1 &&
+       git show $tsha1
+'
+
+test_expect_success 'unpacked objects receive timestamp of pack file' '
+       tmppack=".git/objects/pack/tmp_pack" &&
+       ln "$packfile" "$tmppack" &&
+       git repack -A -l -d &&
+       compare_mtimes "$tmppack" "$fsha1path" "$csha1path" "$tsha1path"
 '
 
 test_done
index 561ae7d0a6ddf9531a8770e54beb43a38fe2aab1..617e97d963dc4a9c794fbb148ac4022b2d4fff65 100755 (executable)
@@ -292,4 +292,12 @@ test_expect_success '--compose adds MIME for utf8 subject' '
        grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
+test_expect_success 'detects ambiguous reference/file conflict' '
+       echo master > master &&
+       git add master &&
+       git commit -m"add master" &&
+       test_must_fail git send-email --dry-run master 2>errors &&
+       grep disambiguate errors
+'
+
 test_done
index 2848e46e38e1b664447e1ae8fc20d47c41e2acb5..938b7fe4b4027e2e74da6f816a43811cdf902204 100755 (executable)
@@ -16,7 +16,7 @@ compare_git_head_with () {
 
 compare_svn_head_with () {
        LC_ALL=en_US.UTF-8 svn log --limit 1 `git svn info --url` | \
-               sed -e 1,3d -e "/^-\+\$/d" >current &&
+               sed -e 1,3d -e "/^-\{1,\}\$/d" >current &&
        test_cmp current "$1"
 }
 
index 6ddd7c19fd22d0092829b861900fe462c5ffe73f..20574354627f1e46b36a8eba0731138462482386 100755 (executable)
@@ -231,4 +231,12 @@ test_expect_success 'fast-export -C -C | fast-import' '
 
 '
 
+test_expect_success 'fast-export | fast-import when master is tagged' '
+
+       git tag -m msg last &&
+       git fast-export -C -C --signed-tags=strip --all > output &&
+       test $(grep -c "^tag " output) = 3
+
+'
+
 test_done
index 6a7645ed86fd5879e959460011a8add015d392d9..3edae43ce9d99b27ed69166d90db71bc3c219404 100644 (file)
@@ -185,31 +185,12 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
                wt_status_print_trailer(s);
 }
 
-static void wt_status_print_initial(struct wt_status *s)
-{
-       int i;
-       struct strbuf buf = STRBUF_INIT;
-
-       if (active_nr) {
-               s->commitable = 1;
-               wt_status_print_cached_header(s);
-       }
-       for (i = 0; i < active_nr; i++) {
-               color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-               color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s",
-                               quote_path(active_cache[i]->name, -1,
-                                          &buf, s->prefix));
-       }
-       if (active_nr)
-               wt_status_print_trailer(s);
-       strbuf_release(&buf);
-}
-
 static void wt_status_print_updated(struct wt_status *s)
 {
        struct rev_info rev;
        init_revisions(&rev, NULL);
-       setup_revisions(0, NULL, &rev, s->reference);
+       setup_revisions(0, NULL, &rev,
+               s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_print_updated_cb;
        rev.diffopt.format_callback_data = s;
@@ -298,7 +279,8 @@ static void wt_status_print_verbose(struct wt_status *s)
        struct rev_info rev;
 
        init_revisions(&rev, NULL);
-       setup_revisions(0, NULL, &rev, s->reference);
+       setup_revisions(0, NULL, &rev,
+               s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
        DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
@@ -360,12 +342,9 @@ void wt_status_print(struct wt_status *s)
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
-               wt_status_print_initial(s);
-       }
-       else {
-               wt_status_print_updated(s);
        }
 
+       wt_status_print_updated(s);
        wt_status_print_changed(s);
        if (wt_status_submodule_summary)
                wt_status_print_submodule_summary(s);
@@ -374,7 +353,7 @@ void wt_status_print(struct wt_status *s)
        else if (s->commitable)
                 fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
 
-       if (s->verbose && !s->is_initial)
+       if (s->verbose)
                wt_status_print_verbose(s);
        if (!s->commitable) {
                if (s->amend)
index e8ef46d10dc53a1ee1781d07685984734bc3160f..d782f06d9916bdde33dc3f7312f9eac4e14ef3a1 100644 (file)
@@ -254,16 +254,6 @@ static long ff_regexp(const char *line, long len,
 
        line_buffer = xstrndup(line, len); /* make NUL terminated */
 
-       /* Exclude terminating newline (and cr) from matching */
-       if (len > 0 && line[len-1] == '\n') {
-               if (len > 1 && line[len-2] == '\r')
-                       len -= 2;
-               else
-                       len--;
-       }
-
-       line_buffer = xstrndup(line, len); /* make NUL terminated */
-
        for (i = 0; i < regs->nr; i++) {
                struct ff_reg *reg = regs->array + i;
                if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
@@ -338,4 +328,3 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
        }
        return git_default_config(var, value, cb);
 }
-