summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.5.4.4.txt32
-rw-r--r--Documentation/RelNotes-1.5.5.txt4
-rw-r--r--Documentation/config.txt65
-rw-r--r--Documentation/git-add.txt18
-rw-r--r--Documentation/git-am.txt6
-rw-r--r--Documentation/git-fetch-pack.txt8
-rw-r--r--Documentation/git-gc.txt17
-rw-r--r--Documentation/git-help.txt59
-rw-r--r--Documentation/git-merge.txt3
-rw-r--r--Documentation/git-mergetool.txt38
-rw-r--r--Documentation/git-pack-objects.txt5
-rw-r--r--Documentation/git-pull.txt2
-rw-r--r--Documentation/git-rebase.txt3
-rw-r--r--Documentation/git-reflog.txt14
-rw-r--r--Documentation/git-rev-parse.txt17
-rw-r--r--Documentation/git-stash.txt13
-rw-r--r--Documentation/git-submodule.txt5
-rw-r--r--Documentation/git-unpack-objects.txt3
-rw-r--r--Documentation/git-web--browse.txt21
-rw-r--r--Documentation/git-whatchanged.txt9
-rw-r--r--Documentation/git.txt3
-rw-r--r--Documentation/manpage-1.72.xsl6
-rw-r--r--Documentation/pretty-options.txt3
-rw-r--r--Documentation/technical/api-run-command.txt7
-rw-r--r--Makefile19
-rw-r--r--builtin-add.c12
-rw-r--r--builtin-checkout.c9
-rw-r--r--builtin-clean.c32
-rw-r--r--builtin-commit.c2
-rw-r--r--builtin-describe.c35
-rw-r--r--builtin-fetch-pack.c26
-rw-r--r--builtin-fetch.c54
-rw-r--r--builtin-gc.c17
-rw-r--r--builtin-log.c11
-rw-r--r--builtin-ls-files.c11
-rw-r--r--builtin-ls-remote.c5
-rw-r--r--builtin-merge-file.c3
-rw-r--r--builtin-merge-recursive.c394
-rw-r--r--builtin-mv.c6
-rw-r--r--builtin-name-rev.c59
-rw-r--r--builtin-pack-objects.c50
-rw-r--r--builtin-read-tree.c32
-rw-r--r--builtin-reflog.c102
-rw-r--r--builtin-remote.c605
-rw-r--r--builtin-reset.c47
-rw-r--r--builtin-rev-parse.c28
-rw-r--r--builtin-shortlog.c9
-rw-r--r--builtin-tag.c6
-rw-r--r--builtin-unpack-objects.c110
-rw-r--r--builtin.h1
-rw-r--r--cache.h14
-rw-r--r--commit.h4
-rw-r--r--compat/snprintf.c40
-rw-r--r--config.mak.in2
-rw-r--r--configure.ac54
-rwxr-xr-xcontrib/completion/git-completion.bash230
-rw-r--r--contrib/emacs/git.el2
-rwxr-xr-xcontrib/examples/git-remote.perl (renamed from git-remote.perl)0
-rwxr-xr-xcontrib/fast-import/git-p445
-rw-r--r--contrib/hooks/post-receive-email2
-rwxr-xr-xcontrib/workdir/git-new-workdir2
-rw-r--r--diff-lib.c55
-rw-r--r--diff.c306
-rw-r--r--diff.h3
-rw-r--r--fast-import.c3
-rw-r--r--fetch-pack.h3
-rw-r--r--fsck.c2
-rwxr-xr-xgit-am.sh44
-rw-r--r--git-compat-util.h16
-rwxr-xr-xgit-cvsimport.perl2
-rwxr-xr-xgit-cvsserver.perl2
-rwxr-xr-xgit-filter-branch.sh11
-rw-r--r--git-gui/Makefile2
-rwxr-xr-xgit-gui/git-gui.sh22
-rw-r--r--git-gui/lib/blame.tcl25
-rw-r--r--git-gui/lib/browser.tcl3
-rw-r--r--git-gui/lib/choose_font.tcl2
-rw-r--r--git-gui/lib/console.tcl10
-rw-r--r--git-gui/lib/error.tcl4
-rw-r--r--git-gui/lib/option.tcl1
-rw-r--r--git-gui/po/zh_cn.po1223
-rwxr-xr-xgit-merge.sh19
-rwxr-xr-xgit-mergetool.sh161
-rwxr-xr-xgit-quiltimport.sh22
-rwxr-xr-xgit-rebase.sh5
-rwxr-xr-xgit-send-email.perl3
-rwxr-xr-xgit-stash.sh36
-rwxr-xr-xgit-submodule.sh50
-rwxr-xr-xgit-svn.perl13
-rwxr-xr-xgit-web--browse.sh15
-rw-r--r--git.c3
-rw-r--r--gitk-git/Makefile4
-rw-r--r--gitk-git/gitk112
-rw-r--r--gitk-git/po/it.po890
-rwxr-xr-xgitweb/gitweb.perl85
-rw-r--r--hash.c6
-rw-r--r--hash.h4
-rw-r--r--help.c116
-rw-r--r--http.c14
-rw-r--r--ident.c2
-rw-r--r--ll-merge.c379
-rw-r--r--ll-merge.h15
-rw-r--r--log-tree.c15
-rw-r--r--log-tree.h4
-rw-r--r--merge-tree.c58
-rw-r--r--parse-options.c28
-rw-r--r--parse-options.h3
-rw-r--r--path-list.c30
-rw-r--r--path-list.h8
-rw-r--r--path.c2
-rw-r--r--pretty.c24
-rw-r--r--quote.c44
-rw-r--r--quote.h4
-rw-r--r--read-cache.c47
-rw-r--r--receive-pack.c36
-rw-r--r--refs.c4
-rw-r--r--refs.h6
-rw-r--r--remote.c3
-rw-r--r--remote.h1
-rw-r--r--run-command.c14
-rw-r--r--setup.c2
-rw-r--r--sha1_name.c88
-rwxr-xr-xt/t0021-conversion.sh4
-rwxr-xr-xt/t0040-parse-options.sh16
-rwxr-xr-xt/t1005-read-tree-reset.sh30
-rwxr-xr-xt/t1410-reflog.sh39
-rwxr-xr-xt/t3101-ls-tree-dirname.sh2
-rwxr-xr-xt/t3407-rebase-abort.sh68
-rwxr-xr-xt/t3903-stash.sh50
-rwxr-xr-xt/t4021-format-patch-signer-mime.sh10
-rwxr-xr-xt/t4028-format-patch-mime-headers.sh30
-rwxr-xr-xt/t4150-am-subdir.sh72
-rwxr-xr-xt/t4201-shortlog.sh5
-rw-r--r--t/t5304-prune.sh49
-rwxr-xr-xt/t5305-include-tag.sh84
-rwxr-xr-xt/t5503-tagfollow.sh150
-rwxr-xr-xt/t5505-remote.sh143
-rwxr-xr-xt/t6031-merge-recursive.sh49
-rwxr-xr-xt/t6120-describe.sh23
-rwxr-xr-xt/t7003-filter-branch.sh24
-rwxr-xr-xt/t7005-editor.sh27
-rwxr-xr-xt/t7010-setup.sh7
-rwxr-xr-xt/t7300-clean.sh55
-rwxr-xr-xt/t7600-merge.sh6
-rw-r--r--t/t7610-mergetool.sh46
-rwxr-xr-xt/t9300-fast-import.sh2
-rw-r--r--t/test-lib.sh24
-rw-r--r--test-parse-options.c2
-rw-r--r--transport.c5
-rw-r--r--transport.h3
-rw-r--r--tree-walk.c62
-rw-r--r--tree-walk.h23
-rw-r--r--unpack-trees.c638
-rw-r--r--unpack-trees.h17
-rw-r--r--upload-pack.c19
-rw-r--r--wt-status.c64
-rw-r--r--xdiff-interface.c4
157 files changed, 6002 insertions, 2405 deletions
diff --git a/Documentation/RelNotes-1.5.4.4.txt b/Documentation/RelNotes-1.5.4.4.txt
index 5635977c93..89fa6d03bc 100644
--- a/Documentation/RelNotes-1.5.4.4.txt
+++ b/Documentation/RelNotes-1.5.4.4.txt
@@ -37,10 +37,30 @@ Fixes since v1.5.4.3
* "git revert" did not properly fail when attempting to run with a
dirty index.
-Also included are a handful documentation updates.
+ * "git merge --no-commit --no-ff <other>" incorrectly made commits.
+
+ * "git merge --squash --no-ff <other>", which is a nonsense combination
+ of options, was not rejected.
+
+ * "git ls-remote" and "git remote show" against an empty repository
+ failed, instead of just giving an empty result (regression).
+
+ * "git fast-import" did not handle a renamed path whose name needs to be
+ quoted, due to a bug in unquote_c_style() function.
+
+ * "git cvsexportcommit" was confused when multiple files with the same
+ basename needed to be pushed out in the same commit.
+
+ * "git daemon" did not send early errors to syslog.
----
-exec >/var/tmp/1
-echo O=$(git describe maint)
-O=v1.5.4.3-32-g0f2d447
-git shortlog --no-merges $O..maint
+ * "git log --merge" did not work well with --left-right option.
+
+ * "git svn" promprted for client cert password every time it accessed the
+ server.
+
+ * The reset command in "git fast-import" data stream was documented to
+ end with an optional LF, but it actually required one.
+
+ * "git svn dcommit/rebase" did not honor --rewrite-root option.
+
+Also included are a handful documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt
index b57fa1eb1a..874dad9a4f 100644
--- a/Documentation/RelNotes-1.5.5.txt
+++ b/Documentation/RelNotes-1.5.5.txt
@@ -160,13 +160,9 @@ Fixes since v1.5.4
All of the fixes in v1.5.4 maintenance series are included in
this release, unless otherwise noted.
- * "git-daemon" did not send early errors to syslog.
-
* "git-http-push" did not allow deletion of remote ref with the usual
"push <remote> :<branch>" syntax.
- * "git-log --merge" did not well work with --left-right option.
-
* "git-rebase --abort" did not go back to the right location if
"git-reset" was run during the "git-rebase" session.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2091caa111..0865f4e01a 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -420,6 +420,11 @@ branch.<name>.rebase::
it unless you understand the implications (see linkgit:git-rebase[1]
for details).
+browser.<tool>.cmd::
+ Specify the command to invoke the specified browser. The
+ specified command is evaluated in shell with the URLs passed
+ as arguments. (See linkgit:git-web--browse[1].)
+
browser.<tool>.path::
Override the path for the given tool that may be used to
browse HTML help (see '-w' option in linkgit:git-help[1]) or a
@@ -556,6 +561,11 @@ format.suffix::
`.patch`. Use this variable to change that suffix (make sure to
include the dot if you want it).
+format.pretty::
+ The default pretty format for log/show/whatchanged command,
+ See linkgit:git-log[1], linkgit:git-show[1],
+ linkgit:git-whatchanged[1].
+
gc.aggressiveWindow::
The window size parameter used in the delta compression
algorithm used by 'git gc --aggressive'. This defaults
@@ -585,6 +595,10 @@ gc.packrefs::
at some stage, and setting this to `false` will continue to
prevent `git pack-refs` from being run from `git gc`.
+gc.pruneexpire::
+ When `git gc` is run, it will call `prune --expire 2.weeks.ago`.
+ Override the grace period with this config variable.
+
gc.reflogexpire::
`git reflog expire` removes reflog entries older than
this time; defaults to 90 days.
@@ -743,14 +757,20 @@ log.showroot::
Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
normally hide the root commit will now show it. True by default.
+man.viewer::
+ Specify the programs that may be used to display help in the
+ 'man' format. See linkgit:git-help[1].
+
merge.summary::
Whether to include summaries of merged commits in newly created
merge commit messages. False by default.
merge.tool::
Controls which merge resolution program is used by
- linkgit:git-mergetool[1]. Valid values are: "kdiff3", "tkdiff",
- "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
+ linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3",
+ "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+ "opendiff". Any other value is treated is custom merge tool
+ and there must be a corresponing mergetool.<tool>.cmd option.
merge.verbosity::
Controls the amount of output shown by the recursive merge
@@ -777,6 +797,31 @@ mergetool.<tool>.path::
Override the path for the given tool. This is useful in case
your tool is not in the PATH.
+mergetool.<tool>.cmd::
+ Specify the command to invoke the specified merge tool. The
+ specified command is evaluated in shell with the following
+ variables available: 'BASE' is the name of a temporary file
+ containing the common base of the files to be merged, if available;
+ 'LOCAL' is the name of a temporary file containing the contents of
+ the file on the current branch; 'REMOTE' is the name of a temporary
+ file containing the contents of the file from the branch being
+ merged; 'MERGED' contains the name of the file to which the merge
+ tool should write the results of a successful merge.
+
+mergetool.<tool>.trustExitCode::
+ For a custom merge command, specify whether the exit code of
+ the merge command can be used to determine whether the merge was
+ successful. If this is not set to true then the merge target file
+ timestamp is checked and the merge assumed to have been successful
+ if the file has been updated, otherwise the user is prompted to
+ indicate the success of the merge.
+
+mergetool.keepBackup::
+ After performing a merge, the original file with conflict markers
+ can be saved as a file with a `.orig` extension. If this variable
+ is set to `false` then this file is not preserved. Defaults to
+ `true` (i.e. keep the backup files).
+
pack.window::
The size of the window used by linkgit:git-pack-objects[1] when no
window size is given on the command line. Defaults to 10.
@@ -828,7 +873,7 @@ pack.indexVersion::
whenever the corresponding pack is larger than 2 GB. Otherwise
the default is 1.
-pack.packSizeLimit:
+pack.packSizeLimit::
The default maximum size of a pack. This setting only affects
packing to a file, i.e. the git:// protocol is unaffected. It
can be overridden by the `\--max-pack-size` option of
@@ -864,15 +909,15 @@ remote.<name>.skipDefaultUpdate::
remote.<name>.receivepack::
The default program to execute on the remote side when pushing. See
- option \--exec of linkgit:git-push[1].
+ option \--receive-pack of linkgit:git-push[1].
remote.<name>.uploadpack::
The default program to execute on the remote side when fetching. See
- option \--exec of linkgit:git-fetch-pack[1].
+ option \--upload-pack of linkgit:git-fetch-pack[1].
remote.<name>.tagopt::
- Setting this value to --no-tags disables automatic tag following when fetching
- from remote <name>
+ Setting this value to \--no-tags disables automatic tag following when
+ fetching from remote <name>
remotes.<group>::
The list of remotes which are fetched by "git remote update
@@ -939,12 +984,6 @@ imap::
The configuration variables in the 'imap' section are described
in linkgit:git-imap-send[1].
-receive.fsckObjects::
- If it is set to true, git-receive-pack will check all received
- objects. It will abort in the case of a malformed object or a
- broken link. The result of an abort are only dangling objects.
- The default value is true.
-
receive.unpackLimit::
If the number of objects received in a push is below this
limit then the objects will be unpacked into loose object
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 47799097ce..c751a17d07 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -207,16 +207,14 @@ patch::
and the working tree file and asks you if you want to stage
the change of each hunk. You can say:
- y - add the change from that hunk to index
- n - do not add the change from that hunk to index
- a - add the change from that hunk and all the rest to index
- d - do not the change from that hunk nor any of the rest to index
- j - do not decide on this hunk now, and view the next
- undecided hunk
- J - do not decide on this hunk now, and view the next hunk
- k - do not decide on this hunk now, and view the previous
- undecided hunk
- K - do not decide on this hunk now, and view the previous hunk
+ y - stage this hunk
+ n - do not stage this hunk
+ a - stage this and all the remaining hunks in the file
+ d - do not stage this hunk nor any of the remaining hunks in the file
+ j - leave this hunk undecided, see next undecided hunk
+ J - leave this hunk undecided, see next hunk
+ k - leave this hunk undecided, see previous undecided hunk
+ K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
? - print help
+
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e640fc75cd..2387a8d6c2 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -9,7 +9,7 @@ git-am - Apply a series of patches from a mailbox
SYNOPSIS
--------
[verse]
-'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
+'git-am' [--signoff] [--keep] [--utf8 | --no-utf8]
[--3way] [--interactive] [--binary]
[--whitespace=<option>] [-C<n>] [-p<n>]
<mbox>|<Maildir>...
@@ -32,10 +32,6 @@ OPTIONS
Add `Signed-off-by:` line to the commit message, using
the committer identity of yourself.
--d=<dir>, --dotest=<dir>::
- Instead of `.dotest` directory, use <dir> as a working
- area to store extracted patches.
-
-k, --keep::
Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index 2b8ffe5324..57598eb056 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository
SYNOPSIS
--------
-'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
+'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
DESCRIPTION
-----------
@@ -45,6 +45,12 @@ OPTIONS
Spend extra cycles to minimize the number of objects to be sent.
Use it on slower connection.
+\--include-tag::
+ If the remote side supports it, annotated tags objects will
+ be downloaded on the same connection as the other objects if
+ the object the tag references is downloaded. The caller must
+ otherwise determine the tags this option made available.
+
\--upload-pack=<git-upload-pack>::
Use this to specify the path to 'git-upload-pack' on the
remote side, if is not found on your $PATH.
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 2e7be916aa..229a7c9b30 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
SYNOPSIS
--------
-'git-gc' [--prune] [--aggressive] [--auto] [--quiet]
+'git-gc' [--aggressive] [--auto] [--quiet]
DESCRIPTION
-----------
@@ -25,17 +25,6 @@ operating performance. Some git commands may automatically run
OPTIONS
-------
---prune::
- Usually `git-gc` packs refs, expires old reflog entries,
- packs loose objects,
- and removes old 'rerere' records. Removal
- of unreferenced loose objects is an unsafe operation
- while other git operations are in progress, so it is not
- done by default. Pass this option if you want it, and only
- when you know nobody else is creating new objects in the
- repository at the same time (e.g. never use this option
- in a cron script).
-
--aggressive::
Usually 'git-gc' runs very quickly while providing good disk
space utilization and performance. This option will cause
@@ -104,6 +93,10 @@ the value, the more time is spent optimizing the delta compression. See
the documentation for the --window' option in linkgit:git-repack[1] for
more details. This defaults to 10.
+The optional configuration variable 'gc.pruneExpire' controls how old
+the unreferenced loose objects have to be before they are pruned. The
+default is "2 weeks ago".
+
See Also
--------
linkgit:git-prune[1]
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index 0926dc12ba..be2ae53b90 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -33,17 +33,21 @@ OPTIONS
option supersedes any other option.
-i|--info::
- Use the 'info' program to display the manual page, instead of
- the 'man' program that is used by default.
+ Display manual page for the command in the 'info' format. The
+ 'info' program will be used for that purpose.
-m|--man::
- Use the 'man' program to display the manual page. This may be
- used to override a value set in the 'help.format'
- configuration variable.
+ Display manual page for the command in the 'man' format. This
+ option may be used to override a value set in the
+ 'help.format' configuration variable.
++
+By default the 'man' program will be used to display the manual page,
+but the 'man.viewer' configuration variable may be used to choose
+other display programs (see below).
-w|--web::
- Use a web browser to display the HTML manual page, instead of
- the 'man' program that is used by default.
+ Display manual page for the command in the 'web' (HTML)
+ format. A web browser will be used for that purpose.
+
The web browser can be specified using the configuration variable
'help.browser', or 'web.browser' if the former is not set. If none of
@@ -54,6 +58,9 @@ linkgit:git-web--browse[1] for more information about this.
CONFIGURATION VARIABLES
-----------------------
+help.format
+~~~~~~~~~~~
+
If no command line option is passed, the 'help.format' configuration
variable will be checked. The following values are supported for this
variable; they make 'git-help' behave as their corresponding command
@@ -61,15 +68,47 @@ line option:
* "man" corresponds to '-m|--man',
* "info" corresponds to '-i|--info',
-* "web" or "html" correspond to '-w|--web',
+* "web" or "html" correspond to '-w|--web'.
+
+help.browser, web.browser and browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
be checked if the 'web' format is chosen (either by command line
option or configuration variable). See '-w|--web' in the OPTIONS
section above and linkgit:git-web--browse[1].
-Note that these configuration variables should probably be set using
-the '--global' flag, for example like this:
+man.viewer
+~~~~~~~~~~
+
+The 'man.viewer' config variable will be checked if the 'man' format
+is chosen. Only the following values are currently supported:
+
+* "man": use the 'man' program as usual,
+* "woman": use 'emacsclient' to launch the "woman" mode in emacs
+(this only works starting with emacsclient versions 22),
+* "konqueror": use a man KIO slave in konqueror.
+
+Multiple values may be given to this configuration variable. Their
+corresponding programs will be tried in the order listed in the
+configuration file.
+
+For example, this configuration:
+
+ [man]
+ viewer = konqueror
+ viewer = woman
+
+will try to use konqueror first. But this may fail (for example if
+DISPLAY is not set) and in that case emacs' woman mode will be tried.
+
+If everything fails the 'man' program will be tried anyway.
+
+Note about git config --global
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Note that all these configuration variables should probably be set
+using the '--global' flag, for example like this:
------------------------------------------------
$ git config --global help.format web
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 0c9ad7f2bb..c136b10692 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -68,7 +68,8 @@ HOW MERGE WORKS
---------------
A merge is always between the current `HEAD` and one or more
-remote branch heads, and the index file must exactly match the
+commits (usually, branch head or tag), and the index file must
+exactly match the
tree of `HEAD` commit (i.e. the contents of the last commit) when
it happens. In other words, `git-diff --cached HEAD` must
report no changes.
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index 50f106ec5b..8ed44947ef 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -12,12 +12,12 @@ SYNOPSIS
DESCRIPTION
-----------
-Use 'git mergetool' to run one of several merge utilities to resolve
+Use `git mergetool` to run one of several merge utilities to resolve
merge conflicts. It is typically run after linkgit:git-merge[1].
If one or more <file> parameters are given, the merge tool program will
be run to resolve differences on each file. If no <file> names are
-specified, 'git mergetool' will run the merge tool program on every file
+specified, `git mergetool` will run the merge tool program on every file
with merge conflicts.
OPTIONS
@@ -27,16 +27,38 @@ OPTIONS
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
+
-If a merge resolution program is not specified, 'git mergetool'
-will use the configuration variable merge.tool. If the
-configuration variable merge.tool is not set, 'git mergetool'
+If a merge resolution program is not specified, `git mergetool`
+will use the configuration variable `merge.tool`. If the
+configuration variable `merge.tool` is not set, `git mergetool`
will pick a suitable default.
+
You can explicitly provide a full path to the tool by setting the
-configuration variable mergetool.<tool>.path. For example, you
+configuration variable `mergetool.<tool>.path`. For example, you
can configure the absolute path to kdiff3 by setting
-mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool
-is available in PATH.
+`mergetool.kdiff3.path`. Otherwise, `git mergetool` assumes the
+tool is available in PATH.
++
+Instead of running one of the known merge tool programs
+`git mergetool` can be customized to run an alternative program
+by specifying the command line to invoke in a configration
+variable `mergetool.<tool>.cmd`.
++
+When `git mergetool` is invoked with this tool (either through the
+`-t` or `--tool` option or the `merge.tool` configuration
+variable) the configured command line will be invoked with `$BASE`
+set to the name of a temporary file containing the common base for
+the merge, if available; `$LOCAL` set to the name of a temporary
+file containing the contents of the file on the current branch;
+`$REMOTE` set to the name of a temporary file containing the
+contents of the file to be merged, and `$MERGED` set to the name
+of the file to which the merge tool should write the result of the
+merge resolution.
++
+If the custom merge tool correctly indicates the success of a
+merge resolution with its exit code then the configuration
+variable `mergetool.<tool>.trustExitCode` can be set to `true`.
+Otherwise, `git mergetool` will prompt the user to indicate the
+success of the resolution after the custom tool has exited.
Author
------
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 5c1bd3b081..eed0a94c6e 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -73,6 +73,11 @@ base-name::
as if all refs under `$GIT_DIR/refs` are specified to be
included.
+--include-tag::
+ Include unasked-for annotated tags if the object they
+ reference was included in the resulting packfile. This
+ can be useful to send new tags to native git clients.
+
--window=[N], --depth=[N]::
These two options affect how the objects contained in
the pack are stored using delta compression. The
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 737894390d..3405ca09e8 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -21,6 +21,8 @@ Note that you can use `.` (current directory) as the
<repository> to pull from the local repository -- this is useful
when merging local branches into the current branch.
+Also note that options meant for `git-pull` itself and underlying
+`git-merge` must be given before the options meant for `git-fetch`.
OPTIONS
-------
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4b10304740..e0412e0866 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -262,8 +262,7 @@ hook if one exists. You can use this hook to do sanity checks and
reject the rebase if it isn't appropriate. Please see the template
pre-rebase hook script for an example.
-You must be in the top directory of your project to start (or continue)
-a rebase. Upon completion, <branch> will be the current branch.
+Upon completion, <branch> will be the current branch.
INTERACTIVE MODE
----------------
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index f9bba36c23..047e3ce14d 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -19,6 +19,8 @@ depending on the subcommand:
git reflog expire [--dry-run] [--stale-fix] [--verbose]
[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
+git reflog delete ref@\{specifier\}...
+
git reflog [show] [log-options] [<ref>]
Reflog is a mechanism to record when the tip of branches are
@@ -43,6 +45,9 @@ two moves ago", `master@\{one.week.ago\}` means "where master used to
point to one week ago", and so on. See linkgit:git-rev-parse[1] for
more details.
+To delete single entries from the reflog, use the subcommand "delete"
+and specify the _exact_ entry (e.g. ``git reflog delete master@\{2\}'').
+
OPTIONS
-------
@@ -75,6 +80,15 @@ them.
--all::
Instead of listing <refs> explicitly, prune all refs.
+--updateref::
+ Update the ref with the sha1 of the top reflog entry (i.e.
+ <ref>@\{0\}) after expiring or deleting.
+
+--rewrite::
+ While expiring or deleting, adjust each reflog entry to ensure
+ that the `old` sha1 field points to the `new` sha1 field of the
+ previous entry.
+
--verbose::
Print extra information on screen.
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index f02f6bbb49..6513c2efe1 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -325,7 +325,7 @@ The lines after the separator describe the options.
Each line of options has this format:
------------
-<opt_spec><arg_spec>? SP+ help LF
+<opt_spec><flags>* SP+ help LF
------------
`<opt_spec>`::
@@ -334,10 +334,17 @@ Each line of options has this format:
is necessary. `h,help`, `dry-run` and `f` are all three correct
`<opt_spec>`.
-`<arg_spec>`::
- an `<arg_spec>` tells the option parser if the option has an argument
- (`=`), an optional one (`?` though its use is discouraged) or none
- (no `<arg_spec>` in that case).
+`<flags>`::
+ `<flags>` are of `*`, `=`, `?` or `!`.
+ * Use `=` if the option takes an argument.
+
+ * Use `?` to mean that the option is optional (though its use is discouraged).
+
+ * Use `*` to mean that this option should not be listed in the usage
+ generated for the `-h` argument. It's shown for `--help-all` as
+ documented in linkgit:gitcli[5].
+
+ * Use `!` to not make the corresponding negated long option available.
The remainder of the line, after stripping the spaces, is used
as the help associated to the option.
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 48e6f5a3f7..8dc35d493e 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -8,7 +8,7 @@ git-stash - Stash the changes in a dirty working directory away
SYNOPSIS
--------
[verse]
-'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
+'git-stash' (list | show [<stash>] | apply [<stash>] | clear | drop [<stash>] | pop [<stash>])
'git-stash' [save [<message>]]
DESCRIPTION
@@ -85,6 +85,17 @@ clear::
Remove all the stashed states. Note that those states will then
be subject to pruning, and may be difficult or impossible to recover.
+drop [<stash>]::
+
+ Remove a single stashed state from the stash list. When no `<stash>`
+ is given, it removes the latest one. i.e. `stash@\{0}`
+
+pop [<stash>]::
+
+ Remove a single stashed state from the stash list and apply on top
+ of the current working tree state. When no `<stash>` is given,
+ `stash@\{0}` is assumed. See also `apply`.
+
DISCUSSION
----------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index e96bf36521..41f9f63566 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -19,8 +19,9 @@ COMMANDS
--------
add::
Add the given repository as a submodule at the given path
- to the changeset to be committed next. In particular, the
- repository is cloned at the specified path, added to the
+ to the changeset to be committed next. If path is a valid
+ repository within the project, it is added as is. Otherwise,
+ repository is cloned at the specified path. path is added to the
changeset and registered in .gitmodules. If no path is
specified, the path is deduced from the repository specification.
If the repository url begins with ./ or ../, it is stored as
diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt
index 3697896a06..b79be3fd4c 100644
--- a/Documentation/git-unpack-objects.txt
+++ b/Documentation/git-unpack-objects.txt
@@ -40,9 +40,6 @@ OPTIONS
and make the best effort to recover as many objects as
possible.
---strict::
- Don't write objects with broken content or links.
-
Author
------
diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt
index df57d010e5..ddbae5b194 100644
--- a/Documentation/git-web--browse.txt
+++ b/Documentation/git-web--browse.txt
@@ -27,6 +27,8 @@ The following browsers (or commands) are currently supported:
* dillo
* open (this is the default under Mac OS X GUI)
+Custom commands may also be specified.
+
OPTIONS
-------
-b BROWSER|--browser=BROWSER::
@@ -43,16 +45,35 @@ OPTIONS
CONFIGURATION VARIABLES
-----------------------
+CONF.VAR (from -c option) and web.browser
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
The web browser can be specified using a configuration variable passed
with the -c (or --config) command line option, or the 'web.browser'
configuration variable if the former is not used.
+browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~
+
You can explicitly provide a full path to your preferred browser by
setting the configuration variable 'browser.<tool>.path'. For example,
you can configure the absolute path to firefox by setting
'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool
is available in PATH.
+browser.<tool>.cmd
+~~~~~~~~~~~~~~~~~~
+
+When the browser, specified by options or configuration variables, is
+not among the supported ones, then the corresponding
+'browser.<tool>.cmd' configuration variable will be looked up. If this
+variable exists then "git web--browse" will treat the specified tool
+as a custom command and will use a shell eval to run the command with
+the URLs passed as arguments.
+
+Note about git config --global
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
Note that these configuration variables should probably be set using
the '--global' flag, for example like this:
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index 54947b6769..a6e7bd4c8b 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -38,11 +38,6 @@ OPTIONS
Show git internal diff output, but for the whole tree,
not just the top level.
---pretty=<format>::
- Controls the output format for the commit logs.
- <format> can be one of 'raw', 'medium', 'short', 'full',
- and 'oneline'.
-
-m::
By default, differences for merge commits are not shown.
With this flag, show differences to that commit from all
@@ -51,6 +46,10 @@ OPTIONS
However, it is not very useful in general, although it
*is* useful on a file-by-file basis.
+include::pretty-options.txt[]
+
+include::pretty-formats.txt[]
+
Examples
--------
git-whatchanged -p v2.6.12.. include/scsi drivers/scsi::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 741ae0e4c8..3ed24d449a 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.5.4.3/git.html[documentation for release 1.5.4.3]
+* link:v1.5.4.4/git.html[documentation for release 1.5.4.4]
* release notes for
+ link:RelNotes-1.5.4.4.txt[1.5.4.4],
link:RelNotes-1.5.4.3.txt[1.5.4.3],
link:RelNotes-1.5.4.2.txt[1.5.4.2],
link:RelNotes-1.5.4.1.txt[1.5.4.1],
diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl
index fe3cd72d6f..4065a3a27a 100644
--- a/Documentation/manpage-1.72.xsl
+++ b/Documentation/manpage-1.72.xsl
@@ -1,5 +1,9 @@
-<!-- callout.xsl: converts asciidoc callouts to man page format -->
+<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<xsl:param name="man.output.quietly" select="1"/>
+<xsl:param name="refentry.meta.get.quietly" select="1"/>
+
<xsl:template match="co">
<xsl:value-of select="concat('&#x2593;fB(',substring-after(@id,'-'),')&#x2593;fR')"/>
</xsl:template>
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 973d8dd733..6d66c74cc1 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -4,6 +4,9 @@
where '<format>' can be one of 'oneline', 'short', 'medium',
'full', 'fuller', 'email', 'raw' and 'format:<string>'.
When omitted, the format defaults to 'medium'.
++
+Note: you can specify the default pretty format in the repository
+configuration (see linkgit:git-config[1]).
--abbrev-commit::
Instead of showing the full 40-byte hexadecimal commit object
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index fde3b45321..c364a22c8f 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -111,9 +111,10 @@ stderr as follows:
.no_stdin, .no_stdout, .no_stderr: The respective channel is
redirected to /dev/null.
- .stdout_to_stderr: stdout of the child is redirected to the
- parent's stderr (i.e. *not* to what .err or
- .no_stderr specify).
+ .stdout_to_stderr: stdout of the child is redirected to its
+ stderr. This happens after stderr is itself redirected.
+ So stdout will follow stderr to wherever it is
+ redirected.
To modify the environment of the sub-process, specify an array of
string pointers (NULL terminated) in .env:
diff --git a/Makefile b/Makefile
index ca5aad963c..e3eaa6a543 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,10 @@ all::
# Define V=1 to have a more verbose compile.
#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+#
# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
# when attempting to read from an fopen'ed directory.
#
@@ -243,7 +247,7 @@ SCRIPT_SH = \
SCRIPT_PERL = \
git-add--interactive.perl \
git-archimport.perl git-cvsimport.perl git-relink.perl \
- git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
+ git-cvsserver.perl git-cvsexportcommit.perl \
git-send-email.perl git-svn.perl
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
@@ -304,7 +308,7 @@ LIB_H = \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
- mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h fsck.h \
+ mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h ll-merge.h fsck.h \
pack-revindex.h
DIFF_OBJS = \
@@ -329,7 +333,7 @@ LIB_OBJS = \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
- alias.o fsck.o pack-revindex.o
+ ll-merge.o alias.o fsck.o pack-revindex.o
BUILTIN_OBJS = \
builtin-add.o \
@@ -381,6 +385,7 @@ BUILTIN_OBJS = \
builtin-push.o \
builtin-read-tree.o \
builtin-reflog.o \
+ builtin-remote.o \
builtin-send-pack.o \
builtin-config.o \
builtin-rerere.o \
@@ -478,6 +483,7 @@ ifeq ($(uname_S),FreeBSD)
NO_MEMMEM = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
+ DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
@@ -629,6 +635,10 @@ endif
ifdef NO_C99_FORMAT
BASIC_CFLAGS += -DNO_C99_FORMAT
endif
+ifdef SNPRINTF_RETURNS_BOGUS
+ COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
+ COMPAT_OBJS += compat/snprintf.o
+endif
ifdef FREAD_READS_DIRECTORIES
COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
COMPAT_OBJS += compat/fopen.o
@@ -747,6 +757,9 @@ ifdef THREADED_DELTA_SEARCH
EXTLIBS += -lpthread
LIB_OBJS += thread-utils.o
endif
+ifdef DIR_HAS_BSD_GROUP_SEMANTICS
+ COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
+endif
ifeq ($(TCLTK_PATH),)
NO_TCLTK=NoThanks
diff --git a/builtin-add.c b/builtin-add.c
index 820110e085..4a91e3eb11 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -228,18 +228,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
goto finish;
}
- if (*argv) {
- /* Was there an invalid path? */
- if (pathspec) {
- int num;
- for (num = 0; pathspec[num]; num++)
- ; /* just counting */
- if (argc != num)
- exit(1); /* error message already given */
- } else
- exit(1); /* error message already given */
- }
-
fill_directory(&dir, pathspec, ignored_too);
if (show_only) {
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 6b08016228..7deb504837 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -152,6 +152,7 @@ static int reset_to_new(struct tree *tree, int quiet)
{
struct unpack_trees_options opts;
struct tree_desc tree_desc;
+
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
opts.update = 1;
@@ -159,6 +160,8 @@ static int reset_to_new(struct tree *tree, int quiet)
opts.merge = 1;
opts.fn = oneway_merge;
opts.verbose_update = !quiet;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
parse_tree(tree);
init_tree_desc(&tree_desc, tree->buffer, tree->size);
if (unpack_trees(1, &tree_desc, &opts))
@@ -170,6 +173,7 @@ static void reset_clean_to_new(struct tree *tree, int quiet)
{
struct unpack_trees_options opts;
struct tree_desc tree_desc;
+
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
opts.skip_unmerged = 1;
@@ -177,6 +181,8 @@ static void reset_clean_to_new(struct tree *tree, int quiet)
opts.merge = 1;
opts.fn = oneway_merge;
opts.verbose_update = !quiet;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
parse_tree(tree);
init_tree_desc(&tree_desc, tree->buffer, tree->size);
if (unpack_trees(1, &tree_desc, &opts))
@@ -224,8 +230,11 @@ static int merge_working_tree(struct checkout_opts *opts,
struct tree_desc trees[2];
struct tree *tree;
struct unpack_trees_options topts;
+
memset(&topts, 0, sizeof(topts));
topts.head_idx = -1;
+ topts.src_index = &the_index;
+ topts.dst_index = &the_index;
refresh_cache(REFRESH_QUIET);
diff --git a/builtin-clean.c b/builtin-clean.c
index 3b220d5060..fefec3010c 100644
--- a/builtin-clean.c
+++ b/builtin-clean.c
@@ -10,6 +10,7 @@
#include "cache.h"
#include "dir.h"
#include "parse-options.h"
+#include "quote.h"
static int force = -1; /* unset */
@@ -34,7 +35,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct dir_struct dir;
const char *path, *base;
static const char **pathspec;
- int prefix_offset = 0;
+ struct strbuf buf;
+ const char *qname;
char *seen = NULL;
struct option options[] = {
OPT__QUIET(&quiet),
@@ -56,6 +58,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
+ strbuf_init(&buf, 0);
memset(&dir, 0, sizeof(dir));
if (ignored_only)
dir.show_ignored = 1;
@@ -72,8 +75,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (!ignored)
setup_standard_excludes(&dir);
- if (prefix)
- prefix_offset = strlen(prefix);
pathspec = get_pathspec(prefix, argv);
read_cache();
@@ -134,39 +135,34 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (S_ISDIR(st.st_mode)) {
strbuf_addstr(&directory, ent->name);
+ qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
if (show_only && (remove_directories || matches)) {
- printf("Would remove %s\n",
- directory.buf + prefix_offset);
+ printf("Would remove %s\n", qname);
} else if (remove_directories || matches) {
if (!quiet)
- printf("Removing %s\n",
- directory.buf + prefix_offset);
+ printf("Removing %s\n", qname);
if (remove_dir_recursively(&directory, 0) != 0) {
- warning("failed to remove '%s'",
- directory.buf + prefix_offset);
+ warning("failed to remove '%s'", qname);
errors++;
}
} else if (show_only) {
- printf("Would not remove %s\n",
- directory.buf + prefix_offset);
+ printf("Would not remove %s\n", qname);
} else {
- printf("Not removing %s\n",
- directory.buf + prefix_offset);
+ printf("Not removing %s\n", qname);
}
strbuf_reset(&directory);
} else {
if (pathspec && !matches)
continue;
+ qname = quote_path_relative(ent->name, -1, &buf, prefix);
if (show_only) {
- printf("Would remove %s\n",
- ent->name + prefix_offset);
+ printf("Would remove %s\n", qname);
continue;
} else if (!quiet) {
- printf("Removing %s\n",
- ent->name + prefix_offset);
+ printf("Removing %s\n", qname);
}
if (unlink(ent->name) != 0) {
- warning("failed to remove '%s'", ent->name);
+ warning("failed to remove '%s'", qname);
errors++;
}
}
diff --git a/builtin-commit.c b/builtin-commit.c
index f49c22e642..660a3458f7 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -198,6 +198,8 @@ static void create_base_index(void)
opts.head_idx = 1;
opts.index_only = 1;
opts.merge = 1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
opts.fn = oneway_merge;
tree = parse_tree_indirect(head_sha1);
diff --git a/builtin-describe.c b/builtin-describe.c
index 2f1e7ba150..df554b30af 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -21,6 +21,7 @@ static int longformat;
static int abbrev = DEFAULT_ABBREV;
static int max_candidates = 10;
const char *pattern = NULL;
+static int always;
struct commit_name {
struct tag *tag;
@@ -156,7 +157,7 @@ static void display_name(struct commit_name *n)
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(n->sha1);
- if (!n->tag || !n->tag->tag)
+ if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
die("annotated tag %s not available", n->path);
if (strcmp(n->tag->tag, n->path))
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
@@ -166,9 +167,11 @@ static void display_name(struct commit_name *n)
printf("%s", n->tag->tag);
else
printf("%s", n->path);
- if (longformat)
- printf("-0-g%s",
- find_unique_abbrev(n->tag->tagged->sha1, abbrev));
+}
+
+static void show_suffix(int depth, const unsigned char *sha1)
+{
+ printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
}
static void describe(const char *arg, int last_one)
@@ -195,7 +198,12 @@ static void describe(const char *arg, int last_one)
n = cmit->util;
if (n) {
+ /*
+ * Exact match to an existing ref.
+ */
display_name(n);
+ if (longformat)
+ show_suffix(0, n->tag->tagged->sha1);
printf("\n");
return;
}
@@ -250,8 +258,14 @@ static void describe(const char *arg, int last_one)
}
}
- if (!match_cnt)
- die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+ if (!match_cnt) {
+ const unsigned char *sha1 = cmit->object.sha1;
+ if (always) {
+ printf("%s\n", find_unique_abbrev(sha1, abbrev));
+ return;
+ }
+ die("cannot describe '%s'", sha1_to_hex(sha1));
+ }
qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
@@ -281,8 +295,7 @@ static void describe(const char *arg, int last_one)
display_name(all_matches[0].name);
if (abbrev)
- printf("-%d-g%s", all_matches[0].depth,
- find_unique_abbrev(cmit->object.sha1, abbrev));
+ show_suffix(all_matches[0].depth, cmit->object.sha1);
printf("\n");
if (!last_one)
@@ -305,6 +318,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
"consider <n> most recent tags (default: 10)"),
OPT_STRING(0, "match", &pattern, "pattern",
"only consider tags matching <pattern>"),
+ OPT_BOOLEAN(0, "always", &always,
+ "show abbreviated commit object as fallback"),
OPT_END(),
};
@@ -320,11 +335,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
die("--long is incompatible with --abbrev=0");
if (contains) {
- const char **args = xmalloc((6 + argc) * sizeof(char*));
+ const char **args = xmalloc((7 + argc) * sizeof(char*));
int i = 0;
args[i++] = "name-rev";
args[i++] = "--name-only";
args[i++] = "--no-undefined";
+ if (always)
+ args[i++] = "--always";
if (!all) {
args[i++] = "--tags";
if (pattern) {
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index b23e886d75..7b28024224 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -18,7 +18,7 @@ static struct fetch_pack_args args = {
};
static const char fetch_pack_usage[] =
-"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
#define COMPLETE (1U << 0)
#define COMMON (1U << 1)
@@ -41,7 +41,8 @@ static void rev_list_push(struct commit *commit, int mark)
commit->object.flags |= mark;
if (!(commit->object.parsed))
- parse_commit(commit);
+ if (parse_commit(commit))
+ return;
insert_by_date(commit, &rev_list);
@@ -83,7 +84,8 @@ static void mark_common(struct commit *commit,
if (!ancestors_only && !(o->flags & POPPED))
non_common_revs--;
if (!o->parsed && !dont_parse)
- parse_commit(commit);
+ if (parse_commit(commit))
+ return;
for (parents = commit->parents;
parents;
@@ -103,20 +105,20 @@ static const unsigned char* get_rev(void)
while (commit == NULL) {
unsigned int mark;
- struct commit_list* parents;
+ struct commit_list *parents = NULL;
if (rev_list == NULL || non_common_revs == 0)
return NULL;
commit = rev_list->item;
if (!(commit->object.parsed))
- parse_commit(commit);
+ if (!parse_commit(commit))
+ parents = commit->parents;
+
commit->object.flags |= POPPED;
if (!(commit->object.flags & COMMON))
non_common_revs--;
- parents = commit->parents;
-
if (commit->object.flags & COMMON) {
/* do not send "have", and ignore ancestors */
commit = NULL;
@@ -174,13 +176,14 @@ static int find_common(int fd[2], unsigned char *result_sha1,
}
if (!fetching)
- packet_write(fd[1], "want %s%s%s%s%s%s%s\n",
+ packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
sha1_to_hex(remote),
(multi_ack ? " multi_ack" : ""),
(use_sideband == 2 ? " side-band-64k" : ""),
(use_sideband == 1 ? " side-band" : ""),
(args.use_thin_pack ? " thin-pack" : ""),
(args.no_progress ? " no-progress" : ""),
+ (args.include_tag ? " include-tag" : ""),
" ofs-delta");
else
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -212,7 +215,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
if (!lookup_object(sha1))
die("object not found: %s", line);
/* make sure that it is parsed as shallow */
- parse_object(sha1);
+ if (!parse_object(sha1))
+ die("error in object: %s", line);
if (unregister_shallow(sha1))
die("no shallow found: %s", line);
continue;
@@ -680,6 +684,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.use_thin_pack = 1;
continue;
}
+ if (!strcmp("--include-tag", arg)) {
+ args.include_tag = 1;
+ continue;
+ }
if (!strcmp("--all", arg)) {
args.fetch_all = 1;
continue;
diff --git a/builtin-fetch.c b/builtin-fetch.c
index ac335f20fe..b2b9935ed6 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -40,6 +40,8 @@ static struct option builtin_fetch_options[] = {
"force overwrite of local branch"),
OPT_SET_INT('t', "tags", &tags,
"fetch all tags and associated objects", TAGS_SET),
+ OPT_SET_INT('n', NULL, &tags,
+ "do not fetch all tags (--no-tags)", TAGS_UNSET),
OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
"allow updating of HEAD ref"),
@@ -101,6 +103,10 @@ static void add_merge_config(struct ref **head,
}
}
+static void find_non_local_tags(struct transport *transport,
+ struct ref **head,
+ struct ref ***tail);
+
static struct ref *get_ref_map(struct transport *transport,
struct refspec *refs, int ref_count, int tags,
int *autotags)
@@ -157,8 +163,11 @@ static struct ref *get_ref_map(struct transport *transport,
if (!ref_map)
die("Couldn't find remote ref HEAD");
ref_map->merge = 1;
+ tail = &ref_map->next;
}
}
+ if (tags == TAGS_DEFAULT && *autotags)
+ find_non_local_tags(transport, &ref_map, &tail);
ref_remove_duplicates(ref_map);
return ref_map;
@@ -452,18 +461,28 @@ static int add_existing(const char *refname, const unsigned char *sha1,
return 0;
}
-static struct ref *find_non_local_tags(struct transport *transport,
- struct ref *fetch_map)
+static int will_fetch(struct ref **head, const unsigned char *sha1)
+{
+ struct ref *rm = *head;
+ while (rm) {
+ if (!hashcmp(rm->old_sha1, sha1))
+ return 1;
+ rm = rm->next;
+ }
+ return 0;
+}
+
+static void find_non_local_tags(struct transport *transport,
+ struct ref **head,
+ struct ref ***tail)
{
- static struct path_list existing_refs = { NULL, 0, 0, 0 };
+ struct path_list existing_refs = { NULL, 0, 0, 0 };
struct path_list new_refs = { NULL, 0, 0, 1 };
char *ref_name;
int ref_name_len;
const unsigned char *ref_sha1;
const struct ref *tag_ref;
struct ref *rm = NULL;
- struct ref *ref_map = NULL;
- struct ref **tail = &ref_map;
const struct ref *ref;
for_each_ref(add_existing, &existing_refs);
@@ -489,7 +508,8 @@ static struct ref *find_non_local_tags(struct transport *transport,
if (!path_list_has_path(&existing_refs, ref_name) &&
!path_list_has_path(&new_refs, ref_name) &&
- has_sha1_file(ref->old_sha1)) {
+ (has_sha1_file(ref->old_sha1) ||
+ will_fetch(head, ref->old_sha1))) {
path_list_insert(ref_name, &new_refs);
rm = alloc_ref(strlen(ref_name) + 1);
@@ -498,19 +518,19 @@ static struct ref *find_non_local_tags(struct transport *transport,
strcpy(rm->peer_ref->name, ref_name);
hashcpy(rm->old_sha1, ref_sha1);
- *tail = rm;
- tail = &rm->next;
+ **tail = rm;
+ *tail = &rm->next;
}
free(ref_name);
}
-
- return ref_map;
+ path_list_clear(&existing_refs, 0);
+ path_list_clear(&new_refs, 0);
}
static int do_fetch(struct transport *transport,
struct refspec *refs, int ref_count)
{
- struct ref *ref_map, *fetch_map;
+ struct ref *ref_map;
struct ref *rm;
int autotags = (transport->remote->fetch_tags == 1);
if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
@@ -537,26 +557,28 @@ static int do_fetch(struct transport *transport,
read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
}
+ if (tags == TAGS_DEFAULT && autotags)
+ transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
if (fetch_refs(transport, ref_map)) {
free_refs(ref_map);
return 1;
}
-
- fetch_map = ref_map;
+ free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
* following ... */
if (tags == TAGS_DEFAULT && autotags) {
- ref_map = find_non_local_tags(transport, fetch_map);
+ struct ref **tail = &ref_map;
+ ref_map = NULL;
+ find_non_local_tags(transport, &ref_map, &tail);
if (ref_map) {
+ transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
fetch_refs(transport, ref_map);
}
free_refs(ref_map);
}
- free_refs(fetch_map);
-
transport_disconnect(transport);
return 0;
diff --git a/builtin-gc.c b/builtin-gc.c
index 045bf0e487..95917d74a8 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -26,12 +26,13 @@ static int pack_refs = 1;
static int aggressive_window = -1;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 20;
+static char *prune_expire = "2.weeks.ago";
#define MAX_ADD 10
static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
-static const char *argv_prune[] = {"prune", NULL};
+static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
static const char *argv_rerere[] = {"rerere", "gc", NULL};
static int gc_config(const char *var, const char *value)
@@ -55,6 +56,17 @@ static int gc_config(const char *var, const char *value)
gc_auto_pack_limit = git_config_int(var, value);
return 0;
}
+ if (!strcmp(var, "gc.pruneexpire")) {
+ if (!value)
+ return config_error_nonbool(var);
+ if (strcmp(value, "now")) {
+ unsigned long now = approxidate("now");
+ if (approxidate(value) >= now)
+ return error("Invalid %s: '%s'", var, value);
+ }
+ prune_expire = xstrdup(value);
+ return 0;
+ }
return git_default_config(var, value);
}
@@ -234,7 +246,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
return error(FAILED_RUN, argv_repack[0]);
- if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
+ argv_prune[2] = prune_expire;
+ if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
return error(FAILED_RUN, argv_prune[0]);
if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
diff --git a/builtin-log.c b/builtin-log.c
index fe8fc6f22a..5c00725f03 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -20,6 +20,7 @@
static int default_show_root = 1;
static const char *fmt_patch_subject_prefix = "PATCH";
+static const char *fmt_pretty;
static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
{
@@ -54,6 +55,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
+ if (fmt_pretty)
+ rev->commit_format = get_commit_format(fmt_pretty);
rev->verbose_header = 1;
DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
rev->show_root_diff = default_show_root;
@@ -221,6 +224,8 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value)
{
+ if (!strcmp(var, "format.pretty"))
+ return git_config_string(&fmt_pretty, var, value);
if (!strcmp(var, "format.subjectprefix")) {
if (!value)
config_error_nonbool(var);
@@ -657,6 +662,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
int i;
const char *encoding = "utf-8";
struct diff_options opts;
+ int need_8bit_cte = 0;
if (rev->commit_format != CMIT_FMT_EMAIL)
die("Cover letter needs email format");
@@ -667,7 +673,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
head_sha1 = sha1_to_hex(head->object.sha1);
- log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers);
+ log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
+ &need_8bit_cte);
committer = git_committer_info(0);
@@ -676,7 +683,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
encoding);
pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
- encoding, 0);
+ encoding, need_8bit_cte);
pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
printf("%s\n", sb.buf);
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 25dbfb4499..dc7eab89b3 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -574,17 +574,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
pathspec = get_pathspec(prefix, argv + i);
/* Verify that the pathspec matches the prefix */
- if (pathspec) {
- if (argc != i) {
- int cnt;
- for (cnt = 0; pathspec[cnt]; cnt++)
- ;
- if (cnt != (argc - i))
- exit(1); /* error message already given */
- }
+ if (pathspec)
prefix = verify_pathspec(prefix);
- } else if (argc != i)
- exit(1); /* error message already given */
/* Treat unmatching pathspec elements as errors */
if (pathspec && error_unmatch) {
diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c
index 023754986e..8907a89d6c 100644
--- a/builtin-ls-remote.c
+++ b/builtin-ls-remote.c
@@ -94,11 +94,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
ref = transport_get_remote_refs(transport);
- transport_disconnect(transport);
-
- if (!ref)
+ if (transport_disconnect(transport))
return 1;
-
for ( ; ref; ref = ref->next) {
if (!check_ref_type(ref, flags))
continue;
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index adce6d4635..3605960c2d 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -57,7 +57,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
if (!f)
ret = error("Could not open %s for writing", filename);
- else if (fwrite(result.ptr, result.size, 1, f) != 1)
+ else if (result.size &&
+ fwrite(result.ptr, result.size, 1, f) != 1)
ret = error("Could not write to %s", filename);
else if (fclose(f))
ret = error("Could not close %s", filename);
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 6fe4102c0c..910c0d20e7 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -11,11 +11,11 @@
#include "tree-walk.h"
#include "diff.h"
#include "diffcore.h"
-#include "run-command.h"
#include "tag.h"
#include "unpack-trees.h"
#include "path-list.h"
#include "xdiff-interface.h"
+#include "ll-merge.h"
#include "interpolate.h"
#include "attr.h"
#include "merge-recursive.h"
@@ -213,6 +213,8 @@ static int git_merge_trees(int index_only,
opts.merge = 1;
opts.head_idx = 2;
opts.fn = threeway_merge;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
init_tree_desc_from_tree(t+0, common);
init_tree_desc_from_tree(t+1, head);
@@ -615,364 +617,16 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
mm->size = size;
}
-/*
- * Customizable low-level merge drivers support.
- */
-
-struct ll_merge_driver;
-typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
- const char *path,
- mmfile_t *orig,
- mmfile_t *src1, const char *name1,
- mmfile_t *src2, const char *name2,
- mmbuffer_t *result);
-
-struct ll_merge_driver {
- const char *name;
- const char *description;
- ll_merge_fn fn;
- const char *recursive;
- struct ll_merge_driver *next;
- char *cmdline;
-};
-
-/*
- * Built-in low-levels
- */
-static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
- const char *path_unused,
- mmfile_t *orig,
- mmfile_t *src1, const char *name1,
- mmfile_t *src2, const char *name2,
- mmbuffer_t *result)
-{
- /*
- * The tentative merge result is "ours" for the final round,
- * or common ancestor for an internal merge. Still return
- * "conflicted merge" status.
- */
- mmfile_t *stolen = index_only ? orig : src1;
-
- result->ptr = stolen->ptr;
- result->size = stolen->size;
- stolen->ptr = NULL;
- return 1;
-}
-
-static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
- const char *path_unused,
- mmfile_t *orig,
- mmfile_t *src1, const char *name1,
- mmfile_t *src2, const char *name2,
- mmbuffer_t *result)
-{
- xpparam_t xpp;
-
- if (buffer_is_binary(orig->ptr, orig->size) ||
- buffer_is_binary(src1->ptr, src1->size) ||
- buffer_is_binary(src2->ptr, src2->size)) {
- warning("Cannot merge binary files: %s vs. %s\n",
- name1, name2);
- return ll_binary_merge(drv_unused, path_unused,
- orig, src1, name1,
- src2, name2,
- result);
- }
-
- memset(&xpp, 0, sizeof(xpp));
- return xdl_merge(orig,
- src1, name1,
- src2, name2,
- &xpp, XDL_MERGE_ZEALOUS,
- result);
-}
-
-static int ll_union_merge(const struct ll_merge_driver *drv_unused,
- const char *path_unused,
- mmfile_t *orig,
- mmfile_t *src1, const char *name1,
- mmfile_t *src2, const char *name2,
- mmbuffer_t *result)
-{
- char *src, *dst;
- long size;
- const int marker_size = 7;
-
- int status = ll_xdl_merge(drv_unused, path_unused,
- orig, src1, NULL, src2, NULL, result);
- if (status <= 0)
- return status;
- size = result->size;
- src = dst = result->ptr;
- while (size) {
- char ch;
- if ((marker_size < size) &&
- (*src == '<' || *src == '=' || *src == '>')) {
- int i;
- ch = *src;
- for (i = 0; i < marker_size; i++)
- if (src[i] != ch)
- goto not_a_marker;
- if (src[marker_size] != '\n')
- goto not_a_marker;
- src += marker_size + 1;
- size -= marker_size + 1;
- continue;
- }
- not_a_marker:
- do {
- ch = *src++;
- *dst++ = ch;
- size--;
- } while (ch != '\n' && size);
- }
- result->size = dst - result->ptr;
- return 0;
-}
-
-#define LL_BINARY_MERGE 0
-#define LL_TEXT_MERGE 1
-#define LL_UNION_MERGE 2
-static struct ll_merge_driver ll_merge_drv[] = {
- { "binary", "built-in binary merge", ll_binary_merge },
- { "text", "built-in 3-way text merge", ll_xdl_merge },
- { "union", "built-in union merge", ll_union_merge },
-};
-
-static void create_temp(mmfile_t *src, char *path)
-{
- int fd;
-
- strcpy(path, ".merge_file_XXXXXX");
- fd = xmkstemp(path);
- if (write_in_full(fd, src->ptr, src->size) != src->size)
- die("unable to write temp-file");
- close(fd);
-}
-
-/*
- * User defined low-level merge driver support.
- */
-static int ll_ext_merge(const struct ll_merge_driver *fn,
- const char *path,
- mmfile_t *orig,
- mmfile_t *src1, const char *name1,
- mmfile_t *src2, const char *name2,
- mmbuffer_t *result)
-{
- char temp[3][50];
- char cmdbuf[2048];
- struct interp table[] = {
- { "%O" },
- { "%A" },
- { "%B" },
- };
- struct child_process child;
- const char *args[20];
- int status, fd, i;
- struct stat st;
-
- if (fn->cmdline == NULL)
- die("custom merge driver %s lacks command line.", fn->name);
-
- result->ptr = NULL;
- result->size = 0;
- create_temp(orig, temp[0]);
- create_temp(src1, temp[1]);
- create_temp(src2, temp[2]);
-
- interp_set_entry(table, 0, temp[0]);
- interp_set_entry(table, 1, temp[1]);
- interp_set_entry(table, 2, temp[2]);
-
- output(1, "merging %s using %s", path,
- fn->description ? fn->description : fn->name);
-
- interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
-
- memset(&child, 0, sizeof(child));
- child.argv = args;
- args[0] = "sh";
- args[1] = "-c";
- args[2] = cmdbuf;
- args[3] = NULL;
-
- status = run_command(&child);
- if (status < -ERR_RUN_COMMAND_FORK)
- ; /* failure in run-command */
- else
- status = -status;
- fd = open(temp[1], O_RDONLY);
- if (fd < 0)
- goto bad;
- if (fstat(fd, &st))
- goto close_bad;
- result->size = st.st_size;
- result->ptr = xmalloc(result->size + 1);
- if (read_in_full(fd, result->ptr, result->size) != result->size) {
- free(result->ptr);
- result->ptr = NULL;
- result->size = 0;
- }
- close_bad:
- close(fd);
- bad:
- for (i = 0; i < 3; i++)
- unlink(temp[i]);
- return status;
-}
-
-/*
- * merge.default and merge.driver configuration items
- */
-static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
-static const char *default_ll_merge;
-
-static int read_merge_config(const char *var, const char *value)
-{
- struct ll_merge_driver *fn;
- const char *ep, *name;
- int namelen;
-
- if (!strcmp(var, "merge.default")) {
- if (!value)
- return config_error_nonbool(var);
- default_ll_merge = strdup(value);
- return 0;
- }
-
- /*
- * We are not interested in anything but "merge.<name>.variable";
- * especially, we do not want to look at variables such as
- * "merge.summary", "merge.tool", and "merge.verbosity".
- */
- if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
- return 0;
-
- /*
- * Find existing one as we might be processing merge.<name>.var2
- * after seeing merge.<name>.var1.
- */
- name = var + 6;
- namelen = ep - name;
- for (fn = ll_user_merge; fn; fn = fn->next)
- if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
- break;
- if (!fn) {
- fn = xcalloc(1, sizeof(struct ll_merge_driver));
- fn->name = xmemdupz(name, namelen);
- fn->fn = ll_ext_merge;
- *ll_user_merge_tail = fn;
- ll_user_merge_tail = &(fn->next);
- }
-
- ep++;
-
- if (!strcmp("name", ep)) {
- if (!value)
- return config_error_nonbool(var);
- fn->description = strdup(value);
- return 0;
- }
-
- if (!strcmp("driver", ep)) {
- if (!value)
- return config_error_nonbool(var);
- /*
- * merge.<name>.driver specifies the command line:
- *
- * command-line
- *
- * The command-line will be interpolated with the following
- * tokens and is given to the shell:
- *
- * %O - temporary file name for the merge base.
- * %A - temporary file name for our version.
- * %B - temporary file name for the other branches' version.
- *
- * The external merge driver should write the results in the
- * file named by %A, and signal that it has done with zero exit
- * status.
- */
- fn->cmdline = strdup(value);
- return 0;
- }
-
- if (!strcmp("recursive", ep)) {
- if (!value)
- return config_error_nonbool(var);
- fn->recursive = strdup(value);
- return 0;
- }
-
- return 0;
-}
-
-static void initialize_ll_merge(void)
-{
- if (ll_user_merge_tail)
- return;
- ll_user_merge_tail = &ll_user_merge;
- git_config(read_merge_config);
-}
-
-static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
-{
- struct ll_merge_driver *fn;
- const char *name;
- int i;
-
- initialize_ll_merge();
-
- if (ATTR_TRUE(merge_attr))
- return &ll_merge_drv[LL_TEXT_MERGE];
- else if (ATTR_FALSE(merge_attr))
- return &ll_merge_drv[LL_BINARY_MERGE];
- else if (ATTR_UNSET(merge_attr)) {
- if (!default_ll_merge)
- return &ll_merge_drv[LL_TEXT_MERGE];
- else
- name = default_ll_merge;
- }
- else
- name = merge_attr;
-
- for (fn = ll_user_merge; fn; fn = fn->next)
- if (!strcmp(fn->name, name))
- return fn;
-
- for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
- if (!strcmp(ll_merge_drv[i].name, name))
- return &ll_merge_drv[i];
-
- /* default to the 3-way */
- return &ll_merge_drv[LL_TEXT_MERGE];
-}
-
-static const char *git_path_check_merge(const char *path)
-{
- static struct git_attr_check attr_merge_check;
-
- if (!attr_merge_check.attr)
- attr_merge_check.attr = git_attr("merge", 5);
-
- if (git_checkattr(path, 1, &attr_merge_check))
- return NULL;
- return attr_merge_check.value;
-}
-
-static int ll_merge(mmbuffer_t *result_buf,
- struct diff_filespec *o,
- struct diff_filespec *a,
- struct diff_filespec *b,
- const char *branch1,
- const char *branch2)
+static int merge_3way(mmbuffer_t *result_buf,
+ struct diff_filespec *o,
+ struct diff_filespec *a,
+ struct diff_filespec *b,
+ const char *branch1,
+ const char *branch2)
{
mmfile_t orig, src1, src2;
char *name1, *name2;
int merge_status;
- const char *ll_driver_name;
- const struct ll_merge_driver *driver;
name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
@@ -981,14 +635,9 @@ static int ll_merge(mmbuffer_t *result_buf,
fill_mm(a->sha1, &src1);
fill_mm(b->sha1, &src2);
- ll_driver_name = git_path_check_merge(a->path);
- driver = find_ll_merge_driver(ll_driver_name);
-
- if (index_only && driver->recursive)
- driver = find_ll_merge_driver(driver->recursive);
- merge_status = driver->fn(driver, a->path,
- &orig, &src1, name1, &src2, name2,
- result_buf);
+ merge_status = ll_merge(result_buf, a->path, &orig,
+ &src1, name1, &src2, name2,
+ index_only);
free(name1);
free(name2);
@@ -1019,9 +668,20 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
result.merge = 1;
- result.mode = a->mode == o->mode ? b->mode: a->mode;
+ /*
+ * Merge modes
+ */
+ if (a->mode == b->mode || a->mode == o->mode)
+ result.mode = b->mode;
+ else {
+ result.mode = a->mode;
+ if (b->mode != o->mode) {
+ result.clean = 0;
+ result.merge = 1;
+ }
+ }
- if (sha_eq(a->sha1, o->sha1))
+ if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, o->sha1))
hashcpy(result.sha, b->sha1);
else if (sha_eq(b->sha1, o->sha1))
hashcpy(result.sha, a->sha1);
@@ -1029,8 +689,8 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
mmbuffer_t result_buf;
int merge_status;
- merge_status = ll_merge(&result_buf, o, a, b,
- branch1, branch2);
+ merge_status = merge_3way(&result_buf, o, a, b,
+ branch1, branch2);
if ((merge_status < 0) || !result_buf.ptr)
die("Failed to execute internal merge");
diff --git a/builtin-mv.c b/builtin-mv.c
index 68aa2a68bb..94f6dd2aad 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -19,7 +19,6 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
int count, int base_name)
{
int i;
- int len = prefix ? strlen(prefix) : 0;
const char **result = xmalloc((count + 1) * sizeof(const char *));
memcpy(result, pathspec, count * sizeof(const char *));
result[count] = NULL;
@@ -33,11 +32,8 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
if (last_slash)
result[i] = last_slash + 1;
}
- result[i] = prefix_path(prefix, len, result[i]);
- if (!result[i])
- exit(1); /* error already given */
}
- return result;
+ return get_pathspec(prefix, result);
}
static void show_list(const char *label, struct path_list *list)
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index f22c8b5f5d..384da4db13 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -125,7 +125,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
}
/* returns a static buffer */
-static const char *get_rev_name(struct object *o)
+static const char *get_rev_name(const struct object *o)
{
static char buffer[1024];
struct rev_name *n;
@@ -151,6 +151,26 @@ static const char *get_rev_name(struct object *o)
}
}
+static void show_name(const struct object *obj,
+ const char *caller_name,
+ int always, int allow_undefined, int name_only)
+{
+ const char *name;
+ const unsigned char *sha1 = obj->sha1;
+
+ if (!name_only)
+ printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
+ name = get_rev_name(obj);
+ if (name)
+ printf("%s\n", name);
+ else if (allow_undefined)
+ printf("undefined\n");
+ else if (always)
+ printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
+ else
+ die("cannot describe '%s'", sha1_to_hex(sha1));
+}
+
static char const * const name_rev_usage[] = {
"git-name-rev [options] ( --all | --stdin | <commit>... )",
NULL
@@ -159,7 +179,7 @@ static char const * const name_rev_usage[] = {
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = { 0, 0, NULL };
- int all = 0, transform_stdin = 0, allow_undefined = 1;
+ int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
struct name_ref_data data = { 0, 0, NULL };
struct option opts[] = {
OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
@@ -170,6 +190,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
+ OPT_BOOLEAN(0, "always", &always,
+ "show abbreviated commit object as fallback"),
OPT_END(),
};
@@ -258,35 +280,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
int i, max;
max = get_max_object_index();
- for (i = 0; i < max; i++) {
- struct object * obj = get_indexed_object(i);
- const char *name;
- if (!obj)
- continue;
- if (!data.name_only)
- printf("%s ", sha1_to_hex(obj->sha1));
- name = get_rev_name(obj);
- if (name)
- printf("%s\n", name);
- else if (allow_undefined)
- printf("undefined\n");
- else
- die("cannot describe '%s'", sha1_to_hex(obj->sha1));
- }
+ for (i = 0; i < max; i++)
+ show_name(get_indexed_object(i), NULL,
+ always, allow_undefined, data.name_only);
} else {
int i;
- for (i = 0; i < revs.nr; i++) {
- const char *name;
- if (!data.name_only)
- printf("%s ", revs.objects[i].name);
- name = get_rev_name(revs.objects[i].item);
- if (name)
- printf("%s\n", name);
- else if (allow_undefined)
- printf("undefined\n");
- else
- die("cannot describe '%s'", sha1_to_hex(revs.objects[i].item->sha1));
- }
+ for (i = 0; i < revs.nr; i++)
+ show_name(revs.objects[i].item, revs.objects[i].name,
+ always, allow_undefined, data.name_only);
}
return 0;
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 2799e68338..777f272668 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -15,6 +15,7 @@
#include "revision.h"
#include "list-objects.h"
#include "progress.h"
+#include "refs.h"
#ifdef THREADED_DELTA_SEARCH
#include "thread-utils.h"
@@ -27,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
[--window=N] [--window-memory=N] [--depth=N] \n\
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
- [--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
+ [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
+ [<ref-list | <object-list]";
struct object_entry {
struct pack_idx_entry idx;
@@ -63,7 +65,7 @@ static struct pack_idx_entry **written_list;
static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
static int non_empty;
-static int no_reuse_delta, no_reuse_object, keep_unreachable;
+static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
static int local;
static int incremental;
static int allow_ofs_delta;
@@ -452,6 +454,7 @@ static void write_pack_file(void)
struct pack_header hdr;
int do_progress = progress >> pack_to_stdout;
uint32_t nr_remaining = nr_result;
+ time_t last_mtime = 0;
if (do_progress)
progress_state = start_progress("Writing objects", nr_result);
@@ -502,6 +505,7 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
mode_t mode = umask(0);
+ struct stat st;
char *idx_tmp_name, tmpname[PATH_MAX];
umask(mode);
@@ -509,6 +513,7 @@ static void write_pack_file(void)
idx_tmp_name = write_idx_file(NULL, written_list,
nr_written, sha1);
+
snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
base_name, sha1_to_hex(sha1));
if (adjust_perm(pack_tmp_name, mode))
@@ -517,6 +522,28 @@ static void write_pack_file(void)
if (rename(pack_tmp_name, tmpname))
die("unable to rename temporary pack file: %s",
strerror(errno));
+
+ /*
+ * Packs are runtime accessed in their mtime
+ * order since newer packs are more likely to contain
+ * younger objects. So if we are creating multiple
+ * packs then we should modify the mtime of later ones
+ * to preserve this property.
+ */
+ if (stat(tmpname, &st) < 0) {
+ warning("failed to stat %s: %s",
+ tmpname, strerror(errno));
+ } else if (!last_mtime) {
+ last_mtime = st.st_mtime;
+ } else {
+ struct utimbuf utb;
+ utb.actime = st.st_atime;
+ utb.modtime = --last_mtime;
+ if (utime(tmpname, &utb) < 0)
+ warning("failed utime() on %s: %s",
+ tmpname, strerror(errno));
+ }
+
snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
base_name, sha1_to_hex(sha1));
if (adjust_perm(idx_tmp_name, mode))
@@ -525,6 +552,7 @@ static void write_pack_file(void)
if (rename(idx_tmp_name, tmpname))
die("unable to rename temporary index file: %s",
strerror(errno));
+
free(idx_tmp_name);
free(pack_tmp_name);
puts(sha1_to_hex(sha1));
@@ -1630,6 +1658,18 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
#define ll_find_deltas(l, s, w, d, p) find_deltas(l, &s, w, d, p)
#endif
+static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+ unsigned char peeled[20];
+
+ if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+ !peel_ref(path, peeled) && /* peelable? */
+ !is_null_sha1(peeled) && /* annotated tag? */
+ locate_object_entry(peeled)) /* object packed? */
+ add_object_entry(sha1, OBJ_TAG, NULL, 0);
+ return 0;
+}
+
static void prepare_pack(int window, int depth)
{
struct object_entry **delta_list;
@@ -2033,6 +2073,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
keep_unreachable = 1;
continue;
}
+ if (!strcmp("--include-tag", arg)) {
+ include_tag = 1;
+ continue;
+ }
if (!strcmp("--unpacked", arg) ||
!prefixcmp(arg, "--unpacked=") ||
!strcmp("--reflog", arg) ||
@@ -2109,6 +2153,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
rp_av[rp_ac] = NULL;
get_object_list(rp_ac, rp_av);
}
+ if (include_tag && nr_result)
+ for_each_ref(add_ref_tag, NULL);
stop_progress(&progress_state);
if (non_empty && !nr_result)
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 0138f5a917..e9cfd2bbc5 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -13,16 +13,15 @@
#include "dir.h"
#include "builtin.h"
-#define MAX_TREES 8
static int nr_trees;
-static struct tree *trees[MAX_TREES];
+static struct tree *trees[MAX_UNPACK_TREES];
static int list_tree(unsigned char *sha1)
{
struct tree *tree;
- if (nr_trees >= MAX_TREES)
- die("I cannot read more than %d trees", MAX_TREES);
+ if (nr_trees >= MAX_UNPACK_TREES)
+ die("I cannot read more than %d trees", MAX_UNPACK_TREES);
tree = parse_tree_indirect(sha1);
if (!tree)
return -1;
@@ -97,11 +96,13 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, newfd, stage = 0;
unsigned char sha1[20];
- struct tree_desc t[MAX_TREES];
+ struct tree_desc t[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
git_config(git_default_config);
@@ -220,27 +221,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
if ((opts.dir && !opts.update))
die("--exclude-per-directory is meaningless unless -u");
- if (opts.prefix) {
- int pfxlen = strlen(opts.prefix);
- int pos;
- if (opts.prefix[pfxlen-1] != '/')
- die("prefix must end with /");
- if (stage != 2)
- die("binding merge takes only one tree");
- pos = cache_name_pos(opts.prefix, pfxlen);
- if (0 <= pos)
- die("corrupt index file");
- pos = -pos-1;
- if (pos < active_nr &&
- !strncmp(active_cache[pos]->name, opts.prefix, pfxlen))
- die("subdirectory '%s' already exists.", opts.prefix);
- pos = cache_name_pos(opts.prefix, pfxlen-1);
- if (0 <= pos)
- die("file '%.*s' already exists.",
- pfxlen-1, opts.prefix);
- opts.pos = -1 - pos;
- }
-
if (opts.merge) {
if (stage < 2)
die("just how do you expect me to merge %d trees?", stage-1);
diff --git a/builtin-reflog.c b/builtin-reflog.c
index ab53c8cb7c..280e24e151 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -14,6 +14,8 @@
static const char reflog_expire_usage[] =
"git-reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+static const char reflog_delete_usage[] =
+"git-reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
static unsigned long default_reflog_expire;
static unsigned long default_reflog_expire_unreachable;
@@ -22,9 +24,12 @@ struct cmd_reflog_expire_cb {
struct rev_info revs;
int dry_run;
int stalefix;
+ int rewrite;
+ int updateref;
int verbose;
unsigned long expire_total;
unsigned long expire_unreachable;
+ int recno;
};
struct expire_reflog_cb {
@@ -32,6 +37,7 @@ struct expire_reflog_cb {
const char *ref;
struct commit *ref_commit;
struct cmd_reflog_expire_cb *cmd;
+ unsigned char last_kept_sha1[20];
};
struct collected_reflog {
@@ -213,6 +219,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
if (timestamp < cb->cmd->expire_total)
goto prune;
+ if (cb->cmd->rewrite)
+ osha1 = cb->last_kept_sha1;
+
old = new = NULL;
if (cb->cmd->stalefix &&
(!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
@@ -230,6 +239,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
goto prune;
}
+ if (cb->cmd->recno && --(cb->cmd->recno) == 0)
+ goto prune;
+
if (cb->newlog) {
char sign = (tz < 0) ? '-' : '+';
int zone = (tz < 0) ? (-tz) : tz;
@@ -237,6 +249,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
sha1_to_hex(osha1), sha1_to_hex(nsha1),
email, timestamp, sign, zone,
message);
+ hashcpy(cb->last_kept_sha1, nsha1);
}
if (cb->cmd->verbose)
printf("keep %s", message);
@@ -280,10 +293,20 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
status |= error("%s: %s", strerror(errno),
newlog_path);
unlink(newlog_path);
+ } else if (cmd->updateref &&
+ (write_in_full(lock->lock_fd,
+ sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+ write_in_full(lock->lock_fd, "\n", 1) != 1 ||
+ close_ref(lock) < 0)) {
+ status |= error("Couldn't write %s",
+ lock->lk->filename);
+ unlink(newlog_path);
} else if (rename(newlog_path, log_file)) {
status |= error("cannot rename %s to %s",
newlog_path, log_file);
unlink(newlog_path);
+ } else if (cmd->updateref && commit_ref(lock)) {
+ status |= error("Couldn't set %s", lock->ref_name);
}
}
free(newlog_path);
@@ -358,6 +381,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
cb.expire_unreachable = approxidate(arg + 21);
else if (!strcmp(arg, "--stale-fix"))
cb.stalefix = 1;
+ else if (!strcmp(arg, "--rewrite"))
+ cb.rewrite = 1;
+ else if (!strcmp(arg, "--updateref"))
+ cb.updateref = 1;
else if (!strcmp(arg, "--all"))
do_all = 1;
else if (!strcmp(arg, "--verbose"))
@@ -406,6 +433,78 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
return status;
}
+static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+ const char *email, unsigned long timestamp, int tz,
+ const char *message, void *cb_data)
+{
+ struct cmd_reflog_expire_cb *cb = cb_data;
+ if (!cb->expire_total || timestamp < cb->expire_total)
+ cb->recno++;
+ return 0;
+}
+
+static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
+{
+ struct cmd_reflog_expire_cb cb;
+ int i, status = 0;
+
+ memset(&cb, 0, sizeof(cb));
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+ cb.dry_run = 1;
+ else if (!strcmp(arg, "--rewrite"))
+ cb.rewrite = 1;
+ else if (!strcmp(arg, "--updateref"))
+ cb.updateref = 1;
+ else if (!strcmp(arg, "--verbose"))
+ cb.verbose = 1;
+ else if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
+ else if (arg[0] == '-')
+ usage(reflog_delete_usage);
+ else
+ break;
+ }
+
+ if (argc - i < 1)
+ return error("Nothing to delete?");
+
+ for ( ; i < argc; i++) {
+ const char *spec = strstr(argv[i], "@{");
+ unsigned char sha1[20];
+ char *ep, *ref;
+ int recno;
+
+ if (!spec) {
+ status |= error("Not a reflog: %s", argv[i]);
+ continue;
+ }
+
+ if (!dwim_ref(argv[i], spec - argv[i], sha1, &ref)) {
+ status |= error("%s points nowhere!", argv[i]);
+ continue;
+ }
+
+ recno = strtoul(spec + 2, &ep, 10);
+ if (*ep == '}') {
+ cb.recno = -recno;
+ for_each_reflog_ent(ref, count_reflog_ent, &cb);
+ } else {
+ cb.expire_total = approxidate(spec + 2);
+ for_each_reflog_ent(ref, count_reflog_ent, &cb);
+ cb.expire_total = 0;
+ }
+
+ status |= expire_reflog(ref, sha1, 0, &cb);
+ free(ref);
+ }
+ return status;
+}
+
/*
* main "reflog"
*/
@@ -425,6 +524,9 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[1], "expire"))
return cmd_reflog_expire(argc - 1, argv + 1, prefix);
+ if (!strcmp(argv[1], "delete"))
+ return cmd_reflog_delete(argc - 1, argv + 1, prefix);
+
/* Not a recognized reflog command..*/
usage(reflog_usage);
}
diff --git a/builtin-remote.c b/builtin-remote.c
new file mode 100644
index 0000000000..24e692953b
--- /dev/null
+++ b/builtin-remote.c
@@ -0,0 +1,605 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "transport.h"
+#include "remote.h"
+#include "path-list.h"
+#include "strbuf.h"
+#include "run-command.h"
+#include "refs.h"
+
+static const char * const builtin_remote_usage[] = {
+ "git remote",
+ "git remote add <name> <url>",
+ "git remote rm <name>",
+ "git remote show <name>",
+ "git remote prune <name>",
+ "git remote update [group]",
+ NULL
+};
+
+static int verbose;
+
+static inline int postfixcmp(const char *string, const char *postfix)
+{
+ int len1 = strlen(string), len2 = strlen(postfix);
+ if (len1 < len2)
+ return 1;
+ return strcmp(string + len1 - len2, postfix);
+}
+
+static inline const char *skip_prefix(const char *name, const char *prefix)
+{
+ return !name ? "" :
+ prefixcmp(name, prefix) ? name : name + strlen(prefix);
+}
+
+static int opt_parse_track(const struct option *opt, const char *arg, int not)
+{
+ struct path_list *list = opt->value;
+ if (not)
+ path_list_clear(list, 0);
+ else
+ path_list_append(arg, list);
+ return 0;
+}
+
+static int fetch_remote(const char *name)
+{
+ const char *argv[] = { "fetch", name, NULL };
+ printf("Updating %s\n", name);
+ if (run_command_v_opt(argv, RUN_GIT_CMD))
+ return error("Could not fetch %s", name);
+ return 0;
+}
+
+static int add(int argc, const char **argv)
+{
+ int fetch = 0, mirror = 0;
+ struct path_list track = { NULL, 0, 0 };
+ const char *master = NULL;
+ struct remote *remote;
+ struct strbuf buf, buf2;
+ const char *name, *url;
+ int i;
+
+ struct option options[] = {
+ OPT_GROUP("add specific options"),
+ OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+ OPT_CALLBACK('t', "track", &track, "branch",
+ "branch(es) to track", opt_parse_track),
+ OPT_STRING('m', "master", &master, "branch", "master branch"),
+ OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+
+ if (argc < 2)
+ usage_with_options(builtin_remote_usage, options);
+
+ name = argv[0];
+ url = argv[1];
+
+ remote = remote_get(name);
+ if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
+ remote->fetch_refspec_nr))
+ die("remote %s already exists.", name);
+
+ strbuf_init(&buf, 0);
+ strbuf_init(&buf2, 0);
+
+ strbuf_addf(&buf, "remote.%s.url", name);
+ if (git_config_set(buf.buf, url))
+ return 1;
+
+ if (track.nr == 0)
+ path_list_append("*", &track);
+ for (i = 0; i < track.nr; i++) {
+ struct path_list_item *item = track.items + i;
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "remote.%s.fetch", name);
+
+ strbuf_reset(&buf2);
+ if (mirror)
+ strbuf_addf(&buf2, "refs/%s:refs/%s",
+ item->path, item->path);
+ else
+ strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
+ item->path, name, item->path);
+ if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+ return 1;
+ }
+
+ if (fetch && fetch_remote(name))
+ return 1;
+
+ if (master) {
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
+
+ strbuf_reset(&buf2);
+ strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
+
+ if (create_symref(buf.buf, buf2.buf, "remote add"))
+ return error("Could not setup master '%s'", master);
+ }
+
+ strbuf_release(&buf);
+ strbuf_release(&buf2);
+ path_list_clear(&track, 0);
+
+ return 0;
+}
+
+struct branch_info {
+ char *remote;
+ struct path_list merge;
+};
+
+static struct path_list branch_list;
+
+static int config_read_branches(const char *key, const char *value)
+{
+ if (!prefixcmp(key, "branch.")) {
+ char *name;
+ struct path_list_item *item;
+ struct branch_info *info;
+ enum { REMOTE, MERGE } type;
+
+ key += 7;
+ if (!postfixcmp(key, ".remote")) {
+ name = xstrndup(key, strlen(key) - 7);
+ type = REMOTE;
+ } else if (!postfixcmp(key, ".merge")) {
+ name = xstrndup(key, strlen(key) - 6);
+ type = MERGE;
+ } else
+ return 0;
+
+ item = path_list_insert(name, &branch_list);
+
+ if (!item->util)
+ item->util = xcalloc(sizeof(struct branch_info), 1);
+ info = item->util;
+ if (type == REMOTE) {
+ if (info->remote)
+ warning("more than one branch.%s", key);
+ info->remote = xstrdup(value);
+ } else {
+ char *space = strchr(value, ' ');
+ value = skip_prefix(value, "refs/heads/");
+ while (space) {
+ char *merge;
+ merge = xstrndup(value, space - value);
+ path_list_append(merge, &info->merge);
+ value = skip_prefix(space + 1, "refs/heads/");
+ space = strchr(value, ' ');
+ }
+ path_list_append(xstrdup(value), &info->merge);
+ }
+ }
+ return 0;
+}
+
+static void read_branches(void)
+{
+ if (branch_list.nr)
+ return;
+ git_config(config_read_branches);
+ sort_path_list(&branch_list);
+}
+
+struct ref_states {
+ struct remote *remote;
+ struct strbuf remote_prefix;
+ struct path_list new, stale, tracked;
+};
+
+static int handle_one_branch(const char *refname,
+ const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct ref_states *states = cb_data;
+ struct refspec refspec;
+
+ memset(&refspec, 0, sizeof(refspec));
+ refspec.dst = (char *)refname;
+ if (!remote_find_tracking(states->remote, &refspec)) {
+ struct path_list_item *item;
+ const char *name = skip_prefix(refspec.src, "refs/heads/");
+ if (unsorted_path_list_has_path(&states->tracked, name) ||
+ unsorted_path_list_has_path(&states->new,
+ name))
+ return 0;
+ item = path_list_append(name, &states->stale);
+ item->util = xstrdup(refname);
+ }
+ return 0;
+}
+
+static int get_ref_states(const struct ref *ref, struct ref_states *states)
+{
+ struct ref *fetch_map = NULL, **tail = &fetch_map;
+ int i;
+
+ for (i = 0; i < states->remote->fetch_refspec_nr; i++)
+ if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
+ die("Could not get fetch map for refspec %s",
+ states->remote->fetch_refspec[i]);
+
+ states->new.strdup_paths = states->tracked.strdup_paths = 1;
+ for (ref = fetch_map; ref; ref = ref->next) {
+ struct path_list *target = &states->tracked;
+ unsigned char sha1[20];
+ void *util = NULL;
+
+ if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
+ target = &states->new;
+ else {
+ target = &states->tracked;
+ if (hashcmp(sha1, ref->new_sha1))
+ util = &states;
+ }
+ path_list_append(skip_prefix(ref->name, "refs/heads/"),
+ target)->util = util;
+ }
+ free_refs(fetch_map);
+
+ strbuf_addf(&states->remote_prefix,
+ "refs/remotes/%s/", states->remote->name);
+ for_each_ref(handle_one_branch, states);
+ sort_path_list(&states->stale);
+
+ return 0;
+}
+
+struct branches_for_remote {
+ const char *prefix;
+ struct path_list *branches;
+};
+
+static int add_branch_for_removal(const char *refname,
+ const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct branches_for_remote *branches = cb_data;
+
+ if (!prefixcmp(refname, branches->prefix)) {
+ struct path_list_item *item;
+
+ /* make sure that symrefs are deleted */
+ if (flags & REF_ISSYMREF)
+ return unlink(git_path(refname));
+
+ item = path_list_append(refname, branches->branches);
+ item->util = xmalloc(20);
+ hashcpy(item->util, sha1);
+ }
+
+ return 0;
+}
+
+static int remove_branches(struct path_list *branches)
+{
+ int i, result = 0;
+ for (i = 0; i < branches->nr; i++) {
+ struct path_list_item *item = branches->items + i;
+ const char *refname = item->path;
+ unsigned char *sha1 = item->util;
+
+ if (delete_ref(refname, sha1))
+ result |= error("Could not remove branch %s", refname);
+ }
+ return result;
+}
+
+static int rm(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ struct remote *remote;
+ struct strbuf buf;
+ struct path_list branches = { NULL, 0, 0, 1 };
+ struct branches_for_remote cb_data = { NULL, &branches };
+ int i;
+
+ if (argc != 2)
+ usage_with_options(builtin_remote_usage, options);
+
+ remote = remote_get(argv[1]);
+ if (!remote)
+ die("No such remote: %s", argv[1]);
+
+ strbuf_init(&buf, 0);
+ strbuf_addf(&buf, "remote.%s", remote->name);
+ if (git_config_rename_section(buf.buf, NULL) < 1)
+ return error("Could not remove config section '%s'", buf.buf);
+
+ read_branches();
+ for (i = 0; i < branch_list.nr; i++) {
+ struct path_list_item *item = branch_list.items + i;
+ struct branch_info *info = item->util;
+ if (info->remote && !strcmp(info->remote, remote->name)) {
+ const char *keys[] = { "remote", "merge", NULL }, **k;
+ for (k = keys; *k; k++) {
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "branch.%s.%s",
+ item->path, *k);
+ if (git_config_set(buf.buf, NULL)) {
+ strbuf_release(&buf);
+ return -1;
+ }
+ }
+ }
+ }
+
+ /*
+ * We cannot just pass a function to for_each_ref() which deletes
+ * the branches one by one, since for_each_ref() relies on cached
+ * refs, which are invalidated when deleting a branch.
+ */
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "refs/remotes/%s/", remote->name);
+ cb_data.prefix = buf.buf;
+ i = for_each_ref(add_branch_for_removal, &cb_data);
+ strbuf_release(&buf);
+
+ if (!i)
+ i = remove_branches(&branches);
+ path_list_clear(&branches, 1);
+
+ return i;
+}
+
+static void show_list(const char *title, struct path_list *list)
+{
+ int i;
+
+ if (!list->nr)
+ return;
+
+ printf(title, list->nr > 1 ? "es" : "");
+ printf("\n ");
+ for (i = 0; i < list->nr; i++)
+ printf("%s%s", i ? " " : "", list->items[i].path);
+ printf("\n");
+}
+
+static int show_or_prune(int argc, const char **argv, int prune)
+{
+ int dry_run = 0, result = 0;
+ struct option options[] = {
+ OPT_GROUP("show specific options"),
+ OPT__DRY_RUN(&dry_run),
+ OPT_END()
+ };
+ struct ref_states states;
+
+ argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+
+ if (argc < 1)
+ usage_with_options(builtin_remote_usage, options);
+
+ memset(&states, 0, sizeof(states));
+ for (; argc; argc--, argv++) {
+ struct transport *transport;
+ const struct ref *ref;
+ struct strbuf buf;
+ int i, got_states;
+
+ states.remote = remote_get(*argv);
+ if (!states.remote)
+ return error("No such remote: %s", *argv);
+ transport = transport_get(NULL, states.remote->url_nr > 0 ?
+ states.remote->url[0] : NULL);
+ ref = transport_get_remote_refs(transport);
+ transport_disconnect(transport);
+
+ read_branches();
+ got_states = get_ref_states(ref, &states);
+ if (got_states)
+ result = error("Error getting local info for '%s'",
+ states.remote->name);
+
+ if (prune) {
+ struct strbuf buf;
+ int prefix_len;
+
+ strbuf_init(&buf, 0);
+ if (states.remote->fetch_refspec_nr == 1 &&
+ states.remote->fetch->pattern &&
+ !strcmp(states.remote->fetch->src,
+ states.remote->fetch->dst))
+ /* handle --mirror remote */
+ strbuf_addstr(&buf, "refs/heads/");
+ else
+ strbuf_addf(&buf, "refs/remotes/%s/", *argv);
+ prefix_len = buf.len;
+
+ for (i = 0; i < states.stale.nr; i++) {
+ strbuf_setlen(&buf, prefix_len);
+ strbuf_addstr(&buf, states.stale.items[i].path);
+ result |= delete_ref(buf.buf, NULL);
+ }
+
+ strbuf_release(&buf);
+ goto cleanup_states;
+ }
+
+ printf("* remote %s\n URL: %s\n", *argv,
+ states.remote->url_nr > 0 ?
+ states.remote->url[0] : "(no URL)");
+
+ for (i = 0; i < branch_list.nr; i++) {
+ struct path_list_item *branch = branch_list.items + i;
+ struct branch_info *info = branch->util;
+ int j;
+
+ if (!info->merge.nr || strcmp(*argv, info->remote))
+ continue;
+ printf(" Remote branch%s merged with 'git pull' "
+ "while on branch %s\n ",
+ info->merge.nr > 1 ? "es" : "",
+ branch->path);
+ for (j = 0; j < info->merge.nr; j++)
+ printf(" %s", info->merge.items[j].path);
+ printf("\n");
+ }
+
+ if (got_states)
+ continue;
+ strbuf_init(&buf, 0);
+ strbuf_addf(&buf, " New remote branch%%s (next fetch will "
+ "store in remotes/%s)", states.remote->name);
+ show_list(buf.buf, &states.new);
+ strbuf_release(&buf);
+ show_list(" Stale tracking branch%s (use 'git remote prune')",
+ &states.stale);
+ show_list(" Tracked remote branch%s",
+ &states.tracked);
+
+ if (states.remote->push_refspec_nr) {
+ printf(" Local branch%s pushed with 'git push'\n ",
+ states.remote->push_refspec_nr > 1 ?
+ "es" : "");
+ for (i = 0; i < states.remote->push_refspec_nr; i++) {
+ struct refspec *spec = states.remote->push + i;
+ printf(" %s%s%s%s", spec->force ? "+" : "",
+ skip_prefix(spec->src, "refs/heads/"),
+ spec->dst ? ":" : "",
+ skip_prefix(spec->dst, "refs/heads/"));
+ }
+ }
+cleanup_states:
+ /* NEEDSWORK: free remote */
+ path_list_clear(&states.new, 0);
+ path_list_clear(&states.stale, 0);
+ path_list_clear(&states.tracked, 0);
+ }
+
+ return result;
+}
+
+static int get_one_remote_for_update(struct remote *remote, void *priv)
+{
+ struct path_list *list = priv;
+ if (!remote->skip_default_update)
+ path_list_append(xstrdup(remote->name), list);
+ return 0;
+}
+
+struct remote_group {
+ const char *name;
+ struct path_list *list;
+} remote_group;
+
+static int get_remote_group(const char *key, const char *value)
+{
+ if (!prefixcmp(key, "remotes.") &&
+ !strcmp(key + 8, remote_group.name)) {
+ /* split list by white space */
+ int space = strcspn(value, " \t\n");
+ while (*value) {
+ if (space > 1)
+ path_list_append(xstrndup(value, space),
+ remote_group.list);
+ value += space + (value[space] != '\0');
+ space = strcspn(value, " \t\n");
+ }
+ }
+
+ return 0;
+}
+
+static int update(int argc, const char **argv)
+{
+ int i, result = 0;
+ struct path_list list = { NULL, 0, 0, 0 };
+ static const char *default_argv[] = { NULL, "default", NULL };
+
+ if (argc < 2) {
+ argc = 2;
+ argv = default_argv;
+ }
+
+ remote_group.list = &list;
+ for (i = 1; i < argc; i++) {
+ remote_group.name = argv[i];
+ result = git_config(get_remote_group);
+ }
+
+ if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default"))
+ result = for_each_remote(get_one_remote_for_update, &list);
+
+ for (i = 0; i < list.nr; i++)
+ result |= fetch_remote(list.items[i].path);
+
+ /* all names were strdup()ed or strndup()ed */
+ list.strdup_paths = 1;
+ path_list_clear(&list, 0);
+
+ return result;
+}
+
+static int get_one_entry(struct remote *remote, void *priv)
+{
+ struct path_list *list = priv;
+
+ path_list_append(remote->name, list)->util = remote->url_nr ?
+ (void *)remote->url[0] : NULL;
+ if (remote->url_nr > 1)
+ warning("Remote %s has more than one URL", remote->name);
+
+ return 0;
+}
+
+static int show_all(void)
+{
+ struct path_list list = { NULL, 0, 0 };
+ int result = for_each_remote(get_one_entry, &list);
+
+ if (!result) {
+ int i;
+
+ sort_path_list(&list);
+ for (i = 0; i < list.nr; i++) {
+ struct path_list_item *item = list.items + i;
+ printf("%s%s%s\n", item->path,
+ verbose ? "\t" : "",
+ verbose && item->util ?
+ (const char *)item->util : "");
+ }
+ }
+ return result;
+}
+
+int cmd_remote(int argc, const char **argv, const char *prefix)
+{
+ struct option options[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_END()
+ };
+ int result;
+
+ argc = parse_options(argc, argv, options, builtin_remote_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (argc < 1)
+ result = show_all();
+ else if (!strcmp(argv[0], "add"))
+ result = add(argc, argv);
+ else if (!strcmp(argv[0], "rm"))
+ result = rm(argc, argv);
+ else if (!strcmp(argv[0], "show"))
+ result = show_or_prune(argc, argv, 0);
+ else if (!strcmp(argv[0], "prune"))
+ result = show_or_prune(argc, argv, 1);
+ else if (!strcmp(argv[0], "update"))
+ result = update(argc, argv);
+ else {
+ error("Unknown subcommand: %s", argv[0]);
+ usage_with_options(builtin_remote_usage, options);
+ }
+
+ return result ? 1 : 0;
+}
diff --git a/builtin-reset.c b/builtin-reset.c
index bb3e19240a..79424bb26e 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -17,9 +17,13 @@
#include "diffcore.h"
#include "tree.h"
#include "branch.h"
+#include "parse-options.h"
-static const char builtin_reset_usage[] =
-"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
+static const char * const git_reset_usage[] = {
+ "git-reset [--mixed | --soft | --hard] [-q] [<commit>]",
+ "git-reset [--mixed] <commit> [--] <paths>...",
+ NULL
+};
static char *args_to_str(const char **argv)
{
@@ -165,40 +169,31 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
int cmd_reset(int argc, const char **argv, const char *prefix)
{
- int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0;
+ int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
const char *rev = "HEAD";
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
struct commit *commit;
char *reflog_action, msg[1024];
+ const struct option options[] = {
+ OPT_SET_INT(0, "mixed", &reset_type,
+ "reset HEAD and index", MIXED),
+ OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
+ OPT_SET_INT(0, "hard", &reset_type,
+ "reset HEAD, index and working tree", HARD),
+ OPT_BOOLEAN('q', NULL, &quiet,
+ "disable showing new HEAD in hard reset"),
+ OPT_END()
+ };
git_config(git_default_config);
+ argc = parse_options(argc, argv, options, git_reset_usage,
+ PARSE_OPT_KEEP_DASHDASH);
reflog_action = args_to_str(argv);
setenv("GIT_REFLOG_ACTION", reflog_action, 0);
- while (i < argc) {
- if (!strcmp(argv[i], "--mixed")) {
- reset_type = MIXED;
- i++;
- }
- else if (!strcmp(argv[i], "--soft")) {
- reset_type = SOFT;
- i++;
- }
- else if (!strcmp(argv[i], "--hard")) {
- reset_type = HARD;
- i++;
- }
- else if (!strcmp(argv[i], "-q")) {
- quiet = 1;
- i++;
- }
- else
- break;
- }
-
- if (i < argc && argv[i][0] != '-')
+ if (i < argc && strcmp(argv[i], "--"))
rev = argv[i++];
if (get_sha1(rev, sha1))
@@ -211,8 +206,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (i < argc && !strcmp(argv[i], "--"))
i++;
- else if (i < argc && argv[i][0] == '-')
- usage(builtin_reset_usage);
/* git reset tree [--] paths... can be used to
* load chosen paths from the tree into the index without
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 90dbb9d7c1..0351d54435 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -322,18 +322,24 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
o->type = OPTION_CALLBACK;
o->help = xstrdup(skipspaces(s));
o->value = &parsed;
+ o->flags = PARSE_OPT_NOARG;
o->callback = &parseopt_dump;
- switch (s[-1]) {
- case '=':
- s--;
- break;
- case '?':
- o->flags = PARSE_OPT_OPTARG;
- s--;
- break;
- default:
- o->flags = PARSE_OPT_NOARG;
- break;
+ while (s > sb.buf && strchr("*=?!", s[-1])) {
+ switch (*--s) {
+ case '=':
+ o->flags &= ~PARSE_OPT_NOARG;
+ break;
+ case '?':
+ o->flags &= ~PARSE_OPT_NOARG;
+ o->flags |= PARSE_OPT_OPTARG;
+ break;
+ case '!':
+ o->flags |= PARSE_OPT_NONEG;
+ break;
+ case '*':
+ o->flags |= PARSE_OPT_HIDDEN;
+ break;
+ }
}
if (s - sb.buf == 1) /* short option only */
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index af31abaaf8..bd795b1db7 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -70,11 +70,12 @@ static void insert_one_record(struct shortlog *log,
else
free(buffer);
+ /* Skip any leading whitespace, including any blank lines. */
+ while (*oneline && isspace(*oneline))
+ oneline++;
eol = strchr(oneline, '\n');
if (!eol)
eol = oneline + strlen(oneline);
- while (*oneline && isspace(*oneline) && *oneline != '\n')
- oneline++;
if (!prefixcmp(oneline, "[PATCH")) {
char *eob = strchr(oneline, ']');
if (eob && (!eol || eob < eol))
@@ -228,7 +229,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
{
struct shortlog log;
struct rev_info rev;
+ int nongit;
+ prefix = setup_git_directory_gently(&nongit);
shortlog_init(&log);
/* since -n is a shadowed rev argument, parse our args first */
@@ -258,7 +261,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
die ("unrecognized argument: %s", argv[1]);
/* assume HEAD if from a tty */
- if (!rev.pending.nr && isatty(0))
+ if (!nongit && !rev.pending.nr && isatty(0))
add_head_to_pending(&rev);
if (rev.pending.nr == 0) {
read_from_stdin(&log);
diff --git a/builtin-tag.c b/builtin-tag.c
index 28c36fdcd1..8dd959fe1c 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -50,12 +50,15 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
size_t len = strlen(editor);
int i = 0;
const char *args[6];
+ struct strbuf arg0;
+ strbuf_init(&arg0, 0);
if (strcspn(editor, "$ \t'") != len) {
/* there are specials */
+ strbuf_addf(&arg0, "%s \"$@\"", editor);
args[i++] = "sh";
args[i++] = "-c";
- args[i++] = "$0 \"$@\"";
+ args[i++] = arg0.buf;
}
args[i++] = editor;
args[i++] = path;
@@ -63,6 +66,7 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
if (run_command_v_opt_cd_env(args, 0, NULL, env))
die("There was a problem with the editor %s.", editor);
+ strbuf_release(&arg0);
}
if (!buffer)
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 9d2a854950..50e07faa12 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -7,13 +7,11 @@
#include "commit.h"
#include "tag.h"
#include "tree.h"
-#include "tree-walk.h"
#include "progress.h"
#include "decorate.h"
-#include "fsck.h"
-static int dry_run, quiet, recover, has_errors, strict;
-static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
+static int dry_run, quiet, recover, has_errors;
+static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
/* We always read in 4kB chunks. */
static unsigned char buffer[4096];
@@ -33,16 +31,6 @@ static struct obj_buffer *lookup_object_buffer(struct object *base)
return lookup_decoration(&obj_decorate, base);
}
-static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
-{
- struct obj_buffer *obj;
- obj = xcalloc(1, sizeof(struct obj_buffer));
- obj->buffer = buffer;
- obj->size = size;
- if (add_decoration(&obj_decorate, object, obj))
- die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
-}
-
/*
* Make sure at least "min" bytes are available in the buffer, and
* return the pointer to the buffer.
@@ -146,58 +134,9 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
struct obj_info {
off_t offset;
unsigned char sha1[20];
- struct object *obj;
};
-#define FLAG_OPEN (1u<<20)
-#define FLAG_WRITTEN (1u<<21)
-
static struct obj_info *obj_list;
-unsigned nr_objects;
-
-static void write_cached_object(struct object *obj)
-{
- unsigned char sha1[20];
- struct obj_buffer *obj_buf = lookup_object_buffer(obj);
- if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
- die("failed to write object %s", sha1_to_hex(obj->sha1));
- obj->flags |= FLAG_WRITTEN;
-}
-
-static int check_object(struct object *obj, int type, void *data)
-{
- if (!obj)
- return 0;
-
- if (obj->flags & FLAG_WRITTEN)
- return 1;
-
- if (type != OBJ_ANY && obj->type != type)
- die("object type mismatch");
-
- if (!(obj->flags & FLAG_OPEN)) {
- unsigned long size;
- int type = sha1_object_info(obj->sha1, &size);
- if (type != obj->type || type <= 0)
- die("object of unexpected type");
- obj->flags |= FLAG_WRITTEN;
- return 1;
- }
-
- if (fsck_object(obj, 1, fsck_error_function))
- die("Error in object");
- if (!fsck_walk(obj, check_object, 0))
- die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
- write_cached_object(obj);
- return 1;
-}
-
-static void write_rest(void)
-{
- unsigned i;
- for (i = 0; i < nr_objects; i++)
- check_object(obj_list[i].obj, OBJ_ANY, 0);
-}
static void added_object(unsigned nr, enum object_type type,
void *data, unsigned long size);
@@ -205,36 +144,9 @@ static void added_object(unsigned nr, enum object_type type,
static void write_object(unsigned nr, enum object_type type,
void *buf, unsigned long size)
{
+ if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+ die("failed to write object");
added_object(nr, type, buf, size);
- if (!strict) {
- if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
- die("failed to write object");
- free(buf);
- obj_list[nr].obj = 0;
- } else if (type == OBJ_BLOB) {
- struct blob *blob;
- if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
- die("failed to write object");
- free(buf);
-
- blob = lookup_blob(obj_list[nr].sha1);
- if (blob)
- blob->object.flags |= FLAG_WRITTEN;
- else
- die("invalid blob object");
- obj_list[nr].obj = 0;
- } else {
- struct object *obj;
- int eaten;
- hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
- obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
- if (!obj)
- die("invalid %s", typename(type));
- /* buf is stored via add_object_buffer and in obj, if its a tree or commit */
- add_object_buffer(obj, buf, size);
- obj->flags |= FLAG_OPEN;
- obj_list[nr].obj = obj;
- }
}
static void resolve_delta(unsigned nr, enum object_type type,
@@ -251,6 +163,7 @@ static void resolve_delta(unsigned nr, enum object_type type,
die("failed to apply delta");
free(delta);
write_object(nr, type, result, result_size);
+ free(result);
}
static void added_object(unsigned nr, enum object_type type,
@@ -280,8 +193,7 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
if (!dry_run && buf)
write_object(nr, type, buf, size);
- else
- free(buf);
+ free(buf);
}
static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
@@ -424,8 +336,7 @@ static void unpack_all(void)
int i;
struct progress *progress = NULL;
struct pack_header *hdr = fill(sizeof(struct pack_header));
-
- nr_objects = ntohl(hdr->hdr_entries);
+ unsigned nr_objects = ntohl(hdr->hdr_entries);
if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
die("bad pack file");
@@ -436,7 +347,6 @@ static void unpack_all(void)
if (!quiet)
progress = start_progress("Unpacking objects", nr_objects);
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
- memset(obj_list, 0, nr_objects * sizeof(*obj_list));
for (i = 0; i < nr_objects; i++) {
unpack_one(i);
display_progress(progress, i + 1);
@@ -472,10 +382,6 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
recover = 1;
continue;
}
- if (!strcmp(arg, "--strict")) {
- strict = 1;
- continue;
- }
if (!prefixcmp(arg, "--pack_header=")) {
struct pack_header *hdr;
char *c;
@@ -501,8 +407,6 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
unpack_all();
SHA1_Update(&ctx, buffer, offset);
SHA1_Final(sha1, &ctx);
- if (strict)
- write_rest();
if (hashcmp(fill(20), sha1))
die("final sha1 did not match");
use(20);
diff --git a/builtin.h b/builtin.h
index 674c8a141f..95126fd0c1 100644
--- a/builtin.h
+++ b/builtin.h
@@ -67,6 +67,7 @@ extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
extern int cmd_push(int argc, const char **argv, const char *prefix);
extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
extern int cmd_reflog(int argc, const char **argv, const char *prefix);
+extern int cmd_remote(int argc, const char **argv, const char *prefix);
extern int cmd_config(int argc, const char **argv, const char *prefix);
extern int cmd_rerere(int argc, const char **argv, const char *prefix);
extern int cmd_reset(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index e230302623..2a1e7ec6b2 100644
--- a/cache.h
+++ b/cache.h
@@ -346,12 +346,12 @@ extern void verify_non_filename(const char *prefix, const char *name);
/* Initialize and use the cache information */
extern int read_index(struct index_state *);
extern int read_index_from(struct index_state *, const char *path);
-extern int write_index(struct index_state *, int newfd);
+extern int write_index(const struct index_state *, int newfd);
extern int discard_index(struct index_state *);
-extern int unmerged_index(struct index_state *);
+extern int unmerged_index(const struct index_state *);
extern int verify_path(const char *path);
extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
-extern int index_name_pos(struct index_state *, const char *name, int namelen);
+extern int index_name_pos(const struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
@@ -368,8 +368,8 @@ extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
#define CE_MATCH_IGNORE_VALID 01
/* do not check the contents but report dirty on racily-clean entries */
#define CE_MATCH_RACY_IS_DIRTY 02
-extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
@@ -536,6 +536,7 @@ extern int create_symref(const char *ref, const char *refs_heads_master, const c
extern int validate_headref(const char *ref);
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
extern void *read_object_with_reference(const unsigned char *sha1,
@@ -543,6 +544,9 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned long *size,
unsigned char *sha1_ret);
+extern struct object *peel_to_type(const char *name, int namelen,
+ struct object *o, enum object_type);
+
enum date_mode {
DATE_NORMAL = 0,
DATE_RELATIVE,
diff --git a/commit.h b/commit.h
index a1e9591426..2f63bc8b2f 100644
--- a/commit.h
+++ b/commit.h
@@ -70,7 +70,7 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
struct strbuf *,
int abbrev, const char *subject,
const char *after_subject, enum date_mode,
- int non_ascii_present);
+ int need_8bit_cte);
void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
const char *line, enum date_mode dmode,
const char *encoding);
@@ -80,7 +80,7 @@ void pp_title_line(enum cmit_fmt fmt,
const char *subject,
const char *after_subject,
const char *encoding,
- int plain_non_ascii);
+ int need_8bit_cte);
void pp_remainder(enum cmit_fmt fmt,
const char **msg_p,
struct strbuf *sb,
diff --git a/compat/snprintf.c b/compat/snprintf.c
new file mode 100644
index 0000000000..dbfc2d6b6e
--- /dev/null
+++ b/compat/snprintf.c
@@ -0,0 +1,40 @@
+#include "../git-compat-util.h"
+
+#undef vsnprintf
+int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
+{
+ char *s;
+ int ret;
+
+ ret = vsnprintf(str, maxsize, format, ap);
+ if (ret != -1)
+ return ret;
+
+ s = NULL;
+ if (maxsize < 128)
+ maxsize = 128;
+
+ while (ret == -1) {
+ maxsize *= 4;
+ str = realloc(s, maxsize);
+ if (! str)
+ break;
+ s = str;
+ ret = vsnprintf(str, maxsize, format, ap);
+ }
+ free(s);
+ return ret;
+}
+
+int git_snprintf(char *str, size_t maxsize, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = git_vsnprintf(str, maxsize, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
diff --git a/config.mak.in b/config.mak.in
index ee6c33df03..7868dfd93a 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -46,3 +46,5 @@ NO_MKDTEMP=@NO_MKDTEMP@
NO_ICONV=@NO_ICONV@
OLD_ICONV=@OLD_ICONV@
NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
+SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
diff --git a/configure.ac b/configure.ac
index 85d7ef570d..82584e9153 100644
--- a/configure.ac
+++ b/configure.ac
@@ -326,6 +326,60 @@ else
NO_C99_FORMAT=
fi
AC_SUBST(NO_C99_FORMAT)
+#
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+AC_CACHE_CHECK([whether system succeeds to read fopen'ed directory],
+ [ac_cv_fread_reads_directories],
+[
+AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+ [[char c;
+ FILE *f = fopen(".", "r");
+ return f && fread(&c, 1, 1, f)]])],
+ [ac_cv_fread_reads_directories=no],
+ [ac_cv_fread_reads_directories=yes])
+])
+if test $ac_cv_fread_reads_directories = yes; then
+ FREAD_READS_DIRECTORIES=UnfortunatelyYes
+else
+ FREAD_READS_DIRECTORIES=
+fi
+AC_SUBST(FREAD_READS_DIRECTORIES)
+#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+AC_CACHE_CHECK([whether snprintf() and/or vsnprintf() return bogus value],
+ [ac_cv_snprintf_returns_bogus],
+[
+AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+ #include "stdarg.h"
+
+ int test_vsnprintf(char *str, size_t maxsize, const char *format, ...)
+ {
+ int ret;
+ va_list ap;
+ va_start(ap, format);
+ ret = vsnprintf(str, maxsize, format, ap);
+ va_end(ap);
+ return ret;
+ }],
+ [[char buf[6];
+ if (test_vsnprintf(buf, 3, "%s", "12345") != 5
+ || strcmp(buf, "12")) return 1;
+ if (snprintf(buf, 3, "%s", "12345") != 5
+ || strcmp(buf, "12")) return 1]])],
+ [ac_cv_snprintf_returns_bogus=no],
+ [ac_cv_snprintf_returns_bogus=yes])
+])
+if test $ac_cv_snprintf_returns_bogus = yes; then
+ SNPRINTF_RETURNS_BOGUS=UnfortunatelyYes
+else
+ SNPRINTF_RETURNS_BOGUS=
+fi
+AC_SUBST(SNPRINTF_RETURNS_BOGUS)
## Checks for library functions.
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 8f70e1efc1..5046f69934 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -70,22 +70,30 @@ __git_ps1 ()
local b
if [ -d "$g/../.dotest" ]
then
- r="|AM/REBASE"
+ if test -f "$g/../.dotest/rebasing"
+ then
+ r="|REBASE"
+ elif test -f "$g/../.dotest/applying"
+ then
+ r="|AM"
+ else
+ r="|AM/REBASE"
+ fi
b="$(git symbolic-ref HEAD 2>/dev/null)"
elif [ -f "$g/.dotest-merge/interactive" ]
then
r="|REBASE-i"
- b="$(cat $g/.dotest-merge/head-name)"
+ b="$(cat "$g/.dotest-merge/head-name")"
elif [ -d "$g/.dotest-merge" ]
then
r="|REBASE-m"
- b="$(cat $g/.dotest-merge/head-name)"
+ b="$(cat "$g/.dotest-merge/head-name")"
elif [ -f "$g/MERGE_HEAD" ]
then
r="|MERGING"
b="$(git symbolic-ref HEAD 2>/dev/null)"
else
- if [ -f $g/BISECT_LOG ]
+ if [ -f "$g/BISECT_LOG" ]
then
r="|BISECTING"
fi
@@ -93,7 +101,7 @@ __git_ps1 ()
then
if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
then
- b="$(cut -c1-7 $g/HEAD)..."
+ b="$(cut -c1-7 "$g/HEAD")..."
fi
fi
fi
@@ -113,13 +121,21 @@ __gitcomp ()
if [ $# -gt 2 ]; then
cur="$3"
fi
- for c in $1; do
- case "$c$4" in
- --*=*) all="$all$c$4$s" ;;
- *.) all="$all$c$4$s" ;;
- *) all="$all$c$4 $s" ;;
- esac
- done
+ case "$cur" in
+ --*=)
+ COMPREPLY=()
+ return
+ ;;
+ *)
+ for c in $1; do
+ case "$c$4" in
+ --*=*) all="$all$c$4$s" ;;
+ *.) all="$all$c$4$s" ;;
+ *) all="$all$c$4 $s" ;;
+ esac
+ done
+ ;;
+ esac
IFS=$s
COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur"))
return
@@ -376,7 +392,6 @@ __git_commands ()
show-index) : plumbing;;
ssh-*) : transport;;
stripspace) : plumbing;;
- svn) : import export;;
symbolic-ref) : plumbing;;
tar-tree) : deprecated;;
unpack-file) : plumbing;;
@@ -420,6 +435,22 @@ __git_aliased_command ()
done
}
+__git_find_subcommand ()
+{
+ local word subcommand c=1
+
+ while [ $c -lt $COMP_CWORD ]; do
+ word="${COMP_WORDS[c]}"
+ for subcommand in $1; do
+ if [ "$subcommand" = "$word" ]; then
+ echo "$subcommand"
+ return
+ fi
+ done
+ c=$((++c))
+ done
+}
+
__git_whitespacelist="nowarn warn error error-all strip"
_git_am ()
@@ -477,24 +508,14 @@ _git_add ()
_git_bisect ()
{
- local i c=1 command
- while [ $c -lt $COMP_CWORD ]; do
- i="${COMP_WORDS[c]}"
- case "$i" in
- start|bad|good|reset|visualize|replay|log)
- command="$i"
- break
- ;;
- esac
- c=$((++c))
- done
-
- if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
- __gitcomp "start bad good reset visualize replay log"
+ local subcommands="start bad good reset visualize replay log"
+ local subcommand="$(__git_find_subcommand "$subcommands")"
+ if [ -z "$subcommand" ]; then
+ __gitcomp "$subcommands"
return
fi
- case "$command" in
+ case "$subcommand" in
bad|good|reset)
__gitcomp "$(__git_refs)"
;;
@@ -506,7 +527,33 @@ _git_bisect ()
_git_branch ()
{
- __gitcomp "$(__git_refs)"
+ local i c=1 only_local_ref="n" has_r="n"
+
+ while [ $c -lt $COMP_CWORD ]; do
+ i="${COMP_WORDS[c]}"
+ case "$i" in
+ -d|-m) only_local_ref="y" ;;
+ -r) has_r="y" ;;
+ esac
+ c=$((++c))
+ done
+
+ case "${COMP_WORDS[COMP_CWORD]}" in
+ --*=*) COMPREPLY=() ;;
+ --*)
+ __gitcomp "
+ --color --no-color --verbose --abbrev= --no-abbrev
+ --track --no-track
+ "
+ ;;
+ *)
+ if [ $only_local_ref = "y" -a $has_r = "n" ]; then
+ __gitcomp "$(__git_heads)"
+ else
+ __gitcomp "$(__git_refs)"
+ fi
+ ;;
+ esac
}
_git_bundle ()
@@ -802,8 +849,8 @@ _git_push ()
_git_rebase ()
{
- local cur="${COMP_WORDS[COMP_CWORD]}"
- if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then
+ local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+ if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then
__gitcomp "--continue --skip --abort"
return
fi
@@ -922,7 +969,6 @@ _git_config ()
core.sharedRepository
core.warnAmbiguousRefs
core.compression
- core.legacyHeaders
core.packedGitWindowSize
core.packedGitLimit
clean.requireForce
@@ -999,21 +1045,13 @@ _git_config ()
_git_remote ()
{
- local i c=1 command
- while [ $c -lt $COMP_CWORD ]; do
- i="${COMP_WORDS[c]}"
- case "$i" in
- add|rm|show|prune|update) command="$i"; break ;;
- esac
- c=$((++c))
- done
-
- if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
- __gitcomp "add rm show prune update"
+ local subcommands="add rm show prune update"
+ local subcommand="$(__git_find_subcommand "$subcommands")"
+ if [ -z "$subcommand" ]; then
return
fi
- case "$command" in
+ case "$subcommand" in
rm|show|prune)
__gitcomp "$(__git_remotes)"
;;
@@ -1087,34 +1125,107 @@ _git_show ()
_git_stash ()
{
- __gitcomp 'list show apply clear'
+ local subcommands='save list show apply clear drop pop create'
+ if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
+ __gitcomp "$subcommands"
+ fi
}
_git_submodule ()
{
- local i c=1 command
- while [ $c -lt $COMP_CWORD ]; do
- i="${COMP_WORDS[c]}"
- case "$i" in
- add|status|init|update) command="$i"; break ;;
- esac
- c=$((++c))
- done
-
- if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+ local subcommands="add status init update"
+ if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
__gitcomp "--quiet --cached"
;;
*)
- __gitcomp "add status init update"
+ __gitcomp "$subcommands"
;;
esac
return
fi
}
+_git_svn ()
+{
+ local subcommands="
+ init fetch clone rebase dcommit log find-rev
+ set-tree commit-diff info create-ignore propget
+ proplist show-ignore show-externals
+ "
+ local subcommand="$(__git_find_subcommand "$subcommands")"
+ if [ -z "$subcommand" ]; then
+ __gitcomp "$subcommands"
+ else
+ local remote_opts="--username= --config-dir= --no-auth-cache"
+ local fc_opts="
+ --follow-parent --authors-file= --repack=
+ --no-metadata --use-svm-props --use-svnsync-props
+ --log-window-size= --no-checkout --quiet
+ --repack-flags --user-log-author $remote_opts
+ "
+ local init_opts="
+ --template= --shared= --trunk= --tags=
+ --branches= --stdlayout --minimize-url
+ --no-metadata --use-svm-props --use-svnsync-props
+ --rewrite-root= $remote_opts
+ "
+ local cmt_opts="
+ --edit --rmdir --find-copies-harder --copy-similarity=
+ "
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$subcommand,$cur" in
+ fetch,--*)
+ __gitcomp "--revision= --fetch-all $fc_opts"
+ ;;
+ clone,--*)
+ __gitcomp "--revision= $fc_opts $init_opts"
+ ;;
+ init,--*)
+ __gitcomp "$init_opts"
+ ;;
+ dcommit,--*)
+ __gitcomp "
+ --merge --strategy= --verbose --dry-run
+ --fetch-all --no-rebase $cmt_opts $fc_opts
+ "
+ ;;
+ set-tree,--*)
+ __gitcomp "--stdin $cmt_opts $fc_opts"
+ ;;
+ create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
+ show-externals,--*)
+ __gitcomp "--revision="
+ ;;
+ log,--*)
+ __gitcomp "
+ --limit= --revision= --verbose --incremental
+ --oneline --show-commit --non-recursive
+ --authors-file=
+ "
+ ;;
+ rebase,--*)
+ __gitcomp "
+ --merge --verbose --strategy= --local
+ --fetch-all $fc_opts
+ "
+ ;;
+ commit-diff,--*)
+ __gitcomp "--message= --file= --revision= $cmt_opts"
+ ;;
+ info,--*)
+ __gitcomp "--url"
+ ;;
+ *)
+ COMPREPLY=()
+ ;;
+ esac
+ fi
+}
+
_git_tag ()
{
local i c=1 f=0
@@ -1164,15 +1275,18 @@ _git ()
c=$((++c))
done
- if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+ if [ -z "$command" ]; then
case "${COMP_WORDS[COMP_CWORD]}" in
--*=*) COMPREPLY=() ;;
--*) __gitcomp "
+ --paginate
--no-pager
--git-dir=
--bare
--version
--exec-path
+ --work-tree=
+ --help
"
;;
*) __gitcomp "$(__git_commands) $(__git_aliases)" ;;
@@ -1216,6 +1330,7 @@ _git ()
show-branch) _git_log ;;
stash) _git_stash ;;
submodule) _git_submodule ;;
+ svn) _git_svn ;;
tag) _git_tag ;;
whatchanged) _git_log ;;
*) COMPREPLY=() ;;
@@ -1266,6 +1381,7 @@ complete -o default -o nospace -F _git_shortlog git-shortlog
complete -o default -o nospace -F _git_show git-show
complete -o default -o nospace -F _git_stash git-stash
complete -o default -o nospace -F _git_submodule git-submodule
+complete -o default -o nospace -F _git_svn git-svn
complete -o default -o nospace -F _git_log git-show-branch
complete -o default -o nospace -F _git_tag git-tag
complete -o default -o nospace -F _git_log git-whatchanged
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index c9268234a5..4fa853fae7 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -1299,7 +1299,7 @@ Return the list of files that haven't been handled."
(let (author-name author-email subject date msg)
(with-temp-buffer
(let ((coding-system (git-get-logoutput-coding-system)))
- (git-call-process-env t nil "log" "-1" commit)
+ (git-call-process-env t nil "log" "-1" "--pretty=medium" commit)
(goto-char (point-min))
(when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
(setq author-name (match-string 1))
diff --git a/git-remote.perl b/contrib/examples/git-remote.perl
index b30ed734e7..b30ed734e7 100755
--- a/git-remote.perl
+++ b/contrib/examples/git-remote.perl
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index be96600753..650ea34176 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -850,29 +850,32 @@ class P4Sync(Command):
## Should move this out, doesn't use SELF.
def readP4Files(self, files):
+ filesForCommit = []
+ filesToRead = []
+
for f in files:
+ includeFile = True
for val in self.clientSpecDirs:
if f['path'].startswith(val[0]):
- if val[1] > 0:
- f['include'] = True
- else:
- f['include'] = False
+ if val[1] <= 0:
+ includeFile = False
break
- files = [f for f in files
- if f['action'] != 'delete' and
- (f.has_key('include') == False or f['include'] == True)]
+ if includeFile:
+ filesForCommit.append(f)
+ if f['action'] != 'delete':
+ filesToRead.append(f)
- if not files:
- return []
+ filedata = []
+ if len(filesToRead) > 0:
+ filedata = p4CmdList('-x - print',
+ stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
+ for f in filesToRead]),
+ stdin_mode='w+')
- filedata = p4CmdList('-x - print',
- stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
- for f in files]),
- stdin_mode='w+')
- if "p4ExitCode" in filedata[0]:
- die("Problems executing p4. Error: [%d]."
- % (filedata[0]['p4ExitCode']));
+ if "p4ExitCode" in filedata[0]:
+ die("Problems executing p4. Error: [%d]."
+ % (filedata[0]['p4ExitCode']));
j = 0;
contents = {}
@@ -896,10 +899,12 @@ class P4Sync(Command):
contents[stat['depotFile']] = text
- for f in files:
- assert not f.has_key('data')
- f['data'] = contents[f['path']]
- return files
+ for f in filesForCommit:
+ path = f['path']
+ if contents.has_key(path):
+ f['data'] = contents[path]
+
+ return filesForCommit
def commit(self, details, files, branch, branchPrefixes, parent = ""):
epoch = details["time"]
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 77c88ebf1f..62a740c482 100644
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -567,7 +567,7 @@ generate_general_email()
echo ""
if [ "$newrev_type" = "commit" ]; then
echo $LOGBEGIN
- git show --no-color --root -s $newrev
+ git show --no-color --root -s --pretty=medium $newrev
echo $LOGEND
else
# What can we do here? The tag marks an object that is not
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 2838546d16..7959eab902 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -63,7 +63,7 @@ mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
# create the links to the original repo. explictly exclude index, HEAD and
# logs/HEAD from the list since they are purely related to the current working
# directory, and should not be shared.
-for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache
+for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
do
case $x in
*/*)
diff --git a/diff-lib.c b/diff-lib.c
index 4581b594d0..52dbac34a4 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -600,8 +600,7 @@ static void mark_merge_entries(void)
*/
static void do_oneway_diff(struct unpack_trees_options *o,
struct cache_entry *idx,
- struct cache_entry *tree,
- int idx_pos, int idx_nr)
+ struct cache_entry *tree)
{
struct rev_info *revs = o->unpack_data;
int match_missing, cached;
@@ -642,32 +641,19 @@ static void do_oneway_diff(struct unpack_trees_options *o,
show_modified(revs, tree, idx, 1, cached, match_missing);
}
-/*
- * Count how many index entries go with the first one
- */
-static inline int count_skip(const struct cache_entry *src, int pos)
+static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
{
- int skip = 1;
-
- /* We can only have multiple entries if the first one is not stage-0 */
- if (ce_stage(src)) {
- struct cache_entry **p = active_cache + pos;
- int namelen = ce_namelen(src);
-
- for (;;) {
- const struct cache_entry *ce;
- pos++;
- if (pos >= active_nr)
- break;
- ce = *++p;
- if (ce_namelen(ce) != namelen)
- break;
- if (memcmp(ce->name, src->name, namelen))
- break;
- skip++;
- }
+ int len = ce_namelen(ce);
+ const struct index_state *index = o->src_index;
+
+ while (o->pos < index->cache_nr) {
+ struct cache_entry *next = index->cache[o->pos];
+ if (len != ce_namelen(next))
+ break;
+ if (memcmp(ce->name, next->name, len))
+ break;
+ o->pos++;
}
- return skip;
}
/*
@@ -685,17 +671,14 @@ static inline int count_skip(const struct cache_entry *src, int pos)
* the fairly complex unpack_trees() semantic requirements, including
* the skipping, the path matching, the type conflict cases etc.
*/
-static int oneway_diff(struct cache_entry **src,
- struct unpack_trees_options *o,
- int index_pos)
+static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
{
- int skip = 0;
struct cache_entry *idx = src[0];
struct cache_entry *tree = src[1];
struct rev_info *revs = o->unpack_data;
- if (index_pos >= 0)
- skip = count_skip(idx, index_pos);
+ if (idx && ce_stage(idx))
+ skip_same_name(idx, o);
/*
* Unpack-trees generates a DF/conflict entry if
@@ -707,9 +690,9 @@ static int oneway_diff(struct cache_entry **src,
tree = NULL;
if (ce_path_match(idx ? idx : tree, revs->prune_data))
- do_oneway_diff(o, idx, tree, index_pos, skip);
+ do_oneway_diff(o, idx, tree);
- return skip;
+ return 0;
}
int run_diff_index(struct rev_info *revs, int cached)
@@ -734,6 +717,8 @@ int run_diff_index(struct rev_info *revs, int cached)
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = revs;
+ opts.src_index = &the_index;
+ opts.dst_index = NULL;
init_tree_desc(&t, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts))
@@ -787,6 +772,8 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = &revs;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
init_tree_desc(&t, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts))
diff --git a/diff.c b/diff.c
index 00e1590c6e..8022e678d1 100644
--- a/diff.c
+++ b/diff.c
@@ -256,40 +256,41 @@ static int count_lines(const char *data, int size)
return count;
}
-static void print_line_count(int count)
+static void print_line_count(FILE *file, int count)
{
switch (count) {
case 0:
- printf("0,0");
+ fprintf(file, "0,0");
break;
case 1:
- printf("1");
+ fprintf(file, "1");
break;
default:
- printf("1,%d", count);
+ fprintf(file, "1,%d", count);
break;
}
}
-static void copy_file_with_prefix(int prefix, const char *data, int size,
+static void copy_file_with_prefix(FILE *file,
+ int prefix, const char *data, int size,
const char *set, const char *reset)
{
int ch, nl_just_seen = 1;
while (0 < size--) {
ch = *data++;
if (nl_just_seen) {
- fputs(set, stdout);
- putchar(prefix);
+ fputs(set, file);
+ putc(prefix, file);
}
if (ch == '\n') {
nl_just_seen = 1;
- fputs(reset, stdout);
+ fputs(reset, file);
} else
nl_just_seen = 0;
- putchar(ch);
+ putc(ch, file);
}
if (!nl_just_seen)
- printf("%s\n\\ No newline at end of file\n", reset);
+ fprintf(file, "%s\n\\ No newline at end of file\n", reset);
}
static void emit_rewrite_diff(const char *name_a,
@@ -322,17 +323,18 @@ static void emit_rewrite_diff(const char *name_a,
diff_populate_filespec(two, 0);
lc_a = count_lines(one->data, one->size);
lc_b = count_lines(two->data, two->size);
- printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
- metainfo, a_name.buf, name_a_tab, reset,
- metainfo, b_name.buf, name_b_tab, reset, fraginfo);
- print_line_count(lc_a);
- printf(" +");
- print_line_count(lc_b);
- printf(" @@%s\n", reset);
+ fprintf(o->file,
+ "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
+ metainfo, a_name.buf, name_a_tab, reset,
+ metainfo, b_name.buf, name_b_tab, reset, fraginfo);
+ print_line_count(o->file, lc_a);
+ fprintf(o->file, " +");
+ print_line_count(o->file, lc_b);
+ fprintf(o->file, " @@%s\n", reset);
if (lc_a)
- copy_file_with_prefix('-', one->data, one->size, old, reset);
+ copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset);
if (lc_b)
- copy_file_with_prefix('+', two->data, two->size, new, reset);
+ copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset);
}
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@ -372,9 +374,10 @@ static void diff_words_append(char *line, unsigned long len,
struct diff_words_data {
struct xdiff_emit_state xm;
struct diff_words_buffer minus, plus;
+ FILE *file;
};
-static void print_word(struct diff_words_buffer *buffer, int len, int color,
+static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color,
int suppress_newline)
{
const char *ptr;
@@ -391,15 +394,15 @@ static void print_word(struct diff_words_buffer *buffer, int len, int color,
len--;
}
- fputs(diff_get_color(1, color), stdout);
- fwrite(ptr, len, 1, stdout);
- fputs(diff_get_color(1, DIFF_RESET), stdout);
+ fputs(diff_get_color(1, color), file);
+ fwrite(ptr, len, 1, file);
+ fputs(diff_get_color(1, DIFF_RESET), file);
if (eol) {
if (suppress_newline)
buffer->suppressed_newline = 1;
else
- putchar('\n');
+ putc('\n', file);
}
}
@@ -409,20 +412,23 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
if (diff_words->minus.suppressed_newline) {
if (line[0] != '+')
- putchar('\n');
+ putc('\n', diff_words->file);
diff_words->minus.suppressed_newline = 0;
}
len--;
switch (line[0]) {
case '-':
- print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
+ print_word(diff_words->file,
+ &diff_words->minus, len, DIFF_FILE_OLD, 1);
break;
case '+':
- print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
+ print_word(diff_words->file,
+ &diff_words->plus, len, DIFF_FILE_NEW, 0);
break;
case ' ':
- print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
+ print_word(diff_words->file,
+ &diff_words->plus, len, DIFF_PLAIN, 0);
diff_words->minus.current += len;
break;
}
@@ -466,7 +472,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
diff_words->minus.text.size = diff_words->plus.text.size = 0;
if (diff_words->minus.suppressed_newline) {
- putchar('\n');
+ putc('\n', diff_words->file);
diff_words->minus.suppressed_newline = 0;
}
}
@@ -481,6 +487,7 @@ struct emit_callback {
const char **label_path;
struct diff_words_data *diff_words;
int *found_changesp;
+ FILE *file;
};
static void free_diff_words_data(struct emit_callback *ecbdata)
@@ -505,11 +512,11 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
return "";
}
-static void emit_line(const char *set, const char *reset, const char *line, int len)
+static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
{
- fputs(set, stdout);
- fwrite(line, len, 1, stdout);
- fputs(reset, stdout);
+ fputs(set, file);
+ fwrite(line, len, 1, file);
+ fputs(reset, file);
}
static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
@@ -518,13 +525,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
if (!*ws)
- emit_line(set, reset, line, len);
+ emit_line(ecbdata->file, set, reset, line, len);
else {
/* Emit just the prefix, then the rest. */
- emit_line(set, reset, line, ecbdata->nparents);
+ emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
(void)check_and_emit_line(line + ecbdata->nparents,
len - ecbdata->nparents, ecbdata->ws_rule,
- stdout, set, reset, ws);
+ ecbdata->file, set, reset, ws);
}
}
@@ -563,10 +570,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
- printf("%s--- %s%s%s\n",
- meta, ecbdata->label_path[0], reset, name_a_tab);
- printf("%s+++ %s%s%s\n",
- meta, ecbdata->label_path[1], reset, name_b_tab);
+ fprintf(ecbdata->file, "%s--- %s%s%s\n",
+ meta, ecbdata->label_path[0], reset, name_a_tab);
+ fprintf(ecbdata->file, "%s+++ %s%s%s\n",
+ meta, ecbdata->label_path[1], reset, name_b_tab);
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
@@ -578,15 +585,16 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
if (2 <= i && i < len && line[i] == ' ') {
ecbdata->nparents = i - 1;
len = sane_truncate_line(ecbdata, line, len);
- emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
+ emit_line(ecbdata->file,
+ diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
reset, line, len);
if (line[len-1] != '\n')
- putchar('\n');
+ putc('\n', ecbdata->file);
return;
}
if (len < ecbdata->nparents) {
- emit_line(reset, reset, line, len);
+ emit_line(ecbdata->file, reset, reset, line, len);
return;
}
@@ -609,7 +617,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
diff_words_show(ecbdata->diff_words);
line++;
len--;
- emit_line(plain, reset, line, len);
+ emit_line(ecbdata->file, plain, reset, line, len);
return;
}
for (i = 0; i < ecbdata->nparents && len; i++) {
@@ -620,7 +628,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
}
if (color != DIFF_FILE_NEW) {
- emit_line(diff_get_color(ecbdata->color_diff, color),
+ emit_line(ecbdata->file,
+ diff_get_color(ecbdata->color_diff, color),
reset, line, len);
return;
}
@@ -759,20 +768,21 @@ static int scale_linear(int it, int width, int max_change)
return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
}
-static void show_name(const char *prefix, const char *name, int len,
+static void show_name(FILE *file,
+ const char *prefix, const char *name, int len,
const char *reset, const char *set)
{
- printf(" %s%s%-*s%s |", set, prefix, len, name, reset);
+ fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset);
}
-static void show_graph(char ch, int cnt, const char *set, const char *reset)
+static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset)
{
if (cnt <= 0)
return;
- printf("%s", set);
+ fprintf(file, "%s", set);
while (cnt--)
- putchar(ch);
- printf("%s", reset);
+ putc(ch, file);
+ fprintf(file, "%s", reset);
}
static void fill_print_name(struct diffstat_file *file)
@@ -877,18 +887,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
}
if (data->files[i]->is_binary) {
- show_name(prefix, name, len, reset, set);
- printf(" Bin ");
- printf("%s%d%s", del_c, deleted, reset);
- printf(" -> ");
- printf("%s%d%s", add_c, added, reset);
- printf(" bytes");
- printf("\n");
+ show_name(options->file, prefix, name, len, reset, set);
+ fprintf(options->file, " Bin ");
+ fprintf(options->file, "%s%d%s", del_c, deleted, reset);
+ fprintf(options->file, " -> ");
+ fprintf(options->file, "%s%d%s", add_c, added, reset);
+ fprintf(options->file, " bytes");
+ fprintf(options->file, "\n");
continue;
}
else if (data->files[i]->is_unmerged) {
- show_name(prefix, name, len, reset, set);
- printf(" Unmerged\n");
+ show_name(options->file, prefix, name, len, reset, set);
+ fprintf(options->file, " Unmerged\n");
continue;
}
else if (!data->files[i]->is_renamed &&
@@ -911,17 +921,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
del = scale_linear(del, width, max_change);
total = add + del;
}
- show_name(prefix, name, len, reset, set);
- printf("%5d ", added + deleted);
- show_graph('+', add, add_c, reset);
- show_graph('-', del, del_c, reset);
- putchar('\n');
- }
- printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
+ show_name(options->file, prefix, name, len, reset, set);
+ fprintf(options->file, "%5d ", added + deleted);
+ show_graph(options->file, '+', add, add_c, reset);
+ show_graph(options->file, '-', del, del_c, reset);
+ fprintf(options->file, "\n");
+ }
+ fprintf(options->file,
+ "%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
set, total_files, adds, dels, reset);
}
-static void show_shortstats(struct diffstat_t* data)
+static void show_shortstats(struct diffstat_t* data, struct diff_options *options)
{
int i, adds = 0, dels = 0, total_files = data->nr;
@@ -942,7 +953,7 @@ static void show_shortstats(struct diffstat_t* data)
}
}
}
- printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+ fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
total_files, adds, dels);
}
@@ -957,24 +968,25 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
struct diffstat_file *file = data->files[i];
if (file->is_binary)
- printf("-\t-\t");
+ fprintf(options->file, "-\t-\t");
else
- printf("%d\t%d\t", file->added, file->deleted);
+ fprintf(options->file,
+ "%d\t%d\t", file->added, file->deleted);
if (options->line_termination) {
fill_print_name(file);
if (!file->is_renamed)
- write_name_quoted(file->name, stdout,
+ write_name_quoted(file->name, options->file,
options->line_termination);
else {
- fputs(file->print_name, stdout);
- putchar(options->line_termination);
+ fputs(file->print_name, options->file);
+ putc(options->line_termination, options->file);
}
} else {
if (file->is_renamed) {
- putchar('\0');
- write_name_quoted(file->from_name, stdout, '\0');
+ putc('\0', options->file);
+ write_name_quoted(file->from_name, options->file, '\0');
}
- write_name_quoted(file->name, stdout, '\0');
+ write_name_quoted(file->name, options->file, '\0');
}
}
}
@@ -984,7 +996,7 @@ struct diffstat_dir {
int nr, percent, cumulative;
};
-static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
+static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
{
unsigned long this_dir = 0;
unsigned int sources = 0;
@@ -1002,7 +1014,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons
slash = strchr(f->name + baselen, '/');
if (slash) {
int newbaselen = slash + 1 - f->name;
- this = gather_dirstat(dir, changed, f->name, newbaselen);
+ this = gather_dirstat(file, dir, changed, f->name, newbaselen);
sources++;
} else {
if (f->is_unmerged || f->is_binary)
@@ -1027,7 +1039,7 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons
if (permille) {
int percent = permille / 10;
if (percent >= dir->percent) {
- printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+ fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
if (!dir->cumulative)
return 0;
}
@@ -1060,7 +1072,7 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
dir.nr = data->nr;
dir.percent = options->dirstat_percent;
dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
- gather_dirstat(&dir, changed, "", 0);
+ gather_dirstat(options->file, &dir, changed, "", 0);
}
static void free_diffstat_info(struct diffstat_t *diffstat)
@@ -1083,6 +1095,7 @@ struct checkdiff_t {
int lineno, color_diff;
unsigned ws_rule;
unsigned status;
+ FILE *file;
};
static void checkdiff_consume(void *priv, char *line, unsigned long len)
@@ -1100,11 +1113,11 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
if (!data->status)
return;
err = whitespace_error_string(data->status);
- printf("%s:%d: %s.\n", data->filename, data->lineno, err);
+ fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err);
free(err);
- emit_line(set, reset, line, 1);
+ emit_line(data->file, set, reset, line, 1);
(void)check_and_emit_line(line + 1, len - 1, data->ws_rule,
- stdout, set, reset, ws);
+ data->file, set, reset, ws);
} else if (line[0] == ' ')
data->lineno++;
else if (line[0] == '@') {
@@ -1140,7 +1153,7 @@ static unsigned char *deflate_it(char *data,
return deflated;
}
-static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
{
void *cp;
void *delta;
@@ -1169,13 +1182,13 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
}
if (delta && delta_size < deflate_size) {
- printf("delta %lu\n", orig_size);
+ fprintf(file, "delta %lu\n", orig_size);
free(deflated);
data = delta;
data_size = delta_size;
}
else {
- printf("literal %lu\n", two->size);
+ fprintf(file, "literal %lu\n", two->size);
free(delta);
data = deflated;
data_size = deflate_size;
@@ -1193,17 +1206,18 @@ static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
line[0] = bytes - 26 + 'a' - 1;
encode_85(line + 1, cp, bytes);
cp = (char *) cp + bytes;
- puts(line);
+ fputs(line, file);
+ fputc('\n', file);
}
- printf("\n");
+ fprintf(file, "\n");
free(data);
}
-static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
{
- printf("GIT binary patch\n");
- emit_binary_diff_body(one, two);
- emit_binary_diff_body(two, one);
+ fprintf(file, "GIT binary patch\n");
+ emit_binary_diff_body(file, one, two);
+ emit_binary_diff_body(file, two, one);
}
static void setup_diff_attr_check(struct git_attr_check *check)
@@ -1334,25 +1348,25 @@ static void builtin_diff(const char *name_a,
b_two = quote_two(o->b_prefix, name_b + (*name_b == '/'));
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
- printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+ fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
if (lbl[0][0] == '/') {
/* /dev/null */
- printf("%snew file mode %06o%s\n", set, two->mode, reset);
+ fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset);
if (xfrm_msg && xfrm_msg[0])
- printf("%s%s%s\n", set, xfrm_msg, reset);
+ fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
}
else if (lbl[1][0] == '/') {
- printf("%sdeleted file mode %06o%s\n", set, one->mode, reset);
+ fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
if (xfrm_msg && xfrm_msg[0])
- printf("%s%s%s\n", set, xfrm_msg, reset);
+ fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
}
else {
if (one->mode != two->mode) {
- printf("%sold mode %06o%s\n", set, one->mode, reset);
- printf("%snew mode %06o%s\n", set, two->mode, reset);
+ fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset);
+ fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset);
}
if (xfrm_msg && xfrm_msg[0])
- printf("%s%s%s\n", set, xfrm_msg, reset);
+ fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
/*
* we do not run diff between different kind
* of objects.
@@ -1376,10 +1390,10 @@ static void builtin_diff(const char *name_a,
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
goto free_ab_and_return;
if (DIFF_OPT_TST(o, BINARY))
- emit_binary_diff(&mf1, &mf2);
+ emit_binary_diff(o->file, &mf1, &mf2);
else
- printf("Binary files %s and %s differ\n",
- lbl[0], lbl[1]);
+ fprintf(o->file, "Binary files %s and %s differ\n",
+ lbl[0], lbl[1]);
o->found_changes = 1;
}
else {
@@ -1401,6 +1415,7 @@ static void builtin_diff(const char *name_a,
ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+ ecbdata.file = o->file;
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1415,9 +1430,11 @@ static void builtin_diff(const char *name_a,
ecb.outf = xdiff_outf;
ecb.priv = &ecbdata;
ecbdata.xm.consume = fn_out_consume;
- if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
+ if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
+ ecbdata.diff_words->file = o->file;
+ }
xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
free_diff_words_data(&ecbdata);
@@ -1496,6 +1513,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
data.lineno = 0;
data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
data.ws_rule = whitespace_rule(attr_path);
+ data.file = o->file;
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
@@ -1966,7 +1984,7 @@ static void run_diff_cmd(const char *pgm,
builtin_diff(name, other ? other : name,
one, two, xfrm_msg, o, complete_rewrite);
else
- printf("* Unmerged path %s\n", name);
+ fprintf(o->file, "* Unmerged path %s\n", name);
}
static void diff_fill_sha1_info(struct diff_filespec *one)
@@ -2157,6 +2175,9 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
void diff_setup(struct diff_options *options)
{
memset(options, 0, sizeof(*options));
+
+ options->file = stdout;
+
options->line_termination = '\n';
options->break_opt = -1;
options->rename_limit = -1;
@@ -2470,7 +2491,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->b_prefix = arg + 13;
else if (!strcmp(arg, "--no-prefix"))
options->a_prefix = options->b_prefix = "";
- else
+ else if (!prefixcmp(arg, "--output=")) {
+ options->file = fopen(arg + strlen("--output="), "w");
+ options->close_file = 1;
+ } else
return 0;
return 1;
}
@@ -2599,15 +2623,15 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
int inter_name_termination = line_termination ? '\t' : '\0';
if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
- printf(":%06o %06o %s ", p->one->mode, p->two->mode,
- diff_unique_abbrev(p->one->sha1, opt->abbrev));
- printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
+ fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
+ diff_unique_abbrev(p->one->sha1, opt->abbrev));
+ fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
}
if (p->score) {
- printf("%c%03d%c", p->status, similarity_index(p),
- inter_name_termination);
+ fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
+ inter_name_termination);
} else {
- printf("%c%c", p->status, inter_name_termination);
+ fprintf(opt->file, "%c%c", p->status, inter_name_termination);
}
if (p->status == DIFF_STATUS_COPIED ||
@@ -2616,14 +2640,14 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
name_a = p->one->path;
name_b = p->two->path;
strip_prefix(opt->prefix_length, &name_a, &name_b);
- write_name_quoted(name_a, stdout, inter_name_termination);
- write_name_quoted(name_b, stdout, line_termination);
+ write_name_quoted(name_a, opt->file, inter_name_termination);
+ write_name_quoted(name_b, opt->file, line_termination);
} else {
const char *name_a, *name_b;
name_a = p->one->mode ? p->one->path : p->two->path;
name_b = NULL;
strip_prefix(opt->prefix_length, &name_a, &name_b);
- write_name_quoted(name_a, stdout, line_termination);
+ write_name_quoted(name_a, opt->file, line_termination);
}
}
@@ -2825,62 +2849,62 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
name_a = p->two->path;
name_b = NULL;
strip_prefix(opt->prefix_length, &name_a, &name_b);
- write_name_quoted(name_a, stdout, opt->line_termination);
+ write_name_quoted(name_a, opt->file, opt->line_termination);
}
}
-static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
+static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_filespec *fs)
{
if (fs->mode)
- printf(" %s mode %06o ", newdelete, fs->mode);
+ fprintf(file, " %s mode %06o ", newdelete, fs->mode);
else
- printf(" %s ", newdelete);
- write_name_quoted(fs->path, stdout, '\n');
+ fprintf(file, " %s ", newdelete);
+ write_name_quoted(fs->path, file, '\n');
}
-static void show_mode_change(struct diff_filepair *p, int show_name)
+static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name)
{
if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
- printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode,
+ fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode,
show_name ? ' ' : '\n');
if (show_name) {
- write_name_quoted(p->two->path, stdout, '\n');
+ write_name_quoted(p->two->path, file, '\n');
}
}
}
-static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
+static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p)
{
char *names = pprint_rename(p->one->path, p->two->path);
- printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
+ fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
free(names);
- show_mode_change(p, 0);
+ show_mode_change(file, p, 0);
}
-static void diff_summary(struct diff_filepair *p)
+static void diff_summary(FILE *file, struct diff_filepair *p)
{
switch(p->status) {
case DIFF_STATUS_DELETED:
- show_file_mode_name("delete", p->one);
+ show_file_mode_name(file, "delete", p->one);
break;
case DIFF_STATUS_ADDED:
- show_file_mode_name("create", p->two);
+ show_file_mode_name(file, "create", p->two);
break;
case DIFF_STATUS_COPIED:
- show_rename_copy("copy", p);
+ show_rename_copy(file, "copy", p);
break;
case DIFF_STATUS_RENAMED:
- show_rename_copy("rename", p);
+ show_rename_copy(file, "rename", p);
break;
default:
if (p->score) {
- fputs(" rewrite ", stdout);
- write_name_quoted(p->two->path, stdout, ' ');
- printf("(%d%%)\n", similarity_index(p));
+ fputs(" rewrite ", file);
+ write_name_quoted(p->two->path, file, ' ');
+ fprintf(file, "(%d%%)\n", similarity_index(p));
}
- show_mode_change(p, !p->score);
+ show_mode_change(file, p, !p->score);
break;
}
}
@@ -3088,14 +3112,14 @@ void diff_flush(struct diff_options *options)
if (output_format & DIFF_FORMAT_DIFFSTAT)
show_stats(&diffstat, options);
if (output_format & DIFF_FORMAT_SHORTSTAT)
- show_shortstats(&diffstat);
+ show_shortstats(&diffstat, options);
free_diffstat_info(&diffstat);
separator++;
}
if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
for (i = 0; i < q->nr; i++)
- diff_summary(q->queue[i]);
+ diff_summary(options->file, q->queue[i]);
separator++;
}
@@ -3103,9 +3127,9 @@ void diff_flush(struct diff_options *options)
if (separator) {
if (options->stat_sep) {
/* attach patch instead of inline */
- fputs(options->stat_sep, stdout);
+ fputs(options->stat_sep, options->file);
} else {
- putchar(options->line_termination);
+ putc(options->line_termination, options->file);
}
}
@@ -3125,6 +3149,8 @@ free_queue:
free(q->queue);
q->queue = NULL;
q->nr = q->alloc = 0;
+ if (options->close_file)
+ fclose(options->file);
}
static void diffcore_apply_filter(const char *filter)
diff --git a/diff.h b/diff.h
index 9a652e7f38..f2c77391a9 100644
--- a/diff.h
+++ b/diff.h
@@ -98,6 +98,9 @@ struct diff_options {
/* this is set by diffcore for DIFF_FORMAT_PATCH */
int found_changes;
+ FILE *file;
+ int close_file;
+
int nr_paths;
const char **paths;
int *pathlens;
diff --git a/fast-import.c b/fast-import.c
index 7f197d5e36..655913ddb2 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -2291,7 +2291,8 @@ static void cmd_reset_branch(void)
else
b = new_branch(sp);
read_next_command();
- if (!cmd_from(b) && command_buf.len > 0)
+ cmd_from(b);
+ if (command_buf.len > 0)
unread_command_buf = 1;
}
diff --git a/fetch-pack.h b/fetch-pack.h
index 8d35ef60bf..8bd9c32561 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -12,7 +12,8 @@ struct fetch_pack_args
use_thin_pack:1,
fetch_all:1,
verbose:1,
- no_progress:1;
+ no_progress:1,
+ include_tag:1;
};
struct ref *fetch_pack(struct fetch_pack_args *args,
diff --git a/fsck.c b/fsck.c
index 6883d1bd68..797e3178ae 100644
--- a/fsck.c
+++ b/fsck.c
@@ -155,8 +155,6 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
o_mode = 0;
o_name = NULL;
o_sha1 = NULL;
- if (!desc.size)
- return error_func(&item->object, FSCK_ERROR, "empty tree");
while (desc.size) {
unsigned mode;
diff --git a/git-am.sh b/git-am.sh
index a2c6fea47a..1f6b5e02c5 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -9,7 +9,7 @@ git-am [options] <mbox>|<Maildir>...
git-am [options] --resolved
git-am [options] --skip
--
-d,dotest= use <dir> and not .dotest
+d,dotest= (removed -- do not use)
i,interactive run interactively
b,binary pass --allo-binary-replacement to git-apply
3,3way allow fall back on 3way merging if needed
@@ -21,9 +21,11 @@ C= pass it through git-apply
p= pass it through git-apply
resolvemsg= override error message when patch failure occurs
r,resolved to be used after a patch failure
-skip skip the current patch"
+skip skip the current patch
+rebasing (internal use for git-rebase)"
. git-sh-setup
+prefix=$(git rev-parse --show-prefix)
set_reflog_action am
require_work_tree
cd_to_toplevel
@@ -49,10 +51,6 @@ stop_here_user_resolve () {
then
cmdline="$cmdline -3"
fi
- if test '.dotest' != "$dotest"
- then
- cmdline="$cmdline -d=$dotest"
- fi
echo "When you have resolved this problem run \"$cmdline --resolved\"."
echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
@@ -124,7 +122,8 @@ reread_subject () {
}
prec=4
-dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
+dotest=".dotest"
+sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
resolvemsg= resume=
git_apply_opt=
@@ -149,8 +148,11 @@ do
resolved=t ;;
--skip)
skip=t ;;
+ --rebasing)
+ rebasing=t threeway=t keep=t binary=t ;;
-d|--dotest)
- shift; dotest=$1;;
+ die "-d option is no longer supported. Do not use."
+ ;;
--resolvemsg)
shift; resolvemsg=$1 ;;
--whitespace)
@@ -186,7 +188,7 @@ then
0,)
# No file input but without resume parameters; catch
# user error to feed us a patch from standard input
- # when there is already .dotest. This is somewhat
+ # when there is already $dotest. This is somewhat
# unreliable -- stdin could be /dev/null for example
# and the caller did not intend to feed us a patch but
# wanted to continue unattended.
@@ -206,6 +208,24 @@ else
# Start afresh.
mkdir -p "$dotest" || exit
+ if test -n "$prefix" && test $# != 0
+ then
+ first=t
+ for arg
+ do
+ test -n "$first" && {
+ set x
+ first=
+ }
+ case "$arg" in
+ /*)
+ set "$@" "$arg" ;;
+ *)
+ set "$@" "$prefix$arg" ;;
+ esac
+ done
+ shift
+ fi
git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" || {
rm -fr "$dotest"
exit 1
@@ -220,6 +240,12 @@ else
echo "$utf8" >"$dotest/utf8"
echo "$keep" >"$dotest/keep"
echo 1 >"$dotest/next"
+ if test -n "$rebasing"
+ then
+ : >"$dotest/rebasing"
+ else
+ : >"$dotest/applying"
+ fi
fi
case "$resolved" in
diff --git a/git-compat-util.h b/git-compat-util.h
index 2a40703c85..a18235e6d0 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -68,6 +68,7 @@
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
+#include <utime.h>
#ifndef NO_SYS_SELECT_H
#include <sys/select.h>
#endif
@@ -209,6 +210,15 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
extern FILE *git_fopen(const char*, const char*);
#endif
+#ifdef SNPRINTF_RETURNS_BOGUS
+#define snprintf git_snprintf
+extern int git_snprintf(char *str, size_t maxsize,
+ const char *format, ...);
+#define vsnprintf git_vsnprintf
+extern int git_vsnprintf(char *str, size_t maxsize,
+ const char *format, va_list ap);
+#endif
+
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 1)
#define HAVE_STRCHRNUL
@@ -437,4 +447,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
#define qsort git_qsort
#endif
+#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
+# define FORCE_DIR_SET_GID S_ISGID
+#else
+# define FORCE_DIR_SET_GID 0
+#endif
+
#endif
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 47f116f37e..95c5eec51e 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -735,7 +735,7 @@ sub commit {
next unless $logmsg =~ $rx && $1;
my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
if (my $sha1 = get_headref("$remote/$mparent")) {
- push @commit_args, '-p', $mparent;
+ push @commit_args, '-p', "$remote/$mparent";
print "Merge parent branch: $mparent\n" if $opt_v;
}
}
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index afe3d0b7fe..7f632af20d 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -2556,7 +2556,7 @@ sub update
if ($base) {
my @merged;
# print "want to log between $base $parent \n";
- open(GITLOG, '-|', 'git-log', "$base..$parent")
+ open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
or die "Cannot call git-log: $!";
my $mergedhash;
while (<GITLOG>) {
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 49e13f0bb1..010353ad82 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -252,7 +252,16 @@ while read commit parents; do
git read-tree -i -m $commit
;;
*)
- git read-tree -i -m $commit:"$filter_subdir"
+ # The commit may not have the subdirectory at all
+ err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
+ if ! git rev-parse --verify $commit:"$filter_subdir" 2>/dev/null
+ then
+ rm -f "$GIT_INDEX_FILE"
+ else
+ echo >&2 "$err"
+ false
+ fi
+ }
esac || die "Could not initialize the index"
GIT_COMMIT=$commit
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 01e0a46ba5..b19fb2d64e 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -221,7 +221,7 @@ ifdef NO_MSGFMT
MSGFMT ?= $(TCL_PATH) po/po2msg.sh
else
MSGFMT ?= msgfmt
- ifeq ($(shell $(MSGFMT) >/dev/null 2>&1 || echo $$?),127)
+ ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
MSGFMT := $(TCL_PATH) po/po2msg.sh
endif
endif
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 238a2393ff..3a58cd2c6b 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -611,6 +611,7 @@ set default_config(gui.matchtrackingbranch) false
set default_config(gui.pruneduringfetch) false
set default_config(gui.trustmtime) false
set default_config(gui.diffcontext) 5
+set default_config(gui.commitmsgwidth) 75
set default_config(gui.newbranchtemplate) {}
set default_config(gui.spellingdictionary) {}
set default_config(gui.fontui) [font configure font_ui]
@@ -2289,8 +2290,9 @@ pack .vpane -anchor n -side top -fill both -expand 1
#
frame .vpane.files.index -height 100 -width 200
label .vpane.files.index.title -text [mc "Staged Changes (Will Commit)"] \
- -background lightgreen
-text $ui_index -background white -borderwidth 0 \
+ -background lightgreen -foreground black
+text $ui_index -background white -foreground black \
+ -borderwidth 0 \
-width 20 -height 10 \
-wrap none \
-cursor $cursor_ptr \
@@ -2308,8 +2310,9 @@ pack $ui_index -side left -fill both -expand 1
#
frame .vpane.files.workdir -height 100 -width 200
label .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
- -background lightsalmon
-text $ui_workdir -background white -borderwidth 0 \
+ -background lightsalmon -foreground black
+text $ui_workdir -background white -foreground black \
+ -borderwidth 0 \
-width 20 -height 10 \
-wrap none \
-cursor $cursor_ptr \
@@ -2416,12 +2419,13 @@ pack $ui_coml -side left -fill x
pack .vpane.lower.commarea.buffer.header.amend -side right
pack .vpane.lower.commarea.buffer.header.new -side right
-text $ui_comm -background white -borderwidth 1 \
+text $ui_comm -background white -foreground black \
+ -borderwidth 1 \
-undo true \
-maxundo 20 \
-autoseparators true \
-relief sunken \
- -width 75 -height 9 -wrap none \
+ -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
-font font_diff \
-yscrollcommand {.vpane.lower.commarea.buffer.sby set}
scrollbar .vpane.lower.commarea.buffer.sby \
@@ -2493,15 +2497,18 @@ trace add variable current_diff_path write trace_current_diff_path
frame .vpane.lower.diff.header -background gold
label .vpane.lower.diff.header.status \
-background gold \
+ -foreground black \
-width $max_status_desc \
-anchor w \
-justify left
label .vpane.lower.diff.header.file \
-background gold \
+ -foreground black \
-anchor w \
-justify left
label .vpane.lower.diff.header.path \
-background gold \
+ -foreground black \
-anchor w \
-justify left
pack .vpane.lower.diff.header.status -side left
@@ -2525,7 +2532,8 @@ bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
#
frame .vpane.lower.diff.body
set ui_diff .vpane.lower.diff.body.t
-text $ui_diff -background white -borderwidth 0 \
+text $ui_diff -background white -foreground black \
+ -borderwidth 0 \
-width 80 -height 15 -wrap none \
-font font_diff \
-xscrollcommand {.vpane.lower.diff.body.sbx set} \
diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl
index 00ecf21333..92fac1bad4 100644
--- a/git-gui/lib/blame.tcl
+++ b/git-gui/lib/blame.tcl
@@ -80,6 +80,7 @@ constructor new {i_commit i_path} {
label $w.header.commit_l \
-text [mc "Commit:"] \
-background gold \
+ -foreground black \
-anchor w \
-justify left
set w_back $w.header.commit_b
@@ -89,6 +90,7 @@ constructor new {i_commit i_path} {
-relief flat \
-state disabled \
-background gold \
+ -foreground black \
-activebackground gold
bind $w_back <Button-1> "
if {\[$w_back cget -state\] eq {normal}} {
@@ -98,16 +100,19 @@ constructor new {i_commit i_path} {
label $w.header.commit \
-textvariable @commit \
-background gold \
+ -foreground black \
-anchor w \
-justify left
label $w.header.path_l \
-text [mc "File:"] \
-background gold \
+ -foreground black \
-anchor w \
-justify left
set w_path $w.header.path
label $w_path \
-background gold \
+ -foreground black \
-anchor w \
-justify left
pack $w.header.commit_l -side left
@@ -135,7 +140,9 @@ constructor new {i_commit i_path} {
-takefocus 0 \
-highlightthickness 0 \
-padx 0 -pady 0 \
- -background white -borderwidth 0 \
+ -background white \
+ -foreground black \
+ -borderwidth 0 \
-state disabled \
-wrap none \
-height 40 \
@@ -148,7 +155,9 @@ constructor new {i_commit i_path} {
-takefocus 0 \
-highlightthickness 0 \
-padx 0 -pady 0 \
- -background white -borderwidth 0 \
+ -background white \
+ -foreground black \
+ -borderwidth 0 \
-state disabled \
-wrap none \
-height 40 \
@@ -166,7 +175,9 @@ constructor new {i_commit i_path} {
-takefocus 0 \
-highlightthickness 0 \
-padx 0 -pady 0 \
- -background white -borderwidth 0 \
+ -background white \
+ -foreground black \
+ -borderwidth 0 \
-state disabled \
-wrap none \
-height 40 \
@@ -184,7 +195,9 @@ constructor new {i_commit i_path} {
-takefocus 0 \
-highlightthickness 0 \
-padx 0 -pady 0 \
- -background white -borderwidth 0 \
+ -background white \
+ -foreground black \
+ -borderwidth 0 \
-state disabled \
-wrap none \
-height 40 \
@@ -213,7 +226,9 @@ constructor new {i_commit i_path} {
set w_cviewer $w.file_pane.cm.t
text $w_cviewer \
- -background white -borderwidth 0 \
+ -background white \
+ -foreground black \
+ -borderwidth 0 \
-state disabled \
-wrap none \
-height 10 \
diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl
index 53d5a62816..ab470d1264 100644
--- a/git-gui/lib/browser.tcl
+++ b/git-gui/lib/browser.tcl
@@ -39,7 +39,8 @@ constructor new {commit {path {}}} {
frame $w.list
set w_list $w.list.l
- text $w_list -background white -borderwidth 0 \
+ text $w_list -background white -foreground black \
+ -borderwidth 0 \
-cursor $cursor_ptr \
-state disabled \
-wrap none \
diff --git a/git-gui/lib/choose_font.tcl b/git-gui/lib/choose_font.tcl
index 0c4051b375..56443b042c 100644
--- a/git-gui/lib/choose_font.tcl
+++ b/git-gui/lib/choose_font.tcl
@@ -55,6 +55,7 @@ constructor pick {path title a_family a_size} {
set w_family $w.inner.family.v
text $w_family \
-background white \
+ -foreground black \
-borderwidth 1 \
-relief sunken \
-cursor $::cursor_ptr \
@@ -92,6 +93,7 @@ constructor pick {path title a_family a_size} {
set w_example $w.example.t
text $w_example \
-background white \
+ -foreground black \
-borderwidth 1 \
-relief sunken \
-height 3 \
diff --git a/git-gui/lib/console.tcl b/git-gui/lib/console.tcl
index 5597188d80..c112464ec3 100644
--- a/git-gui/lib/console.tcl
+++ b/git-gui/lib/console.tcl
@@ -46,7 +46,9 @@ method _init {} {
-justify left \
-font font_uibold
text $w_t \
- -background white -borderwidth 1 \
+ -background white \
+ -foreground black \
+ -borderwidth 1 \
-relief sunken \
-width 80 -height 10 \
-wrap none \
@@ -180,7 +182,8 @@ method done {ok} {
if {$ok} {
if {[winfo exists $w.m.s]} {
bind $w.m.s <Destroy> [list delete_this $this]
- $w.m.s conf -background green -text [mc "Success"]
+ $w.m.s conf -background green -foreground black \
+ -text [mc "Success"]
if {$is_toplevel} {
$w.ok conf -state normal
focus $w.ok
@@ -193,7 +196,8 @@ method done {ok} {
_init $this
}
bind $w.m.s <Destroy> [list delete_this $this]
- $w.m.s conf -background red -text [mc "Error: Command Failed"]
+ $w.m.s conf -background red -foreground black \
+ -text [mc "Error: Command Failed"]
if {$is_toplevel} {
$w.ok conf -state normal
focus $w.ok
diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl
index 8c27678e3a..75650157e5 100644
--- a/git-gui/lib/error.tcl
+++ b/git-gui/lib/error.tcl
@@ -80,7 +80,9 @@ proc hook_failed_popup {hook msg {is_fatal 1}} {
-justify left \
-font font_uibold
text $w.m.t \
- -background white -borderwidth 1 \
+ -background white \
+ -foreground black \
+ -borderwidth 1 \
-relief sunken \
-width 80 -height 10 \
-font font_diff \
diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl
index ea80df0092..9270512582 100644
--- a/git-gui/lib/option.tcl
+++ b/git-gui/lib/option.tcl
@@ -124,6 +124,7 @@ proc do_options {} {
{b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}}
{b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
{i-0..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
+ {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}}
{t gui.newbranchtemplate {mc "New Branch Name Template"}}
} {
set type [lindex $option 0]
diff --git a/git-gui/po/zh_cn.po b/git-gui/po/zh_cn.po
index 621c9479b2..f8697216f7 100644
--- a/git-gui/po/zh_cn.po
+++ b/git-gui/po/zh_cn.po
@@ -3,46 +3,63 @@
# This file is distributed under the same license as the git-gui package.
# Xudong Guan <xudong.guan@gmail.com>, 2007.
#
+# Please use the following translation throughout the file for consistence:
+#
+# repository 版本库
+# commit 提交
+# revision 版本
+# branch 分支
+# tag 标签
+# annotation 标注
+# merge 合并
+# fast forward 快速合并(??)
+# stage 缓存 (译自 index/cache)
+# amend 修正
+# reset 复位
+#
+# 2008-01-06 Eric Miao <eric.y.miao@gmail.com>
+# FIXME: checkout 的标准翻译
+#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: git-gui\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-10-10 04:04-0400\n"
+"POT-Creation-Date: 2007-11-24 10:36+0100\n"
"PO-Revision-Date: 2007-07-21 01:23-0700\n"
-"Last-Translator: Xudong Guan <xudong.guan@gmail.com>\n"
+"Last-Translator: Eric Miao <eric.y.miao@gmail.com>\n"
"Language-Team: Chinese\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
-#: git-gui.sh:763
+#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
+#: git-gui.sh:733
msgid "git-gui: fatal error"
-msgstr ""
+msgstr "git-gui: 致命错误"
-#: git-gui.sh:595
+#: git-gui.sh:565
#, tcl-format
msgid "Invalid font specified in %s:"
-msgstr ""
+msgstr "%s 中指定的字体无效:"
-#: git-gui.sh:620
+#: git-gui.sh:590
msgid "Main Font"
-msgstr ""
+msgstr "主要字体"
-#: git-gui.sh:621
+#: git-gui.sh:591
msgid "Diff/Console Font"
-msgstr ""
+msgstr "Diff/控制终端字体"
-#: git-gui.sh:635
+#: git-gui.sh:605
msgid "Cannot find git in PATH."
-msgstr ""
+msgstr "PATH 中没有找到 git"
-#: git-gui.sh:662
+#: git-gui.sh:632
msgid "Cannot parse Git version string:"
-msgstr ""
+msgstr "无法解析 Git 的版本信息:"
-#: git-gui.sh:680
+#: git-gui.sh:650
#, tcl-format
msgid ""
"Git version cannot be determined.\n"
@@ -53,388 +70,386 @@ msgid ""
"\n"
"Assume '%s' is version 1.5.0?\n"
msgstr ""
+"无法确定 Git 的版本.\n"
+"\n"
+"%s 声明其版本为 '%s'.\n"
+"\n"
+"而 %s 需要 1.5.0 或这以后的 Git 版本.\n"
+"\n"
+"是否假定 '%s' 为版本 1.5.0?\n"
-#: git-gui.sh:853
+#: git-gui.sh:888
msgid "Git directory not found:"
-msgstr ""
+msgstr "Git 目录无法找到:"
-#: git-gui.sh:860
+#: git-gui.sh:895
msgid "Cannot move to top of working directory:"
-msgstr ""
+msgstr "无法移动到工作根目录:"
-#: git-gui.sh:867
+#: git-gui.sh:902
msgid "Cannot use funny .git directory:"
-msgstr ""
+msgstr "无法使用 .git 目录:"
-#: git-gui.sh:872
+#: git-gui.sh:907
msgid "No working directory"
-msgstr ""
+msgstr "没有工作目录"
-#: git-gui.sh:1019
+#: git-gui.sh:1054
msgid "Refreshing file status..."
-msgstr ""
+msgstr "更新文件状态..."
-#: git-gui.sh:1084
+#: git-gui.sh:1119
msgid "Scanning for modified files ..."
-msgstr ""
+msgstr "扫描修改过的文件 ..."
-#: git-gui.sh:1259 lib/browser.tcl:245
-#, fuzzy
+#: git-gui.sh:1294 lib/browser.tcl:245
msgid "Ready."
-msgstr "重做"
+msgstr "就绪"
-#: git-gui.sh:1525
+#: git-gui.sh:1560
msgid "Unmodified"
-msgstr ""
+msgstr "未修改"
-#: git-gui.sh:1527
+#: git-gui.sh:1562
msgid "Modified, not staged"
-msgstr ""
+msgstr "修改但未缓存"
-#: git-gui.sh:1528 git-gui.sh:1533
-#, fuzzy
+#: git-gui.sh:1563 git-gui.sh:1568
msgid "Staged for commit"
-msgstr "从本次提交移除"
+msgstr "缓存为提交"
-#: git-gui.sh:1529 git-gui.sh:1534
-#, fuzzy
+#: git-gui.sh:1564 git-gui.sh:1569
msgid "Portions staged for commit"
-msgstr "从本次提交移除"
+msgstr "部分缓存为提交"
-#: git-gui.sh:1530 git-gui.sh:1535
+#: git-gui.sh:1565 git-gui.sh:1570
msgid "Staged for commit, missing"
-msgstr ""
+msgstr "缓存为提交, 不存在"
-#: git-gui.sh:1532
+#: git-gui.sh:1567
msgid "Untracked, not staged"
-msgstr ""
+msgstr "未跟踪, 未缓存"
-#: git-gui.sh:1537
+#: git-gui.sh:1572
msgid "Missing"
-msgstr ""
+msgstr "不存在"
-#: git-gui.sh:1538
+#: git-gui.sh:1573
msgid "Staged for removal"
-msgstr ""
+msgstr "缓存为删除"
-#: git-gui.sh:1539
+#: git-gui.sh:1574
msgid "Staged for removal, still present"
-msgstr ""
+msgstr "缓存为删除, 但仍存在"
-#: git-gui.sh:1541 git-gui.sh:1542 git-gui.sh:1543 git-gui.sh:1544
+#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
msgid "Requires merge resolution"
-msgstr ""
+msgstr "需要解决合并冲突"
-#: git-gui.sh:1579
+#: git-gui.sh:1614
msgid "Starting gitk... please wait..."
-msgstr ""
+msgstr "启动 gitk... 请等待..."
-#: git-gui.sh:1588
+#: git-gui.sh:1623
#, tcl-format
msgid ""
"Unable to start gitk:\n"
"\n"
"%s does not exist"
msgstr ""
+"无法启动 gitk:\n"
+"\n"
+"%s 不存在"
-#: git-gui.sh:1788 lib/choose_repository.tcl:32
+#: git-gui.sh:1823 lib/choose_repository.tcl:35
msgid "Repository"
-msgstr "版本树"
+msgstr "版本库(repository)"
-#: git-gui.sh:1789
+#: git-gui.sh:1824
msgid "Edit"
msgstr "编辑"
-#: git-gui.sh:1791 lib/choose_rev.tcl:560
+#: git-gui.sh:1826 lib/choose_rev.tcl:560
msgid "Branch"
-msgstr "分支"
+msgstr "分支(branch)"
-#: git-gui.sh:1794 lib/choose_rev.tcl:547
-#, fuzzy
+#: git-gui.sh:1829 lib/choose_rev.tcl:547
msgid "Commit@@noun"
-msgstr "提交"
+msgstr "提交(commit)"
-#: git-gui.sh:1797 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
msgid "Merge"
-msgstr "合并"
+msgstr "合并(merge)"
-#: git-gui.sh:1798 lib/choose_rev.tcl:556
-#, fuzzy
+#: git-gui.sh:1833 lib/choose_rev.tcl:556
msgid "Remote"
-msgstr "改名..."
+msgstr "远端(remote)"
-#: git-gui.sh:1807
+#: git-gui.sh:1842
msgid "Browse Current Branch's Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览当前分支上的文件"
-#: git-gui.sh:1811
-#, fuzzy
+#: git-gui.sh:1846
msgid "Browse Branch Files..."
-msgstr "浏览当前分支文件"
+msgstr "浏览分支上的文件..."
-#: git-gui.sh:1816
+#: git-gui.sh:1851
msgid "Visualize Current Branch's History"
-msgstr "调用gitk显示当前分支"
+msgstr "图示当前分支的历史"
-#: git-gui.sh:1820
+#: git-gui.sh:1855
msgid "Visualize All Branch History"
-msgstr "调用gitk显示所有分支"
+msgstr "图示所有分支的历史"
-#: git-gui.sh:1827
-#, fuzzy, tcl-format
+#: git-gui.sh:1862
+#, tcl-format
msgid "Browse %s's Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览 %s 上的文件"
-#: git-gui.sh:1829
-#, fuzzy, tcl-format
+#: git-gui.sh:1864
+#, tcl-format
msgid "Visualize %s's History"
-msgstr "调用gitk显示所有分支"
+msgstr "图示 %s 分支的历史"
-#: git-gui.sh:1834 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
msgid "Database Statistics"
-msgstr "数据库统计数据"
+msgstr "数据库统计信息"
-#: git-gui.sh:1837 lib/database.tcl:34
+#: git-gui.sh:1872 lib/database.tcl:34
msgid "Compress Database"
msgstr "压缩数据库"
-#: git-gui.sh:1840
+#: git-gui.sh:1875
msgid "Verify Database"
msgstr "验证数据库"
-#: git-gui.sh:1847 git-gui.sh:1851 git-gui.sh:1855 lib/shortcut.tcl:9
-#: lib/shortcut.tcl:45 lib/shortcut.tcl:84
+#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
msgid "Create Desktop Icon"
msgstr "创建桌面图标"
-#: git-gui.sh:1860 lib/choose_repository.tcl:36 lib/choose_repository.tcl:95
+#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
msgid "Quit"
msgstr "退出"
-#: git-gui.sh:1867
+#: git-gui.sh:1902
msgid "Undo"
msgstr "撤销"
-#: git-gui.sh:1870
+#: git-gui.sh:1905
msgid "Redo"
msgstr "重做"
-#: git-gui.sh:1874 git-gui.sh:2366
+#: git-gui.sh:1909 git-gui.sh:2403
msgid "Cut"
msgstr "剪切"
-#: git-gui.sh:1877 git-gui.sh:2369 git-gui.sh:2440 git-gui.sh:2512
+#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
#: lib/console.tcl:67
msgid "Copy"
msgstr "复制"
-#: git-gui.sh:1880 git-gui.sh:2372
+#: git-gui.sh:1915 git-gui.sh:2409
msgid "Paste"
msgstr "粘贴"
-#: git-gui.sh:1883 git-gui.sh:2375 lib/branch_delete.tcl:26
+#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
#: lib/remote_branch_delete.tcl:38
msgid "Delete"
msgstr "删除"
-#: git-gui.sh:1887 git-gui.sh:2379 git-gui.sh:2516 lib/console.tcl:69
+#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
msgid "Select All"
msgstr "全选"
-#: git-gui.sh:1896
+#: git-gui.sh:1931
msgid "Create..."
msgstr "新建..."
-#: git-gui.sh:1902
+#: git-gui.sh:1937
msgid "Checkout..."
-msgstr "切换..."
+msgstr "Checkout..."
-#: git-gui.sh:1908
+#: git-gui.sh:1943
msgid "Rename..."
-msgstr "改名..."
+msgstr "更名..."
-#: git-gui.sh:1913 git-gui.sh:2012
+#: git-gui.sh:1948 git-gui.sh:2048
msgid "Delete..."
msgstr "删除..."
-#: git-gui.sh:1918
+#: git-gui.sh:1953
msgid "Reset..."
-msgstr "重置所有修动..."
+msgstr "复位(Reset)..."
-#: git-gui.sh:1930 git-gui.sh:2313
+#: git-gui.sh:1965 git-gui.sh:2350
msgid "New Commit"
-msgstr "新提交"
+msgstr "新建提交"
-#: git-gui.sh:1938 git-gui.sh:2320
+#: git-gui.sh:1973 git-gui.sh:2357
msgid "Amend Last Commit"
-msgstr "修订上次提交"
+msgstr "修正上次提交"
-#: git-gui.sh:1947 git-gui.sh:2280 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
msgid "Rescan"
msgstr "重新扫描"
-#: git-gui.sh:1953
-#, fuzzy
+#: git-gui.sh:1988
msgid "Stage To Commit"
-msgstr "从本次提交移除"
+msgstr "缓存为提交"
-#: git-gui.sh:1958
-#, fuzzy
+#: git-gui.sh:1994
msgid "Stage Changed Files To Commit"
-msgstr "将被提交的修改"
+msgstr "缓存修改的文件为提交"
-#: git-gui.sh:1964
+#: git-gui.sh:2000
msgid "Unstage From Commit"
-msgstr "从本次提交移除"
+msgstr "从本次提交撤除"
-#: git-gui.sh:1969 lib/index.tcl:352
+#: git-gui.sh:2005 lib/index.tcl:393
msgid "Revert Changes"
-msgstr "恢复修改"
+msgstr "撤销修改"
-#: git-gui.sh:1976 git-gui.sh:2292 git-gui.sh:2390
+#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
msgid "Sign Off"
-msgstr "签名"
+msgstr "签名(Sign Off)"
-#: git-gui.sh:1980 git-gui.sh:2296
-#, fuzzy
+#: git-gui.sh:2016 git-gui.sh:2333
msgid "Commit@@verb"
msgstr "提交"
-#: git-gui.sh:1991
+#: git-gui.sh:2027
msgid "Local Merge..."
msgstr "本地合并..."
-#: git-gui.sh:1996
+#: git-gui.sh:2032
msgid "Abort Merge..."
-msgstr "取消合并..."
+msgstr "中止合并..."
-#: git-gui.sh:2008
+#: git-gui.sh:2044
msgid "Push..."
msgstr "上传..."
-#: git-gui.sh:2019 lib/choose_repository.tcl:41
+#: git-gui.sh:2055 lib/choose_repository.tcl:40
msgid "Apple"
msgstr "苹果"
-#: git-gui.sh:2022 git-gui.sh:2044 lib/about.tcl:13
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
+#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
#, tcl-format
msgid "About %s"
-msgstr "关于%s"
+msgstr "关于 %s"
-#: git-gui.sh:2026
+#: git-gui.sh:2062
msgid "Preferences..."
-msgstr ""
+msgstr "首选项..."
-#: git-gui.sh:2034 git-gui.sh:2558
+#: git-gui.sh:2070 git-gui.sh:2595
msgid "Options..."
msgstr "选项..."
-#: git-gui.sh:2040 lib/choose_repository.tcl:47
+#: git-gui.sh:2076 lib/choose_repository.tcl:46
msgid "Help"
msgstr "帮助"
-#: git-gui.sh:2081
+#: git-gui.sh:2117
msgid "Online Documentation"
msgstr "在线文档"
-#: git-gui.sh:2165
+#: git-gui.sh:2201
#, tcl-format
msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr ""
+msgstr "致命错误: 无法获取路径 %s 的信息: 该文件或目录不存在"
-#: git-gui.sh:2198
+#: git-gui.sh:2234
msgid "Current Branch:"
msgstr "当前分支:"
-#: git-gui.sh:2219
-#, fuzzy
+#: git-gui.sh:2255
msgid "Staged Changes (Will Commit)"
-msgstr "将被提交的修改"
+msgstr "已缓存的改动 (将被提交)"
-#: git-gui.sh:2239
+#: git-gui.sh:2274
msgid "Unstaged Changes"
-msgstr ""
+msgstr "未缓存的改动"
-#: git-gui.sh:2286
+#: git-gui.sh:2323
msgid "Stage Changed"
-msgstr ""
+msgstr "缓存改动"
-#: git-gui.sh:2302 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
msgid "Push"
msgstr "上传"
-#: git-gui.sh:2332
+#: git-gui.sh:2369
msgid "Initial Commit Message:"
-msgstr "初始提交描述:"
+msgstr "初始的提交描述:"
-#: git-gui.sh:2333
+#: git-gui.sh:2370
msgid "Amended Commit Message:"
-msgstr "修订提交描述:"
+msgstr "修正的提交描述:"
-#: git-gui.sh:2334
+#: git-gui.sh:2371
msgid "Amended Initial Commit Message:"
-msgstr "修订初始提交描述:"
+msgstr "修正的初始提交描述:"
-#: git-gui.sh:2335
+#: git-gui.sh:2372
msgid "Amended Merge Commit Message:"
-msgstr "修订合并提交描述:"
+msgstr "修正的合并提交描述:"
-#: git-gui.sh:2336
+#: git-gui.sh:2373
msgid "Merge Commit Message:"
msgstr "合并提交描述:"
-#: git-gui.sh:2337
+#: git-gui.sh:2374
msgid "Commit Message:"
msgstr "提交描述:"
-#: git-gui.sh:2382 git-gui.sh:2520 lib/console.tcl:71
+#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
msgid "Copy All"
msgstr "全部复制"
-#: git-gui.sh:2406 lib/blame.tcl:104
+#: git-gui.sh:2443 lib/blame.tcl:104
msgid "File:"
-msgstr ""
+msgstr "文件:"
-#: git-gui.sh:2508
+#: git-gui.sh:2545
msgid "Refresh"
msgstr "刷新"
-#: git-gui.sh:2529
+#: git-gui.sh:2566
msgid "Apply/Reverse Hunk"
msgstr "应用/撤消此修改块"
-#: git-gui.sh:2535
+#: git-gui.sh:2572
msgid "Decrease Font Size"
msgstr "缩小字体"
-#: git-gui.sh:2539
+#: git-gui.sh:2576
msgid "Increase Font Size"
msgstr "放大字体"
-#: git-gui.sh:2544
+#: git-gui.sh:2581
msgid "Show Less Context"
-msgstr "显示更多diff上下文"
+msgstr "显示更少上下文"
-#: git-gui.sh:2551
+#: git-gui.sh:2588
msgid "Show More Context"
-msgstr "显示更少diff上下文"
+msgstr "显示更多上下文"
-#: git-gui.sh:2565
-#, fuzzy
+#: git-gui.sh:2602
msgid "Unstage Hunk From Commit"
-msgstr "从本次提交移除"
+msgstr "从提交中撤除修改块"
-#: git-gui.sh:2567
-#, fuzzy
+#: git-gui.sh:2604
msgid "Stage Hunk For Commit"
-msgstr "从本次提交移除"
+msgstr "缓存修改块为提交"
-#: git-gui.sh:2586
+#: git-gui.sh:2623
msgid "Initializing..."
-msgstr ""
+msgstr "初始化..."
-#: git-gui.sh:2677
+#: git-gui.sh:2718
#, tcl-format
msgid ""
"Possible environment issues exist.\n"
@@ -444,15 +459,22 @@ msgid ""
"by %s:\n"
"\n"
msgstr ""
+"可能存在环境变量的问题.\n"
+"\n"
+"由 %s 执行的 Git 子进程可能忽略下列环境变量:\n"
+"\n"
-#: git-gui.sh:2707
+#: git-gui.sh:2748
msgid ""
"\n"
"This is due to a known issue with the\n"
"Tcl binary distributed by Cygwin."
msgstr ""
+"\n"
+"这是由 Cygwin 发布的 Tcl 代码中一个\n"
+"已知问题所引起."
-#: git-gui.sh:2712
+#: git-gui.sh:2753
#, tcl-format
msgid ""
"\n"
@@ -462,206 +484,197 @@ msgid ""
"user.email settings into your personal\n"
"~/.gitconfig file.\n"
msgstr ""
+"\n"
+"\n"
+"%s 的一个很好的替代方案是将 user.name 以及\n"
+"user.email 设置放在你的个人 ~/.gitconfig 文件中.\n"
#: lib/about.tcl:25
msgid "git-gui - a graphical user interface for Git."
-msgstr ""
+msgstr "git-gui - Git 的图形化用户界面"
#: lib/blame.tcl:77
msgid "File Viewer"
-msgstr ""
+msgstr "文件查看器"
#: lib/blame.tcl:81
-#, fuzzy
msgid "Commit:"
-msgstr "提交"
+msgstr "提交:"
#: lib/blame.tcl:249
-#, fuzzy
msgid "Copy Commit"
-msgstr "提交"
+msgstr "复制提交"
#: lib/blame.tcl:369
#, tcl-format
msgid "Reading %s..."
-msgstr ""
+msgstr "读取 %s..."
#: lib/blame.tcl:473
msgid "Loading copy/move tracking annotations..."
-msgstr ""
+msgstr "装载复制/移动跟踪标注..."
#: lib/blame.tcl:493
msgid "lines annotated"
-msgstr ""
+msgstr "标注行"
#: lib/blame.tcl:674
msgid "Loading original location annotations..."
-msgstr ""
+msgstr "装载原始位置标注..."
#: lib/blame.tcl:677
msgid "Annotation complete."
-msgstr ""
+msgstr "标注完成."
#: lib/blame.tcl:731
msgid "Loading annotation..."
-msgstr ""
+msgstr "裝載标注..."
#: lib/blame.tcl:787
msgid "Author:"
-msgstr ""
+msgstr "作者:"
#: lib/blame.tcl:791
-#, fuzzy
msgid "Committer:"
-msgstr "提交"
+msgstr "提交者:"
#: lib/blame.tcl:796
msgid "Original File:"
-msgstr ""
+msgstr "原始文件:"
#: lib/blame.tcl:910
msgid "Originally By:"
-msgstr ""
+msgstr "最初由:"
#: lib/blame.tcl:916
msgid "In File:"
-msgstr ""
+msgstr "在文件:"
#: lib/blame.tcl:921
msgid "Copied Or Moved Here By:"
-msgstr ""
+msgstr "由复制或移动至此:"
#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
-#, fuzzy
msgid "Checkout Branch"
-msgstr "当前分支:"
+msgstr "Checkout 分支"
#: lib/branch_checkout.tcl:23
-#, fuzzy
msgid "Checkout"
-msgstr "切换..."
+msgstr "Checkout"
#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
msgid "Cancel"
-msgstr ""
+msgstr "取消"
#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
msgid "Revision"
-msgstr ""
+msgstr "版本"
#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
-#, fuzzy
msgid "Options"
msgstr "选项..."
#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
msgid "Fetch Tracking Branch"
-msgstr ""
+msgstr "获取跟踪分支"
#: lib/branch_checkout.tcl:44
msgid "Detach From Local Branch"
-msgstr ""
+msgstr "从本地分支脱离"
#: lib/branch_create.tcl:22
-#, fuzzy
msgid "Create Branch"
-msgstr "当前分支:"
+msgstr "创建分支"
#: lib/branch_create.tcl:27
-#, fuzzy
msgid "Create New Branch"
-msgstr "当前分支:"
+msgstr "新建分支"
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:199
-#, fuzzy
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
msgid "Create"
-msgstr "新建..."
+msgstr "新建"
#: lib/branch_create.tcl:40
-#, fuzzy
msgid "Branch Name"
-msgstr "分支"
+msgstr "分支名"
#: lib/branch_create.tcl:43
msgid "Name:"
-msgstr ""
+msgstr "名字:"
#: lib/branch_create.tcl:58
msgid "Match Tracking Branch Name"
-msgstr ""
+msgstr "匹配跟踪分支名字"
#: lib/branch_create.tcl:66
msgid "Starting Revision"
-msgstr ""
+msgstr "起始版本"
#: lib/branch_create.tcl:72
msgid "Update Existing Branch:"
-msgstr ""
+msgstr "更新已有分支:"
#: lib/branch_create.tcl:75
msgid "No"
-msgstr ""
+msgstr "号码"
#: lib/branch_create.tcl:80
msgid "Fast Forward Only"
-msgstr ""
+msgstr "仅快速合并"
#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514
-#, fuzzy
msgid "Reset"
-msgstr "重置所有修动..."
+msgstr "复位"
#: lib/branch_create.tcl:97
msgid "Checkout After Creation"
-msgstr ""
+msgstr "在创建后Checkout"
#: lib/branch_create.tcl:131
msgid "Please select a tracking branch."
-msgstr ""
+msgstr "请选择某个跟踪分支."
#: lib/branch_create.tcl:140
#, tcl-format
msgid "Tracking branch %s is not a branch in the remote repository."
-msgstr ""
+msgstr "跟踪分支 %s 并不是远端版本库中的一个分支"
#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
msgid "Please supply a branch name."
-msgstr ""
+msgstr "请提供分支名字."
#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
#, tcl-format
msgid "'%s' is not an acceptable branch name."
-msgstr ""
+msgstr "'%s'不是一个可接受的分支名."
#: lib/branch_delete.tcl:15
-#, fuzzy
msgid "Delete Branch"
-msgstr "当前分支:"
+msgstr "删除分支"
#: lib/branch_delete.tcl:20
msgid "Delete Local Branch"
-msgstr ""
+msgstr "删除本地分支"
#: lib/branch_delete.tcl:37
-#, fuzzy
msgid "Local Branches"
-msgstr "分支"
+msgstr "本地分支"
#: lib/branch_delete.tcl:52
msgid "Delete Only If Merged Into"
-msgstr ""
+msgstr "仅在合并后删除"
#: lib/branch_delete.tcl:54
msgid "Always (Do not perform merge test.)"
-msgstr ""
+msgstr "总是合并 (不作合并测试.)"
#: lib/branch_delete.tcl:103
#, tcl-format
msgid "The following branches are not completely merged into %s:"
-msgstr ""
+msgstr "下列分支没有完全被合并到 %s:"
#: lib/branch_delete.tcl:115
msgid ""
@@ -669,6 +682,9 @@ msgid ""
"\n"
" Delete the selected branches?"
msgstr ""
+"恢复被删除的分支非常困难.\n"
+"\n"
+"是否要删除所选分支?"
#: lib/branch_delete.tcl:141
#, tcl-format
@@ -676,86 +692,84 @@ msgid ""
"Failed to delete branches:\n"
"%s"
msgstr ""
+"无法删除分支:\n"
+"%s"
#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
-#, fuzzy
msgid "Rename Branch"
-msgstr "当前分支:"
+msgstr "更改分支名:"
#: lib/branch_rename.tcl:26
-#, fuzzy
msgid "Rename"
-msgstr "改名..."
+msgstr "更名..."
#: lib/branch_rename.tcl:36
-#, fuzzy
msgid "Branch:"
-msgstr "分支"
+msgstr "分支:"
#: lib/branch_rename.tcl:39
msgid "New Name:"
-msgstr ""
+msgstr "新名字:"
#: lib/branch_rename.tcl:75
msgid "Please select a branch to rename."
-msgstr ""
+msgstr "请选择分支更名."
#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179
#, tcl-format
msgid "Branch '%s' already exists."
-msgstr ""
+msgstr "分支 '%s' 已经存在."
#: lib/branch_rename.tcl:117
#, tcl-format
msgid "Failed to rename '%s'."
-msgstr ""
+msgstr "无法更名 '%s'."
#: lib/browser.tcl:17
msgid "Starting..."
-msgstr ""
+msgstr "开始..."
#: lib/browser.tcl:26
msgid "File Browser"
-msgstr ""
+msgstr "文件浏览器"
#: lib/browser.tcl:125 lib/browser.tcl:142
#, tcl-format
msgid "Loading %s..."
-msgstr ""
+msgstr "装载 %s..."
#: lib/browser.tcl:186
msgid "[Up To Parent]"
-msgstr ""
+msgstr "[上层目录]"
#: lib/browser.tcl:266 lib/browser.tcl:272
-#, fuzzy
msgid "Browse Branch Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览分支文件"
-#: lib/browser.tcl:277 lib/choose_repository.tcl:215
-#: lib/choose_repository.tcl:305 lib/choose_repository.tcl:315
-#: lib/choose_repository.tcl:811
+#: lib/browser.tcl:277 lib/choose_repository.tcl:391
+#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
+#: lib/choose_repository.tcl:989
msgid "Browse"
-msgstr ""
+msgstr "浏览"
#: lib/checkout_op.tcl:79
#, tcl-format
msgid "Fetching %s from %s"
-msgstr ""
+msgstr "获取 %s 自 %s"
#: lib/checkout_op.tcl:127
#, tcl-format
msgid "fatal: Cannot resolve %s"
-msgstr ""
+msgstr "致命错误: 无法解决 %s"
#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
msgid "Close"
-msgstr ""
+msgstr "关闭"
#: lib/checkout_op.tcl:169
#, tcl-format
msgid "Branch '%s' does not exist."
-msgstr ""
+msgstr "分支 '%s' 并不存在."
#: lib/checkout_op.tcl:206
#, tcl-format
@@ -765,20 +779,24 @@ msgid ""
"It cannot fast-forward to %s.\n"
"A merge is required."
msgstr ""
+"分支 '%s' 已经存在.\n"
+"\n"
+"无法快速合并到 %s.\n"
+"需要普通合并."
#: lib/checkout_op.tcl:220
#, tcl-format
msgid "Merge strategy '%s' not supported."
-msgstr ""
+msgstr "合并策略 '%s' 不支持."
#: lib/checkout_op.tcl:239
#, tcl-format
msgid "Failed to update '%s'."
-msgstr ""
+msgstr "无法更新 '%s'."
#: lib/checkout_op.tcl:251
msgid "Staging area (index) is already locked."
-msgstr ""
+msgstr "缓存区域 (index) 已被锁定."
#: lib/checkout_op.tcl:266
msgid ""
@@ -789,25 +807,31 @@ msgid ""
"\n"
"The rescan will be automatically started now.\n"
msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
#: lib/checkout_op.tcl:322
#, tcl-format
msgid "Updating working directory to '%s'..."
-msgstr ""
+msgstr "更新工作目录到 '%s'..."
#: lib/checkout_op.tcl:353
#, tcl-format
msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr ""
+msgstr "中止 '%s' 的 checkout 操作 (需要做文件级合并)."
#: lib/checkout_op.tcl:354
msgid "File level merge required."
-msgstr ""
+msgstr "需要文件级合并."
#: lib/checkout_op.tcl:358
#, tcl-format
msgid "Staying on branch '%s'."
-msgstr ""
+msgstr "停留在分支 '%s'."
#: lib/checkout_op.tcl:429
msgid ""
@@ -816,29 +840,32 @@ msgid ""
"If you wanted to be on a branch, create one now starting from 'This Detached "
"Checkout'."
msgstr ""
+"你不在某个本地分支上.\n"
+"\n"
+"如果你想位于某分支上, 从当前脱节的Checkout中创建一个新分支."
#: lib/checkout_op.tcl:446
-#, fuzzy, tcl-format
+#, tcl-format
msgid "Checked out '%s'."
-msgstr "切换..."
+msgstr "'%s' 已被 checkout"
#: lib/checkout_op.tcl:478
#, tcl-format
msgid "Resetting '%s' to '%s' will lose the following commits:"
-msgstr ""
+msgstr "复位 '%s' 到 '%s' 将导致下列提交的丢失:"
#: lib/checkout_op.tcl:500
msgid "Recovering lost commits may not be easy."
-msgstr ""
+msgstr "恢复丢失的提交是比较困难的."
#: lib/checkout_op.tcl:505
#, tcl-format
msgid "Reset '%s'?"
-msgstr ""
+msgstr "复位 '%s'?"
#: lib/checkout_op.tcl:510 lib/merge.tcl:164
msgid "Visualize"
-msgstr ""
+msgstr "图示"
#: lib/checkout_op.tcl:578
#, tcl-format
@@ -850,286 +877,301 @@ msgid ""
"\n"
"This should not have occurred. %s will now close and give up."
msgstr ""
+"无法设定当前分支.\n"
+"\n"
+"当前工作目录仅有部分被切换出, 我们已成功的更新了您的文件但是无法更新某个内部"
+"的Git文件.\n"
+"\n"
+"这本不该发生, %s 将关闭并放弃."
#: lib/choose_font.tcl:39
-#, fuzzy
msgid "Select"
-msgstr "全选"
+msgstr "选择"
#: lib/choose_font.tcl:53
msgid "Font Family"
-msgstr ""
+msgstr "字体族"
#: lib/choose_font.tcl:73
-#, fuzzy
msgid "Font Size"
-msgstr "缩小字体"
+msgstr "字体大小"
#: lib/choose_font.tcl:90
msgid "Font Example"
-msgstr ""
+msgstr "字体样例"
#: lib/choose_font.tcl:101
msgid ""
"This is example text.\n"
"If you like this text, it can be your font."
msgstr ""
+"这是样例文本.\n"
+"如果你喜欢, 你可以设置该字体."
-#: lib/choose_repository.tcl:25
+#: lib/choose_repository.tcl:27
msgid "Git Gui"
-msgstr ""
+msgstr "Git Gui"
-#: lib/choose_repository.tcl:69 lib/choose_repository.tcl:204
-#, fuzzy
+#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
msgid "Create New Repository"
-msgstr "版本树"
+msgstr "创建新的版本库"
-#: lib/choose_repository.tcl:74 lib/choose_repository.tcl:291
-#, fuzzy
+#: lib/choose_repository.tcl:86
+msgid "New..."
+msgstr "新建..."
+
+#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
msgid "Clone Existing Repository"
-msgstr "版本树"
+msgstr "克隆已有版本库"
-#: lib/choose_repository.tcl:79 lib/choose_repository.tcl:800
-#, fuzzy
+#: lib/choose_repository.tcl:99
+msgid "Clone..."
+msgstr "克隆..."
+
+#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
msgid "Open Existing Repository"
-msgstr "版本树"
+msgstr "打开已有版本库"
-#: lib/choose_repository.tcl:91
-msgid "Next >"
-msgstr ""
+#: lib/choose_repository.tcl:112
+msgid "Open..."
+msgstr "打开..."
-#: lib/choose_repository.tcl:152
+#: lib/choose_repository.tcl:125
+msgid "Recent Repositories"
+msgstr "最近版本库"
+
+#: lib/choose_repository.tcl:131
+msgid "Open Recent Repository:"
+msgstr "打开最近版本库"
+
+#: lib/choose_repository.tcl:294
#, tcl-format
msgid "Location %s already exists."
-msgstr ""
+msgstr "位置 %s 已经存在."
-#: lib/choose_repository.tcl:158 lib/choose_repository.tcl:165
-#: lib/choose_repository.tcl:172
+#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
+#: lib/choose_repository.tcl:314
#, tcl-format
msgid "Failed to create repository %s:"
-msgstr ""
+msgstr "无法创建版本库 %s:"
-#: lib/choose_repository.tcl:209 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
msgid "Directory:"
-msgstr ""
+msgstr "目录:"
-#: lib/choose_repository.tcl:238 lib/choose_repository.tcl:363
-#: lib/choose_repository.tcl:834
-#, fuzzy
+#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1013
msgid "Git Repository"
-msgstr "版本树"
+msgstr "Git 版本库"
-#: lib/choose_repository.tcl:253 lib/choose_repository.tcl:260
+#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
#, tcl-format
msgid "Directory %s already exists."
-msgstr ""
+msgstr "目录 %s 已经存在."
-#: lib/choose_repository.tcl:265
+#: lib/choose_repository.tcl:442
#, tcl-format
msgid "File %s already exists."
-msgstr ""
+msgstr "文件 %s 已经存在."
-#: lib/choose_repository.tcl:286
+#: lib/choose_repository.tcl:463
msgid "Clone"
-msgstr ""
+msgstr "克隆"
-#: lib/choose_repository.tcl:299
+#: lib/choose_repository.tcl:476
msgid "URL:"
-msgstr ""
+msgstr "URL:"
-#: lib/choose_repository.tcl:319
+#: lib/choose_repository.tcl:496
msgid "Clone Type:"
-msgstr ""
+msgstr "克隆类型:"
-#: lib/choose_repository.tcl:325
+#: lib/choose_repository.tcl:502
msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
-msgstr ""
+msgstr "标准方式 (快速, 部分备份, 作硬连接)"
-#: lib/choose_repository.tcl:331
+#: lib/choose_repository.tcl:508
msgid "Full Copy (Slower, Redundant Backup)"
-msgstr ""
+msgstr "全部复制 (较慢, 做备份)"
-#: lib/choose_repository.tcl:337
+#: lib/choose_repository.tcl:514
msgid "Shared (Fastest, Not Recommended, No Backup)"
-msgstr ""
+msgstr "共享方式 (最快, 不推荐, 不做备份)"
-#: lib/choose_repository.tcl:369 lib/choose_repository.tcl:418
-#: lib/choose_repository.tcl:560 lib/choose_repository.tcl:630
-#: lib/choose_repository.tcl:840 lib/choose_repository.tcl:848
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
+#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
#, tcl-format
msgid "Not a Git repository: %s"
-msgstr ""
+msgstr "不是一个 Git 版本库: %s"
-#: lib/choose_repository.tcl:405
+#: lib/choose_repository.tcl:586
msgid "Standard only available for local repository."
-msgstr ""
+msgstr "标准方式仅当是本地版本库时有效."
-#: lib/choose_repository.tcl:409
+#: lib/choose_repository.tcl:590
msgid "Shared only available for local repository."
-msgstr ""
+msgstr "共享方式仅当是本地版本库时有效."
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:617
msgid "Failed to configure origin"
-msgstr ""
+msgstr "无法配置 origin"
-#: lib/choose_repository.tcl:451
+#: lib/choose_repository.tcl:629
msgid "Counting objects"
-msgstr ""
+msgstr "清点对象"
-#: lib/choose_repository.tcl:452
+#: lib/choose_repository.tcl:630
+#, fuzzy
msgid "buckets"
-msgstr ""
+msgstr "水桶??"
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:654
#, tcl-format
msgid "Unable to copy objects/info/alternates: %s"
-msgstr ""
+msgstr "无法复制 objects/info/alternates: %s"
-#: lib/choose_repository.tcl:512
+#: lib/choose_repository.tcl:690
#, tcl-format
msgid "Nothing to clone from %s."
-msgstr ""
+msgstr "没有东西可从 %s 克隆."
-#: lib/choose_repository.tcl:514 lib/choose_repository.tcl:728
-#: lib/choose_repository.tcl:740
+#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
+#: lib/choose_repository.tcl:918
msgid "The 'master' branch has not been initialized."
-msgstr ""
+msgstr "'master'分支尚未初始化."
-#: lib/choose_repository.tcl:527
+#: lib/choose_repository.tcl:705
msgid "Hardlinks are unavailable. Falling back to copying."
-msgstr ""
+msgstr "硬连接不可用. 使用复制."
-#: lib/choose_repository.tcl:539
+#: lib/choose_repository.tcl:717
#, tcl-format
msgid "Cloning from %s"
-msgstr ""
+msgstr "从 %s 克隆"
-#: lib/choose_repository.tcl:570
-#, fuzzy
+#: lib/choose_repository.tcl:748
msgid "Copying objects"
-msgstr "压缩数据库"
+msgstr "复制 objects"
-#: lib/choose_repository.tcl:571
+#: lib/choose_repository.tcl:749
msgid "KiB"
-msgstr ""
+msgstr "KiB"
-#: lib/choose_repository.tcl:595
+#: lib/choose_repository.tcl:773
#, tcl-format
msgid "Unable to copy object: %s"
-msgstr ""
+msgstr "无法复制 object: %s"
-#: lib/choose_repository.tcl:605
+#: lib/choose_repository.tcl:783
msgid "Linking objects"
-msgstr ""
+msgstr "链接 objects"
-#: lib/choose_repository.tcl:606
+#: lib/choose_repository.tcl:784
msgid "objects"
-msgstr ""
+msgstr "objects"
-#: lib/choose_repository.tcl:614
+#: lib/choose_repository.tcl:792
#, tcl-format
msgid "Unable to hardlink object: %s"
-msgstr ""
+msgstr "无法硬链接 object: %s"
-#: lib/choose_repository.tcl:669
+#: lib/choose_repository.tcl:847
msgid "Cannot fetch branches and objects. See console output for details."
-msgstr ""
+msgstr "无法获取分支和对象. 请查看控制终端的输出."
-#: lib/choose_repository.tcl:680
+#: lib/choose_repository.tcl:858
msgid "Cannot fetch tags. See console output for details."
-msgstr ""
+msgstr "无法获取标签. 请查看控制终端的输出."
-#: lib/choose_repository.tcl:704
+#: lib/choose_repository.tcl:882
msgid "Cannot determine HEAD. See console output for details."
-msgstr ""
+msgstr "无法确定 HEAD. 请查看控制终端的输出."
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:891
#, tcl-format
msgid "Unable to cleanup %s"
-msgstr ""
+msgstr "无法清理 %s"
-#: lib/choose_repository.tcl:719
+#: lib/choose_repository.tcl:897
msgid "Clone failed."
-msgstr ""
+msgstr "克隆失败."
-#: lib/choose_repository.tcl:726
+#: lib/choose_repository.tcl:904
msgid "No default branch obtained."
-msgstr ""
+msgstr "没有获取缺省分支"
-#: lib/choose_repository.tcl:737
+#: lib/choose_repository.tcl:915
#, tcl-format
msgid "Cannot resolve %s as a commit."
-msgstr ""
+msgstr "无法解析 %s 为提交."
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:927
msgid "Creating working directory"
-msgstr ""
+msgstr "创建工作目录"
-#: lib/choose_repository.tcl:750 lib/index.tcl:15 lib/index.tcl:80
-#: lib/index.tcl:149
+#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/index.tcl:193
msgid "files"
-msgstr ""
+msgstr "文件"
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:957
msgid "Initial file checkout failed."
-msgstr ""
+msgstr "初始的文件checkout失败"
-#: lib/choose_repository.tcl:795
+#: lib/choose_repository.tcl:973
msgid "Open"
-msgstr ""
+msgstr "打开"
-#: lib/choose_repository.tcl:805
-#, fuzzy
+#: lib/choose_repository.tcl:983
msgid "Repository:"
-msgstr "版本树"
+msgstr "版本库"
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:1033
#, tcl-format
msgid "Failed to open repository %s:"
-msgstr ""
+msgstr "无法打开版本库 %s:"
#: lib/choose_rev.tcl:53
msgid "This Detached Checkout"
-msgstr ""
+msgstr "该脱节的Checkout"
#: lib/choose_rev.tcl:60
msgid "Revision Expression:"
-msgstr ""
+msgstr "版本表达式:"
#: lib/choose_rev.tcl:74
-#, fuzzy
msgid "Local Branch"
-msgstr "分支"
+msgstr "本地分支"
#: lib/choose_rev.tcl:79
-#, fuzzy
msgid "Tracking Branch"
-msgstr "当前分支:"
+msgstr "跟踪分支:"
#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
msgid "Tag"
-msgstr ""
+msgstr "标签"
#: lib/choose_rev.tcl:317
#, tcl-format
msgid "Invalid revision: %s"
-msgstr ""
+msgstr "无效版本: %s"
#: lib/choose_rev.tcl:338
msgid "No revision selected."
-msgstr ""
+msgstr "没有选择版本."
#: lib/choose_rev.tcl:346
msgid "Revision expression is empty."
-msgstr ""
+msgstr "版本表达式为空."
#: lib/choose_rev.tcl:530
msgid "Updated"
-msgstr ""
+msgstr "已更新"
#: lib/choose_rev.tcl:558
msgid "URL"
-msgstr ""
+msgstr "URL"
#: lib/commit.tcl:9
msgid ""
@@ -1138,6 +1180,9 @@ msgid ""
"You are about to create the initial commit. There is no commit before this "
"to amend.\n"
msgstr ""
+"没有改动需要修正.\n"
+"\n"
+"你正在创建最初的提交. 在此之前没有提交可以修正.\n"
#: lib/commit.tcl:18
msgid ""
@@ -1147,18 +1192,22 @@ msgid ""
"completed. You cannot amend the prior commit unless you first abort the "
"current merge activity.\n"
msgstr ""
+"在合并时无法修正.\n"
+"\n"
+"你当前正在一次尚未完成的合并操作过程中. 除非中止当前合并活动,\n"
+"否则无法修正之前的提交.\n"
#: lib/commit.tcl:49
msgid "Error loading commit data for amend:"
-msgstr ""
+msgstr "为修正装载提交数据出错:"
#: lib/commit.tcl:76
msgid "Unable to obtain your identity:"
-msgstr ""
+msgstr "无法获知你的身份:"
#: lib/commit.tcl:81
msgid "Invalid GIT_COMMITTER_IDENT:"
-msgstr ""
+msgstr "无效的 GIT_COMMITTER_IDENT"
#: lib/commit.tcl:133
msgid ""
@@ -1169,6 +1218,12 @@ msgid ""
"\n"
"The rescan will be automatically started now.\n"
msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
#: lib/commit.tcl:154
#, tcl-format
@@ -1178,6 +1233,9 @@ msgid ""
"File %s has merge conflicts. You must resolve them and stage the file "
"before committing.\n"
msgstr ""
+"尚未合并的文件没有办法提交.\n"
+"\n"
+"文件 %s 有合并冲突, 你必须解决这些冲突并缓存该文件作提交.\n"
#: lib/commit.tcl:162
#, tcl-format
@@ -1186,6 +1244,9 @@ msgid ""
"\n"
"File %s cannot be committed by this program.\n"
msgstr ""
+"检测到未知文件状态 %s.\n"
+"\n"
+"文件 %s 无法由该程序提交.\n"
#: lib/commit.tcl:170
msgid ""
@@ -1193,6 +1254,9 @@ msgid ""
"\n"
"You must stage at least 1 file before you can commit.\n"
msgstr ""
+"没有需要提交的变动.\n"
+"\n"
+"提交前你必须首先缓存至少一个文件.\n"
#: lib/commit.tcl:183
msgid ""
@@ -1200,19 +1264,26 @@ msgid ""
"\n"
"A good commit message has the following format:\n"
"\n"
-"- First line: Describe in one sentance what you did.\n"
+"- First line: Describe in one sentence what you did.\n"
"- Second line: Blank\n"
"- Remaining lines: Describe why this change is good.\n"
msgstr ""
+"请提供一条提交信息.\n"
+"\n"
+"一条好的提交信息有下列格式:\n"
+"\n"
+"- 第一行: 一句话概括你做的修改.\n"
+"- 第二行: 空行\n"
+"- 剩余行: 请描述为什么你做的这些改动是好的.\n"
#: lib/commit.tcl:257
msgid "write-tree failed:"
-msgstr ""
+msgstr "write-tree 失败:"
#: lib/commit.tcl:275
#, tcl-format
msgid "Commit %s appears to be corrupt"
-msgstr ""
+msgstr "提交 %s 似乎已损坏"
#: lib/commit.tcl:279
msgid ""
@@ -1222,77 +1293,81 @@ msgid ""
"\n"
"A rescan will be automatically started now.\n"
msgstr ""
+"没有改动提交.\n"
+"\n"
+"该提交没有改动任何文件也不是一个合并提交.\n"
+"\n"
+"重新扫描将自动开始.\n"
#: lib/commit.tcl:286
msgid "No changes to commit."
-msgstr ""
+msgstr "没有改动要提交."
#: lib/commit.tcl:303
#, tcl-format
msgid "warning: Tcl does not support encoding '%s'."
-msgstr ""
+msgstr "警告: Tcl 不支持编码方式 '%s'."
#: lib/commit.tcl:317
msgid "commit-tree failed:"
-msgstr ""
+msgstr "commit-tree 失败:"
#: lib/commit.tcl:339
msgid "update-ref failed:"
-msgstr ""
+msgstr "update-ref 失败:"
#: lib/commit.tcl:430
#, tcl-format
msgid "Created commit %s: %s"
-msgstr ""
+msgstr "创建了 commit %s: %s"
#: lib/console.tcl:57
msgid "Working... please wait..."
-msgstr ""
+msgstr "工作中... 请等待..."
#: lib/console.tcl:183
msgid "Success"
-msgstr ""
+msgstr "成功"
#: lib/console.tcl:196
msgid "Error: Command Failed"
-msgstr ""
+msgstr "错误: 命令失败"
#: lib/database.tcl:43
msgid "Number of loose objects"
-msgstr ""
+msgstr "松散对象的数量"
#: lib/database.tcl:44
msgid "Disk space used by loose objects"
-msgstr ""
+msgstr "松散对象所使用的磁盘空间"
#: lib/database.tcl:45
msgid "Number of packed objects"
-msgstr ""
+msgstr "压缩对象数量"
#: lib/database.tcl:46
msgid "Number of packs"
-msgstr ""
+msgstr "压缩包数量"
#: lib/database.tcl:47
msgid "Disk space used by packed objects"
-msgstr ""
+msgstr "压缩对象所使用的磁盘空间"
#: lib/database.tcl:48
msgid "Packed objects waiting for pruning"
-msgstr ""
+msgstr "压缩对象等待清理"
#: lib/database.tcl:49
msgid "Garbage files"
-msgstr ""
+msgstr "垃圾文件"
#: lib/database.tcl:72
-#, fuzzy
msgid "Compressing the object database"
-msgstr "压缩数据库"
+msgstr "压缩对象数据库"
#: lib/database.tcl:83
msgid "Verifying the object database with fsck-objects"
-msgstr ""
+msgstr "使用 fsck-objects 验证对象数据库"
#: lib/database.tcl:108
#, tcl-format
@@ -1304,11 +1379,16 @@ msgid ""
"\n"
"Compress the database now?"
msgstr ""
+"该版本库当前约有 %i 个松散对象.\n"
+"\n"
+"为达到较优的性能,强烈建议你在松散对象多于 %i 时压缩数据库.\n"
+"\n"
+"现在就压缩数据库么?"
#: lib/date.tcl:25
#, tcl-format
msgid "Invalid date from Git: %s"
-msgstr ""
+msgstr "无效的日期: %s"
#: lib/diff.tcl:42
#, tcl-format
@@ -1323,80 +1403,107 @@ msgid ""
"A rescan will be automatically started to find other files which may have "
"the same state."
msgstr ""
+"未检测到改动.\n"
+"\n"
+"该文件的修改日期被另一个程序所更新, 但其内容并没有变化.\n"
+"\n"
+"对于类似情况的其他文件的重新扫描将自动开始."
#: lib/diff.tcl:81
-#, tcl-format
+#, fuzzy, tcl-format
msgid "Loading diff of %s..."
-msgstr ""
+msgstr "装载 %s 的 diff ..."
#: lib/diff.tcl:114 lib/diff.tcl:184
#, tcl-format
msgid "Unable to display %s"
-msgstr ""
+msgstr "无法显示 %s"
#: lib/diff.tcl:115
msgid "Error loading file:"
-msgstr ""
+msgstr "装载文件出错:"
#: lib/diff.tcl:122
msgid "Git Repository (subproject)"
-msgstr ""
+msgstr "Git 版本库 (子项目)"
#: lib/diff.tcl:134
msgid "* Binary file (not showing content)."
-msgstr ""
+msgstr "* 二进制文件 (不显示内容)."
#: lib/diff.tcl:185
msgid "Error loading diff:"
-msgstr ""
+msgstr "装载 diff 错误:"
#: lib/diff.tcl:302
msgid "Failed to unstage selected hunk."
-msgstr ""
+msgstr "无法将选择的代码段从缓存中删除."
#: lib/diff.tcl:309
msgid "Failed to stage selected hunk."
-msgstr ""
+msgstr "无法缓存所选代码段."
#: lib/error.tcl:12 lib/error.tcl:102
msgid "error"
-msgstr ""
+msgstr "错误"
#: lib/error.tcl:28
msgid "warning"
-msgstr ""
+msgstr "警告"
#: lib/error.tcl:81
msgid "You must correct the above errors before committing."
-msgstr ""
+msgstr "你必须在提交前修正上述错误."
-#: lib/index.tcl:241
-#, fuzzy, tcl-format
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "无法解锁缓存 (index)"
+
+#: lib/index.tcl:15
+msgid "Index Error"
+msgstr "缓存(Index)错误"
+
+#: lib/index.tcl:21
+msgid ""
+"Updating the Git index failed. A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr "更新 Git 缓存(Index)失败, 重新扫描将自动开始以重新同步 git-gui."
+
+#: lib/index.tcl:27
+msgid "Continue"
+msgstr "继续"
+
+#: lib/index.tcl:31
+msgid "Unlock Index"
+msgstr "解锁 Index"
+
+#: lib/index.tcl:282
+#, tcl-format
msgid "Unstaging %s from commit"
-msgstr "从本次提交移除"
+msgstr "从提交缓存中删除 %s"
-#: lib/index.tcl:285
+#: lib/index.tcl:326
#, tcl-format
msgid "Adding %s"
-msgstr ""
+msgstr "添加 %s"
-#: lib/index.tcl:340
-#, fuzzy, tcl-format
+#: lib/index.tcl:381
+#, tcl-format
msgid "Revert changes in file %s?"
-msgstr "恢复修改"
+msgstr "撤销文件 %s 中的改动?"
-#: lib/index.tcl:342
+#: lib/index.tcl:383
#, tcl-format
msgid "Revert changes in these %i files?"
-msgstr ""
+msgstr "撤销这些 (%i个) 文件的改动?"
-#: lib/index.tcl:348
+#: lib/index.tcl:389
msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr ""
+msgstr "任何未缓存的改动将在这次撤销中永久丢失."
-#: lib/index.tcl:351
+#: lib/index.tcl:392
msgid "Do Nothing"
-msgstr ""
+msgstr "不做操作"
#: lib/merge.tcl:13
msgid ""
@@ -1404,6 +1511,9 @@ msgid ""
"\n"
"You must finish amending this commit before starting any type of merge.\n"
msgstr ""
+"修正时无法做合并.\n"
+"\n"
+"你必须完成对该提交的修正才能继续任何类型的合并操作.\n"
#: lib/merge.tcl:27
msgid ""
@@ -1414,6 +1524,12 @@ msgid ""
"\n"
"The rescan will be automatically started now.\n"
msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
#: lib/merge.tcl:44
#, tcl-format
@@ -1425,6 +1541,12 @@ msgid ""
"You must resolve them, stage the file, and commit to complete the current "
"merge. Only then can you begin another merge.\n"
msgstr ""
+"你正处在一个有冲突的合并操作中.\n"
+"\n"
+"文件 %s 有合并冲突.\n"
+"\n"
+"你必须解决这些冲突, 缓存该文件, 并提交来完成当前的合并.仅当这样后才能开始下一"
+"个合并操作.\n"
#: lib/merge.tcl:54
#, tcl-format
@@ -1436,6 +1558,12 @@ msgid ""
"You should complete the current commit before starting a merge. Doing so "
"will help you abort a failed merge, should the need arise.\n"
msgstr ""
+"你正处在一个改动当中.\n"
+"\n"
+"文件 %s 已被修改.\n"
+"\n"
+"你必须完成当前的提交后才能开始合并. 如果需要, 这么做将有助于"
+"中止一次失败的合并.\n"
#: lib/merge.tcl:106
#, tcl-format
@@ -1445,24 +1573,24 @@ msgstr ""
#: lib/merge.tcl:119
#, tcl-format
msgid "Merging %s and %s"
-msgstr ""
+msgstr "合并 %s 和 %s"
#: lib/merge.tcl:131
msgid "Merge completed successfully."
-msgstr ""
+msgstr "合并成功完成."
#: lib/merge.tcl:133
msgid "Merge failed. Conflict resolution is required."
-msgstr ""
+msgstr "合并失败. 需要解决冲突."
#: lib/merge.tcl:158
#, tcl-format
msgid "Merge Into %s"
-msgstr ""
+msgstr "合并到 %s"
#: lib/merge.tcl:177
msgid "Revision To Merge"
-msgstr ""
+msgstr "要合并的版本"
#: lib/merge.tcl:212
msgid ""
@@ -1470,6 +1598,9 @@ msgid ""
"\n"
"You must finish amending this commit.\n"
msgstr ""
+"修正操作中无法中止.\n"
+"\n"
+"你必须先完成本次修正操作.\n"
#: lib/merge.tcl:222
msgid ""
@@ -1479,6 +1610,11 @@ msgid ""
"\n"
"Continue with aborting the current merge?"
msgstr ""
+"中止合并?\n"
+"\n"
+"中止当前的合并操作将导致 *所有* 尚未提交的改动丢失.\n"
+"\n"
+"是否要继续中止当前的合并操作?"
#: lib/merge.tcl:228
msgid ""
@@ -1488,150 +1624,137 @@ msgid ""
"\n"
"Continue with resetting the current changes?"
msgstr ""
+"是否复位当前改动?\n"
+"\n"
+"复位当前的改动将导致 *所有* 未提交的改动丢失.\n"
+"\n"
+"是否要继续复位当前的改动?"
#: lib/merge.tcl:239
msgid "Aborting"
-msgstr ""
+msgstr "中止"
#: lib/merge.tcl:266
msgid "Abort failed."
-msgstr ""
+msgstr "中止失败"
#: lib/merge.tcl:268
msgid "Abort completed. Ready."
-msgstr ""
+msgstr "中止完成. 就绪."
#: lib/option.tcl:82
msgid "Restore Defaults"
-msgstr ""
+msgstr "恢复默认值"
#: lib/option.tcl:86
msgid "Save"
-msgstr ""
+msgstr "保存"
#: lib/option.tcl:96
-#, fuzzy, tcl-format
+#, tcl-format
msgid "%s Repository"
-msgstr "版本树"
+msgstr "%s 版本库"
#: lib/option.tcl:97
msgid "Global (All Repositories)"
-msgstr ""
+msgstr "全局 (所有版本库)"
#: lib/option.tcl:103
msgid "User Name"
-msgstr ""
+msgstr "用户名"
#: lib/option.tcl:104
msgid "Email Address"
-msgstr ""
+msgstr "Email 地址"
#: lib/option.tcl:106
-#, fuzzy
msgid "Summarize Merge Commits"
-msgstr "修订合并提交描述:"
+msgstr "概述合并提交:"
#: lib/option.tcl:107
msgid "Merge Verbosity"
-msgstr ""
+msgstr "合并冗余度"
#: lib/option.tcl:108
msgid "Show Diffstat After Merge"
-msgstr ""
+msgstr "在合并后显示 Diffstat"
#: lib/option.tcl:110
msgid "Trust File Modification Timestamps"
-msgstr ""
+msgstr "相信文件的改动时间"
#: lib/option.tcl:111
msgid "Prune Tracking Branches During Fetch"
-msgstr ""
+msgstr "获取时清除跟踪分支"
#: lib/option.tcl:112
msgid "Match Tracking Branches"
-msgstr ""
+msgstr "匹配跟踪分支"
#: lib/option.tcl:113
msgid "Number of Diff Context Lines"
-msgstr ""
+msgstr "Diff 上下文行数"
#: lib/option.tcl:114
msgid "New Branch Name Template"
-msgstr ""
+msgstr "新建分支命名模板"
#: lib/option.tcl:176
msgid "Change Font"
-msgstr ""
+msgstr "更改字体"
#: lib/option.tcl:180
#, tcl-format
msgid "Choose %s"
-msgstr ""
+msgstr "选择 %s"
#: lib/option.tcl:186
msgid "pt."
-msgstr ""
+msgstr "磅"
#: lib/option.tcl:200
msgid "Preferences"
-msgstr ""
+msgstr "首选项"
#: lib/option.tcl:235
msgid "Failed to completely save options:"
-msgstr ""
-
-#: lib/remote.tcl:165
-msgid "Prune from"
-msgstr ""
-
-#: lib/remote.tcl:170
-#, fuzzy
-msgid "Fetch from"
-msgstr "导入"
-
-#: lib/remote.tcl:213
-#, fuzzy
-msgid "Push to"
-msgstr "上传"
+msgstr "无法完全保存选项:"
#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
msgid "Delete Remote Branch"
-msgstr ""
+msgstr "删除远端分支"
#: lib/remote_branch_delete.tcl:47
-#, fuzzy
msgid "From Repository"
-msgstr "版本树"
+msgstr "从版本库"
#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
msgid "Remote:"
-msgstr ""
+msgstr "Remote:"
#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
msgid "Arbitrary URL:"
-msgstr ""
+msgstr "任意 URL:"
#: lib/remote_branch_delete.tcl:84
-#, fuzzy
msgid "Branches"
msgstr "分支"
#: lib/remote_branch_delete.tcl:109
-#, fuzzy
msgid "Delete Only If"
-msgstr "删除"
+msgstr "删除仅当"
#: lib/remote_branch_delete.tcl:111
msgid "Merged Into:"
-msgstr ""
+msgstr "合并到"
#: lib/remote_branch_delete.tcl:119
msgid "Always (Do not perform merge checks)"
-msgstr ""
+msgstr "总是合并 (不作合并检查)"
#: lib/remote_branch_delete.tcl:152
msgid "A branch is required for 'Merged Into'."
-msgstr ""
+msgstr "'合并到' 需要指定某个分支"
#: lib/remote_branch_delete.tcl:184
#, tcl-format
@@ -1640,6 +1763,9 @@ msgid ""
"\n"
" - %s"
msgstr ""
+"下列分支没有被全部合并到 %s 中:\n"
+"\n"
+" - %s"
#: lib/remote_branch_delete.tcl:189
#, tcl-format
@@ -1647,10 +1773,11 @@ msgid ""
"One or more of the merge tests failed because you have not fetched the "
"necessary commits. Try fetching from %s first."
msgstr ""
+"由于没有获取到必要的提交,一个或多个合并测试失败。请尝试从 %s 处先获取。"
#: lib/remote_branch_delete.tcl:207
msgid "Please select one or more branches to delete."
-msgstr ""
+msgstr "请选择某个或多个分支来删除"
#: lib/remote_branch_delete.tcl:216
msgid ""
@@ -1658,112 +1785,108 @@ msgid ""
"\n"
"Delete the selected branches?"
msgstr ""
+"恢复被删除的分支非常困难.\n"
+"\n"
+"是否要删除所选分支?"
#: lib/remote_branch_delete.tcl:226
#, tcl-format
msgid "Deleting branches from %s"
-msgstr ""
+msgstr "从 %s 中删除分支"
#: lib/remote_branch_delete.tcl:286
msgid "No repository selected."
-msgstr ""
+msgstr "没有选择版本库"
#: lib/remote_branch_delete.tcl:291
#, tcl-format
msgid "Scanning %s..."
-msgstr ""
+msgstr "正在扫描 %s..."
-#: lib/shortcut.tcl:26 lib/shortcut.tcl:74
-msgid "Cannot write script:"
-msgstr ""
+#: lib/remote.tcl:165
+msgid "Prune from"
+msgstr "从..清除(prune)"
+
+#: lib/remote.tcl:170
+msgid "Fetch from"
+msgstr "从..获取(fetch)"
+
+#: lib/remote.tcl:213
+msgid "Push to"
+msgstr "上传到(push)"
+
+#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+msgid "Cannot write shortcut:"
+msgstr "无法修改快捷方式:"
-#: lib/shortcut.tcl:149
+#: lib/shortcut.tcl:136
msgid "Cannot write icon:"
-msgstr ""
+msgstr "无法修改图标:"
#: lib/status_bar.tcl:83
#, tcl-format
msgid "%s ... %*i of %*i %s (%3i%%)"
-msgstr ""
+msgstr "%s ... %*i of %*i %s (%3i%%)"
#: lib/transport.tcl:6
-#, fuzzy, tcl-format
+#, tcl-format
msgid "fetch %s"
-msgstr "导入"
+msgstr "获取(fetch)"
#: lib/transport.tcl:7
#, tcl-format
msgid "Fetching new changes from %s"
-msgstr ""
+msgstr "从 %s 处获取新的改动"
#: lib/transport.tcl:18
#, tcl-format
msgid "remote prune %s"
-msgstr ""
+msgstr "清除远端 %s"
#: lib/transport.tcl:19
#, tcl-format
msgid "Pruning tracking branches deleted from %s"
-msgstr ""
+msgstr "清除"
#: lib/transport.tcl:25 lib/transport.tcl:71
#, tcl-format
msgid "push %s"
-msgstr ""
+msgstr "上传 %s"
#: lib/transport.tcl:26
#, tcl-format
msgid "Pushing changes to %s"
-msgstr ""
+msgstr "上传改动到 %s"
#: lib/transport.tcl:72
#, tcl-format
msgid "Pushing %s %s to %s"
-msgstr ""
+msgstr "上传 %s %s 到 %s"
#: lib/transport.tcl:89
-#, fuzzy
msgid "Push Branches"
-msgstr "分支"
+msgstr "上传分支"
#: lib/transport.tcl:103
-#, fuzzy
msgid "Source Branches"
-msgstr "当前分支:"
+msgstr "源端分支:"
#: lib/transport.tcl:120
-#, fuzzy
msgid "Destination Repository"
-msgstr "版本树"
+msgstr "目标版本库"
#: lib/transport.tcl:158
msgid "Transfer Options"
-msgstr ""
+msgstr "传输选项"
#: lib/transport.tcl:160
msgid "Force overwrite existing branch (may discard changes)"
-msgstr ""
+msgstr "强制覆盖已有的分支 (可能会丢失改动)"
#: lib/transport.tcl:164
msgid "Use thin pack (for slow network connections)"
-msgstr ""
+msgstr "使用 thin pack (适用于低速网络连接)"
#: lib/transport.tcl:168
msgid "Include tags"
-msgstr ""
-
-#~ msgid "Add To Commit"
-#~ msgstr "添加到本次提交"
-
-#~ msgid "Add Existing To Commit"
-#~ msgstr "添加默认修改文件"
-
-#~ msgid "Unstaged Changes (Will Not Be Committed)"
-#~ msgstr "不被提交的修改"
-
-#~ msgid "Add Existing"
-#~ msgstr "添加默认修改文件"
-
-#, fuzzy
-#~ msgid "Push to %s..."
-#~ msgstr "上传..."
+msgstr "包含标签"
diff --git a/git-merge.sh b/git-merge.sh
index 1c123a37e6..7dbbb1d79d 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -37,6 +37,7 @@ use_strategies=
allow_fast_forward=t
allow_trivial_merge=t
+squash= no_commit=
dropsave() {
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
@@ -70,7 +71,7 @@ finish_up_to_date () {
squash_message () {
echo Squashed commit of the following:
echo
- git log --no-merges ^"$head" $remoteheads
+ git log --no-merges --pretty=medium ^"$head" $remoteheads
}
finish () {
@@ -152,17 +153,21 @@ parse_config () {
--summary)
show_diffstat=t ;;
--squash)
- allow_fast_forward=t squash=t no_commit=t ;;
+ test "$allow_fast_forward" = t ||
+ die "You cannot combine --squash with --no-ff."
+ squash=t no_commit=t ;;
--no-squash)
- allow_fast_forward=t squash= no_commit= ;;
+ squash= no_commit= ;;
--commit)
- allow_fast_forward=t squash= no_commit= ;;
+ no_commit= ;;
--no-commit)
- allow_fast_forward=t squash= no_commit=t ;;
+ no_commit=t ;;
--ff)
- allow_fast_forward=t squash= no_commit= ;;
+ allow_fast_forward=t ;;
--no-ff)
- allow_fast_forward=false squash= no_commit= ;;
+ test "$squash" != t ||
+ die "You cannot combine --squash with --no-ff."
+ allow_fast_forward=f ;;
-s|--strategy)
shift
case " $all_strategies " in
diff --git a/git-mergetool.sh b/git-mergetool.sh
index cbbb707959..5c86f69229 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -34,7 +34,7 @@ base_present () {
cleanup_temp_files () {
if test "$1" = --save-backup ; then
- mv -- "$BACKUP" "$path.orig"
+ mv -- "$BACKUP" "$MERGED.orig"
rm -f -- "$LOCAL" "$REMOTE" "$BASE"
else
rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
@@ -67,14 +67,14 @@ resolve_symlink_merge () {
read ans
case "$ans" in
[lL]*)
- git checkout-index -f --stage=2 -- "$path"
- git add -- "$path"
+ git checkout-index -f --stage=2 -- "$MERGED"
+ git add -- "$MERGED"
cleanup_temp_files --save-backup
return
;;
[rR]*)
- git checkout-index -f --stage=3 -- "$path"
- git add -- "$path"
+ git checkout-index -f --stage=3 -- "$MERGED"
+ git add -- "$MERGED"
cleanup_temp_files --save-backup
return
;;
@@ -95,12 +95,12 @@ resolve_deleted_merge () {
read ans
case "$ans" in
[mMcC]*)
- git add -- "$path"
+ git add -- "$MERGED"
cleanup_temp_files --save-backup
return
;;
[dD]*)
- git rm -- "$path" > /dev/null
+ git rm -- "$MERGED" > /dev/null
cleanup_temp_files
return
;;
@@ -112,11 +112,11 @@ resolve_deleted_merge () {
}
check_unchanged () {
- if test "$path" -nt "$BACKUP" ; then
+ if test "$MERGED" -nt "$BACKUP" ; then
status=0;
else
while true; do
- echo "$path seems unchanged."
+ echo "$MERGED seems unchanged."
printf "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
@@ -127,50 +127,38 @@ check_unchanged () {
fi
}
-save_backup () {
- if test "$status" -eq 0; then
- mv -- "$BACKUP" "$path.orig"
- fi
-}
-
-remove_backup () {
- if test "$status" -eq 0; then
- rm "$BACKUP"
- fi
-}
-
merge_file () {
- path="$1"
+ MERGED="$1"
- f=`git ls-files -u -- "$path"`
+ f=`git ls-files -u -- "$MERGED"`
if test -z "$f" ; then
- if test ! -f "$path" ; then
- echo "$path: file not found"
+ if test ! -f "$MERGED" ; then
+ echo "$MERGED: file not found"
else
- echo "$path: file does not need merging"
+ echo "$MERGED: file does not need merging"
fi
exit 1
fi
- ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')"
- BACKUP="$path.BACKUP.$ext"
- LOCAL="$path.LOCAL.$ext"
- REMOTE="$path.REMOTE.$ext"
- BASE="$path.BASE.$ext"
+ ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+ BACKUP="$MERGED.BACKUP.$ext"
+ LOCAL="$MERGED.LOCAL.$ext"
+ REMOTE="$MERGED.REMOTE.$ext"
+ BASE="$MERGED.BASE.$ext"
- mv -- "$path" "$BACKUP"
- cp -- "$BACKUP" "$path"
+ mv -- "$MERGED" "$BACKUP"
+ cp -- "$BACKUP" "$MERGED"
- base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
- local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
- remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
+ base_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
+ local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
+ remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
- base_present && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null
- local_present && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null
- remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null
+ base_present && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
+ local_present && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
+ remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
if test -z "$local_mode" -o -z "$remote_mode"; then
- echo "Deleted merge conflict for '$path':"
+ echo "Deleted merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_deleted_merge
@@ -178,14 +166,14 @@ merge_file () {
fi
if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
- echo "Symbolic link merge conflict for '$path':"
+ echo "Symbolic link merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_symlink_merge
return
fi
- echo "Normal merge conflict for '$path':"
+ echo "Normal merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
@@ -194,36 +182,32 @@ merge_file () {
case "$merge_tool" in
kdiff3)
if base_present ; then
- ("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
- -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+ ("$merge_tool_path" --auto --L1 "$MERGED (Base)" --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" \
+ -o "$MERGED" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
else
- ("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
- -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+ ("$merge_tool_path" --auto --L1 "$MERGED (Local)" --L2 "$MERGED (Remote)" \
+ -o "$MERGED" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
fi
status=$?
- remove_backup
;;
tkdiff)
if base_present ; then
- "$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+ "$merge_tool_path" -a "$BASE" -o "$MERGED" -- "$LOCAL" "$REMOTE"
else
- "$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE"
+ "$merge_tool_path" -o "$MERGED" -- "$LOCAL" "$REMOTE"
fi
status=$?
- save_backup
;;
meld|vimdiff)
touch "$BACKUP"
- "$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE"
+ "$merge_tool_path" -- "$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
- save_backup
;;
gvimdiff)
- touch "$BACKUP"
- "$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE"
- check_unchanged
- save_backup
- ;;
+ touch "$BACKUP"
+ "$merge_tool_path" -f -- "$LOCAL" "$MERGED" "$REMOTE"
+ check_unchanged
+ ;;
xxdiff)
touch "$BACKUP"
if base_present ; then
@@ -231,53 +215,68 @@ merge_file () {
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
- --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
+ --merged-file "$MERGED" -- "$LOCAL" "$BASE" "$REMOTE"
else
"$merge_tool_path" -X --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
- --merged-file "$path" -- "$LOCAL" "$REMOTE"
+ --merged-file "$MERGED" -- "$LOCAL" "$REMOTE"
fi
check_unchanged
- save_backup
;;
opendiff)
touch "$BACKUP"
if base_present; then
- "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
+ "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
else
- "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat
+ "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
fi
check_unchanged
- save_backup
;;
ecmerge)
touch "$BACKUP"
if base_present; then
- "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path"
+ "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED"
else
- "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path"
+ "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED"
fi
check_unchanged
- save_backup
;;
emerge)
if base_present ; then
- "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
+ "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$MERGED")"
else
- "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
+ "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
fi
status=$?
- save_backup
+ ;;
+ *)
+ if test -n "$merge_tool_cmd"; then
+ if test "$merge_tool_trust_exit_code" = "false"; then
+ touch "$BACKUP"
+ ( eval $merge_tool_cmd )
+ check_unchanged
+ else
+ ( eval $merge_tool_cmd )
+ status=$?
+ fi
+ fi
;;
esac
if test "$status" -ne 0; then
- echo "merge of $path failed" 1>&2
- mv -- "$BACKUP" "$path"
+ echo "merge of $MERGED failed" 1>&2
+ mv -- "$BACKUP" "$MERGED"
exit 1
fi
- git add -- "$path"
+
+ if test "$merge_keep_backup" = "true"; then
+ mv -- "$BACKUP" "$MERGED.orig"
+ else
+ rm -- "$BACKUP"
+ fi
+
+ git add -- "$MERGED"
cleanup_temp_files
}
@@ -309,12 +308,20 @@ do
shift
done
+valid_custom_tool()
+{
+ merge_tool_cmd="$(git config mergetool.$1.cmd)"
+ test -n "$merge_tool_cmd"
+}
+
valid_tool() {
case "$1" in
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
- return 1
+ if ! valid_custom_tool "$1"; then
+ return 1
+ fi
;;
esac
}
@@ -380,10 +387,16 @@ else
init_merge_tool_path "$merge_tool"
- if ! type "$merge_tool_path" > /dev/null 2>&1; then
+ merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
+
+ if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
exit 1
fi
+
+ if ! test -z "$merge_tool_cmd"; then
+ merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
+ fi
fi
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index 233e5eae1d..7cd8f7134e 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -63,7 +63,23 @@ tmp_info="$tmp_dir/info"
commit=$(git rev-parse HEAD)
mkdir $tmp_dir || exit 2
-for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
+while read patch_name level garbage
+do
+ case "$patch_name" in ''|'#'*) continue;; esac
+ case "$level" in
+ -p*) ;;
+ ''|'#'*)
+ level=;;
+ *)
+ echo "unable to parse patch level, ignoring it."
+ level=;;
+ esac
+ case "$garbage" in
+ ''|'#'*);;
+ *)
+ echo "trailing garbage found in series file: $garbage"
+ exit 1;;
+ esac
if ! [ -f "$QUILT_PATCHES/$patch_name" ] ; then
echo "$patch_name doesn't exist. Skipping."
continue
@@ -113,10 +129,10 @@ for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
fi
if [ -z "$dry_run" ] ; then
- git apply --index -C1 "$tmp_patch" &&
+ git apply --index -C1 ${level:+"$level"} "$tmp_patch" &&
tree=$(git write-tree) &&
commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
fi
-done
+done <"$QUILT_PATCHES/series"
rm -rf $tmp_dir || exit 5
diff --git a/git-rebase.sh b/git-rebase.sh
index 6b9af962a9..ff66af3ba8 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -18,8 +18,7 @@ original <branch> and remove the .dotest working files, use the command
git rebase --abort instead.
Note that if <branch> is not specified on the command line, the
-currently checked out branch is used. You must be in the top
-directory of your project to start (or continue) a rebase.
+currently checked out branch is used.
Example: git-rebase master~1 topic
@@ -376,7 +375,7 @@ fi
if test -z "$do_merge"
then
git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
- git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" &&
+ git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
move_to_original_branch
ret=$?
test 0 != $ret -a -d .dotest &&
diff --git a/git-send-email.perl b/git-send-email.perl
index 29b1105c4c..be4a20d7cd 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -317,7 +317,7 @@ if ($suppress_cc{'all'}) {
# If explicit old-style ones are specified, they trump --suppress-cc.
$suppress_cc{'self'} = $suppress_from if defined $suppress_from;
-$suppress_cc{'sob'} = $signed_off_cc if defined $signed_off_cc;
+$suppress_cc{'sob'} = !$signed_off_cc if defined $signed_off_cc;
# Debugging, print out the suppressions.
if (0) {
@@ -855,6 +855,7 @@ foreach my $t (@files) {
$message .= $_;
if (/^(Signed-off-by|Cc): (.*)$/i) {
next if ($suppress_cc{'sob'});
+ chomp;
my $c = $2;
chomp $c;
next if ($c eq $sender and $suppress_cc{'self'});
diff --git a/git-stash.sh b/git-stash.sh
index b00f888169..c2b68205a2 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Copyright (c) 2007, Nanako Shiraishi
-USAGE='[ | save | list | show | apply | clear | create ]'
+USAGE='[ | save | list | show | apply | clear | drop | pop | create ]'
SUBDIRECTORY_OK=Yes
OPTIONS_SPEC=
@@ -196,6 +196,28 @@ apply_stash () {
fi
}
+drop_stash () {
+ have_stash || die 'No stash entries to drop'
+
+ if test $# = 0
+ then
+ set x "$ref_stash@{0}"
+ shift
+ fi
+ # Verify supplied argument looks like a stash entry
+ s=$(git rev-parse --revs-only --no-flags "$@") &&
+ git rev-parse --verify "$s:" > /dev/null 2>&1 &&
+ git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
+ git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
+ die "$*: not a valid stashed state"
+
+ git reflog delete --updateref --rewrite "$@" &&
+ echo "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+
+ # clear_stash if we just dropped the last stash entry
+ git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
+}
+
# Main command set
case "$1" in
list)
@@ -230,6 +252,18 @@ create)
fi
create_stash "$*" && echo "$w_commit"
;;
+drop)
+ shift
+ drop_stash "$@"
+ ;;
+pop)
+ shift
+ if apply_stash "$@"
+ then
+ test -z "$unstash_index" || shift
+ drop_stash "$@"
+ fi
+ ;;
*)
if test $# -eq 0
then
diff --git a/git-submodule.sh b/git-submodule.sh
index 558a5ca107..5f1d5ef06e 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -155,20 +155,6 @@ cmd_add()
usage
fi
- case "$repo" in
- ./*|../*)
- # dereference source url relative to parent's url
- realrepo="$(resolve_relative_url $repo)" ;;
- *)
- # Turn the source into an absolute path if
- # it is local
- if base=$(get_repo_base "$repo"); then
- repo="$base"
- fi
- realrepo=$repo
- ;;
- esac
-
# Guess path from repo if not specified or strip trailing slashes
if test -z "$path"; then
path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
@@ -176,15 +162,39 @@ cmd_add()
path=$(echo "$path" | sed -e 's|/*$||')
fi
- test -e "$path" &&
- die "'$path' already exists"
-
git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
die "'$path' already exists in the index"
- module_clone "$path" "$realrepo" || exit
- (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
- die "Unable to checkout submodule '$path'"
+ # perhaps the path exists and is already a git repo, else clone it
+ if test -e "$path"
+ then
+ if test -d "$path/.git" &&
+ test "$(unset GIT_DIR; cd $path; git rev-parse --git-dir)" = ".git"
+ then
+ echo "Adding existing repo at '$path' to the index"
+ else
+ die "'$path' already exists and is not a valid git repo"
+ fi
+ else
+ case "$repo" in
+ ./*|../*)
+ # dereference source url relative to parent's url
+ realrepo="$(resolve_relative_url $repo)" ;;
+ *)
+ # Turn the source into an absolute path if
+ # it is local
+ if base=$(get_repo_base "$repo"); then
+ repo="$base"
+ fi
+ realrepo=$repo
+ ;;
+ esac
+
+ module_clone "$path" "$realrepo" || exit
+ (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
+ die "Unable to checkout submodule '$path'"
+ fi
+
git add "$path" ||
die "Failed to add submodule '$path'"
diff --git a/git-svn.perl b/git-svn.perl
index 9e2faf90aa..bba22c1321 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -522,7 +522,8 @@ sub cmd_dcommit {
}
sub cmd_find_rev {
- my $revision_or_hash = shift;
+ my $revision_or_hash = shift or die "SVN or git revision required ",
+ "as a command-line argument\n";
my $result;
if ($revision_or_hash =~ /^r\d+$/) {
my $head = shift;
@@ -957,9 +958,10 @@ sub complete_url_ls_init {
"wanted to set to: $gs->{url}\n";
}
command_oneline('config', $k, $gs->{url}) unless $orig_url;
- my $remote_path = "$ra->{svn_path}/$repo_path/*";
+ my $remote_path = "$ra->{svn_path}/$repo_path";
$remote_path =~ s#/+#/#g;
$remote_path =~ s#^/##g;
+ $remote_path .= "/*" if $remote_path !~ /\*/;
my ($n) = ($switch =~ /^--(\w+)/);
if (length $pfx && $pfx !~ m#/$#) {
die "--prefix='$pfx' must have a trailing slash '/'\n";
@@ -1540,9 +1542,14 @@ sub find_by_url { # repos_root and, path are optional
$remotes->{$repo_id}->{$_});
}
my $p = $path;
+ my $rwr = rewrite_root({repo_id => $repo_id});
unless (defined $p) {
$p = $full_url;
- $p =~ s#^\Q$u\E(?:/|$)## or next;
+ my $z = $u;
+ if ($rwr) {
+ $z = $rwr;
+ }
+ $p =~ s#^\Q$z\E(?:/|$)## or next;
}
foreach my $f (keys %$fetch) {
next if $f ne $p;
diff --git a/git-web--browse.sh b/git-web--browse.sh
index 1023b90859..384148a59f 100755
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -23,12 +23,18 @@ USAGE='[--browser=browser|--tool=browser] [--config=conf.var] url/file ...'
NONGIT_OK=Yes
. git-sh-setup
+valid_custom_tool()
+{
+ browser_cmd="$(git config "browser.$1.cmd")"
+ test -n "$browser_cmd"
+}
+
valid_tool() {
case "$1" in
firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open)
;; # happy
*)
- return 1
+ valid_custom_tool "$1" || return 1
;;
esac
}
@@ -122,7 +128,7 @@ else
init_browser_path "$browser"
- if ! type "$browser_path" > /dev/null 2>&1; then
+ if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then
die "The browser $browser is not available as '$browser_path'."
fi
fi
@@ -157,4 +163,9 @@ case "$browser" in
dillo)
"$browser_path" "$@" &
;;
+ *)
+ if test -n "$browser_cmd"; then
+ ( eval $browser_cmd "$@" )
+ fi
+ ;;
esac
diff --git a/git.c b/git.c
index 9cca81a60e..13de8018f0 100644
--- a/git.c
+++ b/git.c
@@ -334,6 +334,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "push", cmd_push, RUN_SETUP },
{ "read-tree", cmd_read_tree, RUN_SETUP },
{ "reflog", cmd_reflog, RUN_SETUP },
+ { "remote", cmd_remote, RUN_SETUP },
{ "repo-config", cmd_config },
{ "rerere", cmd_rerere, RUN_SETUP },
{ "reset", cmd_reset, RUN_SETUP },
@@ -342,7 +343,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
{ "rm", cmd_rm, RUN_SETUP },
{ "send-pack", cmd_send_pack, RUN_SETUP },
- { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
+ { "shortlog", cmd_shortlog, USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
diff --git a/gitk-git/Makefile b/gitk-git/Makefile
index ae2b80b108..f90dfabffa 100644
--- a/gitk-git/Makefile
+++ b/gitk-git/Makefile
@@ -8,6 +8,7 @@ gitk_libdir ?= $(sharedir)/gitk/lib
msgsdir ?= $(gitk_libdir)/msgs
msgsdir_SQ = $(subst ','\'',$(msgsdir))
+TCL_PATH ?= tclsh
TCLTK_PATH ?= wish
INSTALL ?= install
RM ?= rm -f
@@ -22,6 +23,9 @@ ifdef NO_MSGFMT
MSGFMT ?= $(TCL_PATH) po/po2msg.sh
else
MSGFMT ?= msgfmt
+ ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
+ MSGFMT := $(TCL_PATH) po/po2msg.sh
+ endif
endif
PO_TEMPLATE = po/gitk.pot
diff --git a/gitk-git/gitk b/gitk-git/gitk
index f1f21e97bf..84ab02e15f 100644
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -82,7 +82,7 @@ proc dorunq {} {
proc start_rev_list {view} {
global startmsecs
global commfd leftover tclencoding datemode
- global viewargs viewfiles commitidx viewcomplete vnextroot
+ global viewargs viewargscmd viewfiles commitidx viewcomplete vnextroot
global showlocalchanges commitinterest mainheadid
global progressdirn progresscoords proglastnc curview
@@ -90,13 +90,23 @@ proc start_rev_list {view} {
set commitidx($view) 0
set viewcomplete($view) 0
set vnextroot($view) 0
+ set args $viewargs($view)
+ if {$viewargscmd($view) ne {}} {
+ if {[catch {
+ set str [exec sh -c $viewargscmd($view)]
+ } err]} {
+ error_popup "Error executing --argscmd command: $err"
+ exit 1
+ }
+ set args [concat $args [split $str "\n"]]
+ }
set order "--topo-order"
if {$datemode} {
set order "--date-order"
}
if {[catch {
set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \
- --boundary $viewargs($view) "--" $viewfiles($view)] r]
+ --boundary $args "--" $viewfiles($view)] r]
} err]} {
error_popup "[mc "Error executing git rev-list:"] $err"
exit 1
@@ -393,6 +403,9 @@ proc readcommit {id} {
proc updatecommits {} {
global viewdata curview phase displayorder ordertok idpending
global children commitrow selectedline thickerline showneartags
+ global isworktree
+
+ set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
if {$phase ne {}} {
stop_rev_list
@@ -827,6 +840,7 @@ proc makewindow {} {
}
frame .bleft.top
frame .bleft.mid
+ frame .bleft.bottom
button .bleft.top.search -text [mc "Search"] -command dosearch
pack .bleft.top.search -side left -padx 5
@@ -854,18 +868,25 @@ proc makewindow {} {
checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
-command changeignorespace -variable ignorespace
pack .bleft.mid.ignspace -side left -padx 5
- set ctext .bleft.ctext
+ set ctext .bleft.bottom.ctext
text $ctext -background $bgcolor -foreground $fgcolor \
-state disabled -font textfont \
- -yscrollcommand scrolltext -wrap none
+ -yscrollcommand scrolltext -wrap none \
+ -xscrollcommand ".bleft.bottom.sbhorizontal set"
if {$have_tk85} {
$ctext conf -tabstyle wordprocessor
}
- scrollbar .bleft.sb -command "$ctext yview"
+ scrollbar .bleft.bottom.sb -command "$ctext yview"
+ scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h \
+ -width 10
pack .bleft.top -side top -fill x
pack .bleft.mid -side top -fill x
- pack .bleft.sb -side right -fill y
- pack $ctext -side left -fill both -expand 1
+ grid $ctext .bleft.bottom.sb -sticky nsew
+ grid .bleft.bottom.sbhorizontal -sticky ew
+ grid columnconfigure .bleft.bottom 0 -weight 1
+ grid rowconfigure .bleft.bottom 0 -weight 1
+ grid rowconfigure .bleft.bottom 1 -weight 0
+ pack .bleft.bottom -side top -fill both -expand 1
lappend bglist $ctext
lappend fglist $ctext
@@ -930,9 +951,17 @@ proc makewindow {} {
.pwbottom add .bright
.ctop add .pwbottom
- # restore window position if known
+ # restore window width & height if known
if {[info exists geometry(main)]} {
- wm geometry . "$geometry(main)"
+ if {[scan $geometry(main) "%dx%d" w h] >= 2} {
+ if {$w > [winfo screenwidth .]} {
+ set w [winfo screenwidth .]
+ }
+ if {$h > [winfo screenheight .]} {
+ set h [winfo screenheight .]
+ }
+ wm geometry . "${w}x$h"
+ }
}
if {[tk windowingsystem] eq {aqua}} {
@@ -1160,9 +1189,10 @@ proc savestuff {w} {
global canv canv2 canv3 mainfont textfont uifont tabstop
global stuffsaved findmergefiles maxgraphpct
global maxwidth showneartags showlocalchanges
- global viewname viewfiles viewargs viewperm nextviewnum
+ global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
global cmitmode wrapcomment datetimeformat limitdiffs
global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
+ global autoselect
if {$stuffsaved} return
if {![winfo viewable .]} return
@@ -1177,6 +1207,7 @@ proc savestuff {w} {
puts $f [list set maxwidth $maxwidth]
puts $f [list set cmitmode $cmitmode]
puts $f [list set wrapcomment $wrapcomment]
+ puts $f [list set autoselect $autoselect]
puts $f [list set showneartags $showneartags]
puts $f [list set showlocalchanges $showlocalchanges]
puts $f [list set datetimeformat $datetimeformat]
@@ -1199,7 +1230,7 @@ proc savestuff {w} {
puts -nonewline $f "set permviews {"
for {set v 0} {$v < $nextviewnum} {incr v} {
if {$viewperm($v)} {
- puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
+ puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
}
}
puts $f "}"
@@ -1850,7 +1881,7 @@ proc shellsplit {str} {
proc newview {ishighlight} {
global nextviewnum newviewname newviewperm newishighlight
- global newviewargs revtreeargs
+ global newviewargs revtreeargs viewargscmd newviewargscmd curview
set newishighlight $ishighlight
set top .gitkview
@@ -1858,16 +1889,17 @@ proc newview {ishighlight} {
raise $top
return
}
- set newviewname($nextviewnum) "View $nextviewnum"
+ set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
set newviewperm($nextviewnum) 0
set newviewargs($nextviewnum) [shellarglist $revtreeargs]
+ set newviewargscmd($nextviewnum) $viewargscmd($curview)
vieweditor $top $nextviewnum [mc "Gitk view definition"]
}
proc editview {} {
global curview
global viewname viewperm newviewname newviewperm
- global viewargs newviewargs
+ global viewargs newviewargs viewargscmd newviewargscmd
set top .gitkvedit-$curview
if {[winfo exists $top]} {
@@ -1877,6 +1909,7 @@ proc editview {} {
set newviewname($curview) $viewname($curview)
set newviewperm($curview) $viewperm($curview)
set newviewargs($curview) [shellarglist $viewargs($curview)]
+ set newviewargscmd($curview) $viewargscmd($curview)
vieweditor $top $curview "Gitk: edit view $viewname($curview)"
}
@@ -1897,6 +1930,14 @@ proc vieweditor {top n title} {
entry $top.args -width 50 -textvariable newviewargs($n) \
-background $bgcolor
grid $top.args - -sticky ew -padx 5
+
+ message $top.ac -aspect 1000 \
+ -text [mc "Command to generate more commits to include:"]
+ grid $top.ac - -sticky w -pady 5
+ entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
+ -background white
+ grid $top.argscmd - -sticky ew -padx 5
+
message $top.l -aspect 1000 \
-text [mc "Enter files and directories to include, one per line:"]
grid $top.l - -sticky w
@@ -1940,7 +1981,7 @@ proc allviewmenus {n op args} {
proc newviewok {top n} {
global nextviewnum newviewperm newviewname newishighlight
global viewname viewfiles viewperm selectedview curview
- global viewargs newviewargs viewhlmenu
+ global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
if {[catch {
set newargs [shellsplit $newviewargs($n)]
@@ -1964,6 +2005,7 @@ proc newviewok {top n} {
set viewperm($n) $newviewperm($n)
set viewfiles($n) $files
set viewargs($n) $newargs
+ set viewargscmd($n) $newviewargscmd($n)
addviewmenu $n
if {!$newishighlight} {
run showview $n
@@ -1980,9 +2022,11 @@ proc newviewok {top n} {
# doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
# entryconf [list -label $viewname($n) -value $viewname($n)]
}
- if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
+ if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
+ $newviewargscmd($n) ne $viewargscmd($n)} {
set viewfiles($n) $files
set viewargs($n) $newargs
+ set viewargscmd($n) $newviewargscmd($n)
if {$curview == $n} {
run updatecommits
}
@@ -2058,8 +2102,6 @@ proc showview {n} {
set ybot [expr {[lindex $span 1] * $ymax}]
if {$ytop < $y && $y < $ybot} {
set yscreen [expr {$y - $ytop}]
- } else {
- set yscreen [expr {($ybot - $ytop) / 2}]
}
} elseif {[info exists pending_select]} {
set selid $pending_select
@@ -2120,7 +2162,7 @@ proc showview {n} {
set yf 0
set row {}
set selectfirst 0
- if {$selid ne {} && [info exists commitrow($n,$selid)]} {
+ if {[info exists yscreen] && [info exists commitrow($n,$selid)]} {
set row $commitrow($n,$selid)
# try to get the selected row in the same position on the screen
set ymax [lindex [$canv cget -scrollregion] 3]
@@ -2844,8 +2886,9 @@ proc dohidelocalchanges {} {
# spawn off a process to do git diff-index --cached HEAD
proc dodiffindex {} {
global localirow localfrow lserial showlocalchanges
+ global isworktree
- if {!$showlocalchanges} return
+ if {!$showlocalchanges || !$isworktree} return
incr lserial
set localfrow -1
set localirow -1
@@ -4650,6 +4693,7 @@ proc selectline {l isnew} {
global commentend idtags linknum
global mergemax numcommits pending_select
global cmitmode showneartags allcommits
+ global autoselect
catch {unset pending_select}
$canv delete hover
@@ -4705,8 +4749,10 @@ proc selectline {l isnew} {
set currentid $id
$sha1entry delete 0 end
$sha1entry insert 0 $id
- $sha1entry selection from 0
- $sha1entry selection to end
+ if {$autoselect} {
+ $sha1entry selection from 0
+ $sha1entry selection to end
+ }
rhighlight_sel $id
$ctext conf -state normal
@@ -5604,7 +5650,7 @@ proc searchmarkvisible {doall} {
proc scrolltext {f0 f1} {
global searchstring
- .bleft.sb set $f0 $f1
+ .bleft.bottom.sb set $f0 $f1
if {$searchstring ne {}} {
searchmarkvisible 0
}
@@ -7943,7 +7989,7 @@ proc doprefs {} {
global maxwidth maxgraphpct
global oldprefs prefstop showneartags showlocalchanges
global bgcolor fgcolor ctext diffcolors selectbgcolor
- global tabstop limitdiffs
+ global tabstop limitdiffs autoselect
set top .gitkprefs
set prefstop $top
@@ -7973,6 +8019,11 @@ proc doprefs {} {
checkbutton $top.showlocal.b -variable showlocalchanges
pack $top.showlocal.b $top.showlocal.l -side left
grid x $top.showlocal -sticky w
+ frame $top.autoselect
+ label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
+ checkbutton $top.autoselect.b -variable autoselect
+ pack $top.autoselect.b $top.autoselect.l -side left
+ grid x $top.autoselect -sticky w
label $top.ddisp -text [mc "Diff display options"]
grid $top.ddisp - -sticky w -pady 10
@@ -8463,6 +8514,7 @@ set maxlinelen 200
set showlocalchanges 1
set limitdiffs 1
set datetimeformat "%Y-%m-%d %H:%M:%S"
+set autoselect 1
set colors {green red blue magenta darkgrey brown orange}
set bgcolor white
@@ -8522,8 +8574,9 @@ set mergeonly 0
set revtreeargs {}
set cmdline_files {}
set i 0
+set revtreeargscmd {}
foreach arg $argv {
- switch -- $arg {
+ switch -glob -- $arg {
"" { }
"-d" { set datemode 1 }
"--merge" {
@@ -8534,6 +8587,9 @@ foreach arg $argv {
set cmdline_files [lrange $argv [expr {$i + 1}] end]
break
}
+ "--argscmd=*" {
+ set revtreeargscmd [string range $arg 10 end]
+ }
default {
lappend revtreeargs $arg
}
@@ -8635,6 +8691,7 @@ set highlight_files {}
set viewfiles(0) {}
set viewperm(0) 0
set viewargs(0) {}
+set viewargscmd(0) {}
set cmdlineok 0
set stopped 0
@@ -8643,6 +8700,7 @@ set patchnum 0
set localirow -1
set localfrow -1
set lserial 0
+set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
setcoords
makewindow
# wait for the window to become visible
@@ -8650,7 +8708,7 @@ tkwait visibility .
wm title . "[file tail $argv0]: [file tail [pwd]]"
readrefs
-if {$cmdline_files ne {} || $revtreeargs ne {}} {
+if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
# create a view for the files/dirs specified on the command line
set curview 1
set selectedview 1
@@ -8658,6 +8716,7 @@ if {$cmdline_files ne {} || $revtreeargs ne {}} {
set viewname(1) [mc "Command line"]
set viewfiles(1) $cmdline_files
set viewargs(1) $revtreeargs
+ set viewargscmd(1) $revtreeargscmd
set viewperm(1) 0
addviewmenu 1
.bar.view entryconf [mc "Edit view..."] -state normal
@@ -8671,6 +8730,7 @@ if {[info exists permviews]} {
set viewname($n) [lindex $v 0]
set viewfiles($n) [lindex $v 1]
set viewargs($n) [lindex $v 2]
+ set viewargscmd($n) [lindex $v 3]
set viewperm($n) 1
addviewmenu $n
}
diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po
new file mode 100644
index 0000000000..d0f4c2e19a
--- /dev/null
+++ b/gitk-git/po/it.po
@@ -0,0 +1,890 @@
+# Translation of gitk
+# Copyright (C) 2005-2008 Paul Mackerras
+# This file is distributed under the same license as the gitk package.
+# Michele Ballabio <barra_cuda@katamail.com>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"PO-Revision-Date: 2008-03-13 17:34+0100\n"
+"Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
+"Language-Team: Italian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:111
+msgid "Error executing git rev-list:"
+msgstr "Errore nell'esecuzione di git rev-list:"
+
+#: gitk:124
+msgid "Reading"
+msgstr "Lettura in corso"
+
+#: gitk:151 gitk:2191
+msgid "Reading commits..."
+msgstr "Lettura delle revisioni in corso..."
+
+#: gitk:275
+msgid "Can't parse git log output:"
+msgstr "Impossibile elaborare i dati di git log:"
+
+#: gitk:386 gitk:2195
+msgid "No commits selected"
+msgstr "Nessuna revisione selezionata"
+
+#: gitk:500
+msgid "No commit information available"
+msgstr "Nessuna informazione disponibile sulle revisioni"
+
+#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+msgid "OK"
+msgstr "OK"
+
+#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
+#: gitk:7924 gitk:8083
+msgid "Cancel"
+msgstr "Annulla"
+
+#: gitk:661
+msgid "File"
+msgstr "File"
+
+#: gitk:663
+msgid "Update"
+msgstr "Aggiorna"
+
+#: gitk:664
+msgid "Reread references"
+msgstr "Rileggi riferimenti"
+
+#: gitk:665
+msgid "List references"
+msgstr "Elenca riferimenti"
+
+#: gitk:666
+msgid "Quit"
+msgstr "Esci"
+
+#: gitk:668
+msgid "Edit"
+msgstr "Modifica"
+
+#: gitk:669
+msgid "Preferences"
+msgstr "Preferenze"
+
+#: gitk:672 gitk:1892
+msgid "View"
+msgstr "Vista"
+
+#: gitk:673
+msgid "New view..."
+msgstr "Nuova vista..."
+
+#: gitk:674 gitk:2133 gitk:8722
+msgid "Edit view..."
+msgstr "Modifica vista..."
+
+#: gitk:676 gitk:2134 gitk:8723
+msgid "Delete view"
+msgstr "Elimina vista"
+
+#: gitk:678
+msgid "All files"
+msgstr "Tutti i file"
+
+#: gitk:682
+msgid "Help"
+msgstr "Aiuto"
+
+#: gitk:683 gitk:1317
+msgid "About gitk"
+msgstr "Informazioni su gitk"
+
+#: gitk:684
+msgid "Key bindings"
+msgstr "Scorciatoie da tastiera"
+
+#: gitk:741
+msgid "SHA1 ID: "
+msgstr "SHA1 ID: "
+
+#: gitk:791
+msgid "Find"
+msgstr "Trova"
+
+#: gitk:792
+msgid "next"
+msgstr "succ"
+
+#: gitk:793
+msgid "prev"
+msgstr "prec"
+
+#: gitk:794
+msgid "commit"
+msgstr "revisione"
+
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+msgid "containing:"
+msgstr "contenente:"
+
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+msgid "touching paths:"
+msgstr "che riguarda i percorsi:"
+
+#: gitk:801 gitk:2436
+msgid "adding/removing string:"
+msgstr "che aggiunge/rimuove la stringa:"
+
+#: gitk:810 gitk:812
+msgid "Exact"
+msgstr "Esatto"
+
+#: gitk:812 gitk:2514 gitk:4274
+msgid "IgnCase"
+msgstr ""
+
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+msgid "Regexp"
+msgstr ""
+
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+msgid "All fields"
+msgstr "Tutti i campi"
+
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+msgid "Headline"
+msgstr "Titolo"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+msgid "Comments"
+msgstr "Commenti"
+
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
+#: gitk:5971
+msgid "Author"
+msgstr "Autore"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+msgid "Committer"
+msgstr "Revisione creata da"
+
+#: gitk:845
+msgid "Search"
+msgstr "Cerca"
+
+#: gitk:852
+msgid "Diff"
+msgstr ""
+
+#: gitk:854
+msgid "Old version"
+msgstr "Vecchia versione"
+
+#: gitk:856
+msgid "New version"
+msgstr "Nuova versione"
+
+#: gitk:858
+msgid "Lines of context"
+msgstr "Linee di contesto"
+
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Ignora modifiche agli spazi"
+
+#: gitk:926
+msgid "Patch"
+msgstr "Modifiche"
+
+#: gitk:928
+msgid "Tree"
+msgstr "Directory"
+
+#: gitk:1053 gitk:1068 gitk:6022
+msgid "Diff this -> selected"
+msgstr "Diff questo -> selezionato"
+
+#: gitk:1055 gitk:1070 gitk:6023
+msgid "Diff selected -> this"
+msgstr "Diff selezionato -> questo"
+
+#: gitk:1057 gitk:1072 gitk:6024
+msgid "Make patch"
+msgstr "Crea patch"
+
+#: gitk:1058 gitk:6162
+msgid "Create tag"
+msgstr "Crea etichetta"
+
+#: gitk:1059 gitk:6255
+msgid "Write commit to file"
+msgstr "Scrivi revisione in un file"
+
+#: gitk:1060 gitk:6309
+msgid "Create new branch"
+msgstr "Crea un nuovo ramo"
+
+#: gitk:1061
+msgid "Cherry-pick this commit"
+msgstr "Porta questa revisione in cima al ramo attuale"
+
+#: gitk:1063
+msgid "Reset HEAD branch to here"
+msgstr "Aggiorna il ramo HEAD a questa revisione"
+
+#: gitk:1079
+msgid "Check out this branch"
+msgstr "Attiva questo ramo"
+
+#: gitk:1081
+msgid "Remove this branch"
+msgstr "Elimina questo ramo"
+
+#: gitk:1087
+msgid "Highlight this too"
+msgstr "Evidenzia anche questo"
+
+#: gitk:1089
+msgid "Highlight this only"
+msgstr "Evidenzia solo questo"
+
+#: gitk:1318
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - un visualizzatore di revisioni per git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
+"License"
+
+#: gitk:1326 gitk:1387 gitk:6581
+msgid "Close"
+msgstr "Chiudi"
+
+#: gitk:1345
+msgid "Gitk key bindings"
+msgstr "Scorciatoie da tastiera di Gitk"
+
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Scorciatoie da tastiera di Gitk:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tEsci"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tVai alla prima revisione"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tVai all'ultima revisione"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tVai più in alto di una revisione"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tVai più in basso di una revisione"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tTorna indietro nella cronologia"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tVai avanti nella cronologia"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tVai più in alto di una pagina nella lista delle revisioni"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tVai più in basso di una pagina nella lista delle revisioni"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tScorri alla fine della lista delle revisioni"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tScorri la lista delle revisioni in alto di una riga"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tScorri la lista delle revisioni in basso di una riga"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tScorri la lista delle revisioni in alto di una pagina"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tScorri la lista delle revisioni in basso di una pagina"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tTrova all'indietro (verso l'alto, revisioni successive)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tTrova in avanti (verso il basso, revisioni precedenti)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tScorri la vista delle differenze in basso di una pagina"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tTrova"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tTrova in avanti"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tTrova in avanti"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tTrova in avanti, o cerca di nuovo"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tTrova all'indietro"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tScorri la vista delle differenze al file successivo"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumenta grandezza carattere"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAumenta grandezza carattere"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tDiminuisci grandezza carattere"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tDiminuisci grandezza carattere"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAggiorna"
+
+#: gitk:1896
+msgid "Gitk view definition"
+msgstr "Scelta vista Gitk"
+
+#: gitk:1921
+msgid "Name"
+msgstr "Nome"
+
+#: gitk:1924
+msgid "Remember this view"
+msgstr "Ricorda questa vista"
+
+#: gitk:1928
+msgid "Commits to include (arguments to git rev-list):"
+msgstr "Revisioni da includere (argomenti di git rev-list):"
+
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Comando che genera altre revisioni da visualizzare:"
+
+#: gitk:1942
+msgid "Enter files and directories to include, one per line:"
+msgstr "Inserire file e directory da includere, uno per riga:"
+
+#: gitk:1989
+msgid "Error in commit selection arguments:"
+msgstr "Errore negli argomenti di selezione delle revisioni:"
+
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+msgid "None"
+msgstr "Nessuno"
+
+#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+msgid "Date"
+msgstr "Data"
+
+#: gitk:2531 gitk:4336
+msgid "CDate"
+msgstr ""
+
+#: gitk:2680 gitk:2685
+msgid "Descendant"
+msgstr "Discendente"
+
+#: gitk:2681
+msgid "Not descendant"
+msgstr "Non discendente"
+
+#: gitk:2688 gitk:2693
+msgid "Ancestor"
+msgstr "Ascendente"
+
+#: gitk:2689
+msgid "Not ancestor"
+msgstr "Non ascendente"
+
+#: gitk:2924
+msgid "Local changes checked in to index but not committed"
+msgstr "Modifiche locali presenti nell'indice ma non nell'archivio"
+
+#: gitk:2954
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Modifiche locali non presenti né nell'archivio né nell'indice"
+
+#: gitk:4305
+msgid "Searching"
+msgstr "Ricerca in corso"
+
+#: gitk:4767
+msgid "Tags:"
+msgstr "Etichette:"
+
+#: gitk:4784 gitk:4790 gitk:5951
+msgid "Parent"
+msgstr "Genitore"
+
+#: gitk:4795
+msgid "Child"
+msgstr "Figlio"
+
+#: gitk:4804
+msgid "Branch"
+msgstr "Ramo"
+
+#: gitk:4807
+msgid "Follows"
+msgstr "Segue"
+
+#: gitk:4810
+msgid "Precedes"
+msgstr "Precede"
+
+#: gitk:5093
+msgid "Error getting merge diffs:"
+msgstr "Errore nella lettura delle differenze di fusione:"
+
+#: gitk:5778
+msgid "Goto:"
+msgstr "Vai a:"
+
+#: gitk:5780
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:5805
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "La SHA1 id abbreviata %s è ambigua"
+
+#: gitk:5817
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "La SHA1 id %s è sconosciuta"
+
+#: gitk:5819
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "L'etichetta/ramo %s è sconosciuto"
+
+#: gitk:5961
+msgid "Children"
+msgstr "Figli"
+
+#: gitk:6018
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Aggiorna il ramo %s a questa revisione"
+
+#: gitk:6049
+msgid "Top"
+msgstr "Inizio"
+
+#: gitk:6050
+msgid "From"
+msgstr "Da"
+
+#: gitk:6055
+msgid "To"
+msgstr "A"
+
+#: gitk:6078
+msgid "Generate patch"
+msgstr "Genera patch"
+
+#: gitk:6080
+msgid "From:"
+msgstr "Da:"
+
+#: gitk:6089
+msgid "To:"
+msgstr "A:"
+
+#: gitk:6098
+msgid "Reverse"
+msgstr "Inverti"
+
+#: gitk:6100 gitk:6269
+msgid "Output file:"
+msgstr "Scrivi sul file:"
+
+#: gitk:6106
+msgid "Generate"
+msgstr "Genera"
+
+#: gitk:6142
+msgid "Error creating patch:"
+msgstr "Errore nella creazione della patch:"
+
+#: gitk:6164 gitk:6257 gitk:6311
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:6173
+msgid "Tag name:"
+msgstr "Nome etichetta:"
+
+#: gitk:6177 gitk:6320
+msgid "Create"
+msgstr "Crea"
+
+#: gitk:6192
+msgid "No tag name specified"
+msgstr "Nessuna etichetta specificata"
+
+#: gitk:6196
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "L'etichetta \"%s\" esiste già"
+
+#: gitk:6202
+msgid "Error creating tag:"
+msgstr "Errore nella creazione dell'etichetta:"
+
+#: gitk:6266
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:6274
+msgid "Write"
+msgstr "Scrivi"
+
+#: gitk:6290
+msgid "Error writing commit:"
+msgstr "Errore nella scrittura della revisione:"
+
+#: gitk:6316
+msgid "Name:"
+msgstr "Nome:"
+
+#: gitk:6335
+msgid "Please specify a name for the new branch"
+msgstr "Specificare un nome per il nuovo ramo"
+
+#: gitk:6364
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?"
+
+#: gitk:6369
+msgid "Cherry-picking"
+msgstr ""
+
+#: gitk:6381
+msgid "No changes committed"
+msgstr "Nessuna modifica archiviata"
+
+#: gitk:6404
+msgid "Confirm reset"
+msgstr "Conferma git reset"
+
+#: gitk:6406
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Aggiornare il ramo %s a %s?"
+
+#: gitk:6410
+msgid "Reset type:"
+msgstr "Tipo di aggiornamento:"
+
+#: gitk:6414
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono"
+
+#: gitk:6417
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice"
+
+#: gitk:6420
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: Aggiorna la directory di lavoro e l'indice\n"
+"(abbandona TUTTE le modifiche locali)"
+
+#: gitk:6436
+msgid "Resetting"
+msgstr "git reset in corso"
+
+#: gitk:6493
+msgid "Checking out"
+msgstr "Attivazione in corso"
+
+#: gitk:6523
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Impossibile cancellare il ramo attualmente attivo"
+
+#: gitk:6529
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Le revisioni nel ramo %s non sono presenti su altri rami.\n"
+"Cancellare il ramo %s?"
+
+#: gitk:6560
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Etichette e rami: %s"
+
+#: gitk:6574
+msgid "Filter"
+msgstr "Filtro"
+
+#: gitk:6868
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Errore nella lettura della topologia delle revisioni: le informazioni sul "
+"ramo e le etichette precedenti e seguenti saranno incomplete."
+
+#: gitk:7852
+msgid "Tag"
+msgstr "Etichetta"
+
+#: gitk:7852
+msgid "Id"
+msgstr "Id"
+
+#: gitk:7892
+msgid "Gitk font chooser"
+msgstr "Scelta caratteri gitk"
+
+#: gitk:7909
+msgid "B"
+msgstr "B"
+
+#: gitk:7912
+msgid "I"
+msgstr "I"
+
+#: gitk:8005
+msgid "Gitk preferences"
+msgstr "Preferenze gitk"
+
+#: gitk:8006
+msgid "Commit list display options"
+msgstr "Opzioni visualizzazione dell'elenco revisioni"
+
+#: gitk:8009
+msgid "Maximum graph width (lines)"
+msgstr "Larghezza massima del grafico (in linee)"
+
+#: gitk:8013
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Larghezza massima del grafico (% del pannello)"
+
+#: gitk:8018
+msgid "Show local changes"
+msgstr "Mostra modifiche locali"
+
+#: gitk:8023
+msgid "Auto-select SHA1"
+msgstr "Seleziona automaticamente SHA1 hash"
+
+#: gitk:8028
+msgid "Diff display options"
+msgstr "Opzioni di visualizzazione delle differenze"
+
+#: gitk:8030
+msgid "Tab spacing"
+msgstr "Spaziatura tabulazioni"
+
+#: gitk:8034
+msgid "Display nearby tags"
+msgstr "Mostra etichette vicine"
+
+#: gitk:8039
+msgid "Limit diffs to listed paths"
+msgstr "Limita le differenze ai percorsi elencati"
+
+#: gitk:8044
+msgid "Colors: press to choose"
+msgstr "Colori: premere per scegliere"
+
+#: gitk:8047
+msgid "Background"
+msgstr "Sfondo"
+
+#: gitk:8051
+msgid "Foreground"
+msgstr "Primo piano"
+
+#: gitk:8055
+msgid "Diff: old lines"
+msgstr "Diff: vecchie linee"
+
+#: gitk:8060
+msgid "Diff: new lines"
+msgstr "Diff: nuove linee"
+
+#: gitk:8065
+msgid "Diff: hunk header"
+msgstr "Diff: intestazione della sezione"
+
+#: gitk:8071
+msgid "Select bg"
+msgstr "Sfondo selezione"
+
+#: gitk:8075
+msgid "Fonts: press to choose"
+msgstr "Carattere: premere per scegliere"
+
+#: gitk:8077
+msgid "Main font"
+msgstr "Carattere principale"
+
+#: gitk:8078
+msgid "Diff display font"
+msgstr "Carattere per differenze"
+
+#: gitk:8079
+msgid "User interface font"
+msgstr "Carattere per interfaccia utente"
+
+#: gitk:8095
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: scegliere un colore per %s"
+
+#: gitk:8476
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Questa versione di Tcl/Tk non può avviare gitk.\n"
+" Gitk richiede Tcl/Tk versione 8.4 o superiore."
+
+#: gitk:8565
+msgid "Cannot find a git repository here."
+msgstr "Archivio git non trovato."
+
+#: gitk:8569
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Directory git \"%s\" non trovata."
+
+#: gitk:8612
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file"
+
+#: gitk:8624
+msgid "Bad arguments to gitk:"
+msgstr "Gitk: argomenti errati:"
+
+#: gitk:8636
+msgid "Couldn't get list of unmerged files:"
+msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
+
+#: gitk:8652
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
+"sono file in attesa di fusione."
+
+#: gitk:8655
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
+"specificati non sono in attesa di fusione."
+
+#: gitk:8716
+msgid "Command line"
+msgstr "Linea di comando"
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 922dee98b9..ec73cb1256 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -5305,51 +5305,19 @@ sub git_search {
print "<table class=\"pickaxe search\">\n";
my $alternate = 1;
$/ = "\n";
- my $git_command = git_cmd_str();
- my $searchqtext = $searchtext;
- $searchqtext =~ s/'/'\\''/;
- my $pickaxe_flags = $search_use_regexp ? '--pickaxe-regex' : '';
- open my $fd, "-|", "$git_command rev-list $hash | " .
- "$git_command diff-tree -r --stdin -S\'$searchqtext\' $pickaxe_flags";
+ open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
+ '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
+ ($search_use_regexp ? '--pickaxe-regex' : ());
undef %co;
my @files;
while (my $line = <$fd>) {
- if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
- my %set;
- $set{'file'} = $6;
- $set{'from_id'} = $3;
- $set{'to_id'} = $4;
- $set{'id'} = $set{'to_id'};
- if ($set{'id'} =~ m/0{40}/) {
- $set{'id'} = $set{'from_id'};
- }
- if ($set{'id'} =~ m/0{40}/) {
- next;
- }
- push @files, \%set;
- } elsif ($line =~ m/^([0-9a-fA-F]{40})$/){
+ chomp $line;
+ next unless $line;
+
+ my %set = parse_difftree_raw_line($line);
+ if (defined $set{'commit'}) {
+ # finish previous commit
if (%co) {
- if ($alternate) {
- print "<tr class=\"dark\">\n";
- } else {
- print "<tr class=\"light\">\n";
- }
- $alternate ^= 1;
- my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
- print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
- "<td><i>" . $author . "</i></td>\n" .
- "<td>" .
- $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
- -class => "list subject"},
- chop_and_escape_str($co{'title'}, 50) . "<br/>");
- while (my $setref = shift @files) {
- my %set = %$setref;
- print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
- hash=>$set{'id'}, file_name=>$set{'file'}),
- -class => "list"},
- "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
- "<br/>\n";
- }
print "</td>\n" .
"<td class=\"link\">" .
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
@@ -5358,11 +5326,44 @@ sub git_search {
print "</td>\n" .
"</tr>\n";
}
- %co = parse_commit($1);
+
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ %co = parse_commit($set{'commit'});
+ my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+ "<td><i>$author</i></td>\n" .
+ "<td>" .
+ $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+ -class => "list subject"},
+ chop_and_escape_str($co{'title'}, 50) . "<br/>");
+ } elsif (defined $set{'to_id'}) {
+ next if ($set{'to_id'} =~ m/^0{40}$/);
+
+ print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
+ hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
+ -class => "list"},
+ "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
+ "<br/>\n";
}
}
close $fd;
+ # finish last commit (warning: repetition!)
+ if (%co) {
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
+ " | " .
+ $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
+ print "</td>\n" .
+ "</tr>\n";
+ }
+
print "</table>\n";
}
diff --git a/hash.c b/hash.c
index d9ec82fa66..1cd4c9d5c0 100644
--- a/hash.c
+++ b/hash.c
@@ -9,7 +9,7 @@
* the existing entry, or the empty slot if none existed. The caller
* can then look at the (*ptr) to see whether it existed or not.
*/
-static struct hash_table_entry *lookup_hash_entry(unsigned int hash, struct hash_table *table)
+static struct hash_table_entry *lookup_hash_entry(unsigned int hash, const struct hash_table *table)
{
unsigned int size = table->size, nr = hash % size;
struct hash_table_entry *array = table->array;
@@ -66,7 +66,7 @@ static void grow_hash_table(struct hash_table *table)
free(old_array);
}
-void *lookup_hash(unsigned int hash, struct hash_table *table)
+void *lookup_hash(unsigned int hash, const struct hash_table *table)
{
if (!table->array)
return NULL;
@@ -81,7 +81,7 @@ void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table)
return insert_hash_entry(hash, ptr, table);
}
-int for_each_hash(struct hash_table *table, int (*fn)(void *))
+int for_each_hash(const struct hash_table *table, int (*fn)(void *))
{
int sum = 0;
unsigned int i;
diff --git a/hash.h b/hash.h
index a8b0fbb5b5..69e33a47b9 100644
--- a/hash.h
+++ b/hash.h
@@ -28,9 +28,9 @@ struct hash_table {
struct hash_table_entry *array;
};
-extern void *lookup_hash(unsigned int hash, struct hash_table *table);
+extern void *lookup_hash(unsigned int hash, const struct hash_table *table);
extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table);
-extern int for_each_hash(struct hash_table *table, int (*fn)(void *));
+extern int for_each_hash(const struct hash_table *table, int (*fn)(void *));
extern void free_hash(struct hash_table *table);
static inline void init_hash(struct hash_table *table)
diff --git a/help.c b/help.c
index e57a50ed59..ecaca770d3 100644
--- a/help.c
+++ b/help.c
@@ -8,6 +8,12 @@
#include "exec_cmd.h"
#include "common-cmds.h"
#include "parse-options.h"
+#include "run-command.h"
+
+static struct man_viewer_list {
+ void (*exec)(const char *);
+ struct man_viewer_list *next;
+} *man_viewer_list;
enum help_format {
HELP_FORMAT_MAN,
@@ -42,6 +48,102 @@ static enum help_format parse_help_format(const char *format)
die("unrecognized help format '%s'", format);
}
+static int check_emacsclient_version(void)
+{
+ struct strbuf buffer = STRBUF_INIT;
+ struct child_process ec_process;
+ const char *argv_ec[] = { "emacsclient", "--version", NULL };
+ int version;
+
+ /* emacsclient prints its version number on stderr */
+ memset(&ec_process, 0, sizeof(ec_process));
+ ec_process.argv = argv_ec;
+ ec_process.err = -1;
+ ec_process.stdout_to_stderr = 1;
+ if (start_command(&ec_process)) {
+ fprintf(stderr, "Failed to start emacsclient.\n");
+ return -1;
+ }
+ strbuf_read(&buffer, ec_process.err, 20);
+ close(ec_process.err);
+
+ /*
+ * Don't bother checking return value, because "emacsclient --version"
+ * seems to always exits with code 1.
+ */
+ finish_command(&ec_process);
+
+ if (prefixcmp(buffer.buf, "emacsclient")) {
+ fprintf(stderr, "Failed to parse emacsclient version.\n");
+ strbuf_release(&buffer);
+ return -1;
+ }
+
+ strbuf_remove(&buffer, 0, strlen("emacsclient"));
+ version = atoi(buffer.buf);
+
+ if (version < 22) {
+ fprintf(stderr,
+ "emacsclient version '%d' too old (< 22).\n",
+ version);
+ strbuf_release(&buffer);
+ return -1;
+ }
+
+ strbuf_release(&buffer);
+ return 0;
+}
+
+static void exec_woman_emacs(const char *page)
+{
+ if (!check_emacsclient_version()) {
+ /* This works only with emacsclient version >= 22. */
+ struct strbuf man_page = STRBUF_INIT;
+ strbuf_addf(&man_page, "(woman \"%s\")", page);
+ execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL);
+ }
+}
+
+static void exec_man_konqueror(const char *page)
+{
+ const char *display = getenv("DISPLAY");
+ if (display && *display) {
+ struct strbuf man_page = STRBUF_INIT;
+ strbuf_addf(&man_page, "man:%s(1)", page);
+ execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL);
+ }
+}
+
+static void exec_man_man(const char *page)
+{
+ execlp("man", "man", page, NULL);
+}
+
+static void do_add_man_viewer(void (*exec)(const char *))
+{
+ struct man_viewer_list **p = &man_viewer_list;
+
+ while (*p)
+ p = &((*p)->next);
+ *p = xmalloc(sizeof(**p));
+ (*p)->next = NULL;
+ (*p)->exec = exec;
+}
+
+static int add_man_viewer(const char *value)
+{
+ if (!strcasecmp(value, "man"))
+ do_add_man_viewer(exec_man_man);
+ else if (!strcasecmp(value, "woman"))
+ do_add_man_viewer(exec_woman_emacs);
+ else if (!strcasecmp(value, "konqueror"))
+ do_add_man_viewer(exec_man_konqueror);
+ else
+ warning("'%s': unsupported man viewer.", value);
+
+ return 0;
+}
+
static int git_help_config(const char *var, const char *value)
{
if (!strcmp(var, "help.format")) {
@@ -50,6 +152,11 @@ static int git_help_config(const char *var, const char *value)
help_format = parse_help_format(value);
return 0;
}
+ if (!strcmp(var, "man.viewer")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return add_man_viewer(value);
+ }
return git_default_config(var, value);
}
@@ -347,9 +454,16 @@ static void setup_man_path(void)
static void show_man_page(const char *git_cmd)
{
+ struct man_viewer_list *viewer;
const char *page = cmd_to_page(git_cmd);
+
setup_man_path();
- execlp("man", "man", page, NULL);
+ for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+ {
+ viewer->exec(page); /* will return when unable */
+ }
+ exec_man_man(page);
+ die("no man viewer handled the request");
}
static void show_info_page(const char *git_cmd)
diff --git a/http.c b/http.c
index 8e554c0969..256a5f15f4 100644
--- a/http.c
+++ b/http.c
@@ -284,23 +284,15 @@ void http_init(struct remote *remote)
void http_cleanup(void)
{
struct active_request_slot *slot = active_queue_head;
-#ifdef USE_CURL_MULTI
- char *wait_url;
-#endif
while (slot != NULL) {
struct active_request_slot *next = slot->next;
+ if (slot->curl != NULL) {
#ifdef USE_CURL_MULTI
- if (slot->in_use) {
- curl_easy_getinfo(slot->curl,
- CURLINFO_EFFECTIVE_URL,
- &wait_url);
- fprintf(stderr, "Waiting for %s\n", wait_url);
- run_active_slot(slot);
- }
+ curl_multi_remove_handle(curlm, slot->curl);
#endif
- if (slot->curl != NULL)
curl_easy_cleanup(slot->curl);
+ }
free(slot);
slot = next;
}
diff --git a/ident.c b/ident.c
index b839dcf5f0..ed44a5345a 100644
--- a/ident.c
+++ b/ident.c
@@ -171,7 +171,7 @@ static const char au_env[] = "GIT_AUTHOR_NAME";
static const char co_env[] = "GIT_COMMITTER_NAME";
static const char *env_hint =
"\n"
-"*** Your name cannot be determined from your system services (gecos).\n"
+"*** Please tell me who you are.\n"
"\n"
"Run\n"
"\n"
diff --git a/ll-merge.c b/ll-merge.c
new file mode 100644
index 0000000000..5ae74331bc
--- /dev/null
+++ b/ll-merge.c
@@ -0,0 +1,379 @@
+/*
+ * Low level 3-way in-core file merge.
+ *
+ * Copyright (c) 2007 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "attr.h"
+#include "xdiff-interface.h"
+#include "run-command.h"
+#include "interpolate.h"
+#include "ll-merge.h"
+
+struct ll_merge_driver;
+
+typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
+ mmbuffer_t *result,
+ const char *path,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ int virtual_ancestor);
+
+struct ll_merge_driver {
+ const char *name;
+ const char *description;
+ ll_merge_fn fn;
+ const char *recursive;
+ struct ll_merge_driver *next;
+ char *cmdline;
+};
+
+/*
+ * Built-in low-levels
+ */
+static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
+ mmbuffer_t *result,
+ const char *path_unused,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ int virtual_ancestor)
+{
+ /*
+ * The tentative merge result is "ours" for the final round,
+ * or common ancestor for an internal merge. Still return
+ * "conflicted merge" status.
+ */
+ mmfile_t *stolen = virtual_ancestor ? orig : src1;
+
+ result->ptr = stolen->ptr;
+ result->size = stolen->size;
+ stolen->ptr = NULL;
+ return 1;
+}
+
+static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
+ mmbuffer_t *result,
+ const char *path_unused,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ int virtual_ancestor)
+{
+ xpparam_t xpp;
+
+ if (buffer_is_binary(orig->ptr, orig->size) ||
+ buffer_is_binary(src1->ptr, src1->size) ||
+ buffer_is_binary(src2->ptr, src2->size)) {
+ warning("Cannot merge binary files: %s vs. %s\n",
+ name1, name2);
+ return ll_binary_merge(drv_unused, result,
+ path_unused,
+ orig, src1, name1,
+ src2, name2,
+ virtual_ancestor);
+ }
+
+ memset(&xpp, 0, sizeof(xpp));
+ return xdl_merge(orig,
+ src1, name1,
+ src2, name2,
+ &xpp, XDL_MERGE_ZEALOUS,
+ result);
+}
+
+static int ll_union_merge(const struct ll_merge_driver *drv_unused,
+ mmbuffer_t *result,
+ const char *path_unused,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ int virtual_ancestor)
+{
+ char *src, *dst;
+ long size;
+ const int marker_size = 7;
+
+ int status = ll_xdl_merge(drv_unused, result, path_unused,
+ orig, src1, NULL, src2, NULL,
+ virtual_ancestor);
+ if (status <= 0)
+ return status;
+ size = result->size;
+ src = dst = result->ptr;
+ while (size) {
+ char ch;
+ if ((marker_size < size) &&
+ (*src == '<' || *src == '=' || *src == '>')) {
+ int i;
+ ch = *src;
+ for (i = 0; i < marker_size; i++)
+ if (src[i] != ch)
+ goto not_a_marker;
+ if (src[marker_size] != '\n')
+ goto not_a_marker;
+ src += marker_size + 1;
+ size -= marker_size + 1;
+ continue;
+ }
+ not_a_marker:
+ do {
+ ch = *src++;
+ *dst++ = ch;
+ size--;
+ } while (ch != '\n' && size);
+ }
+ result->size = dst - result->ptr;
+ return 0;
+}
+
+#define LL_BINARY_MERGE 0
+#define LL_TEXT_MERGE 1
+#define LL_UNION_MERGE 2
+static struct ll_merge_driver ll_merge_drv[] = {
+ { "binary", "built-in binary merge", ll_binary_merge },
+ { "text", "built-in 3-way text merge", ll_xdl_merge },
+ { "union", "built-in union merge", ll_union_merge },
+};
+
+static void create_temp(mmfile_t *src, char *path)
+{
+ int fd;
+
+ strcpy(path, ".merge_file_XXXXXX");
+ fd = xmkstemp(path);
+ if (write_in_full(fd, src->ptr, src->size) != src->size)
+ die("unable to write temp-file");
+ close(fd);
+}
+
+/*
+ * User defined low-level merge driver support.
+ */
+static int ll_ext_merge(const struct ll_merge_driver *fn,
+ mmbuffer_t *result,
+ const char *path,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ int virtual_ancestor)
+{
+ char temp[3][50];
+ char cmdbuf[2048];
+ struct interp table[] = {
+ { "%O" },
+ { "%A" },
+ { "%B" },
+ };
+ struct child_process child;
+ const char *args[20];
+ int status, fd, i;
+ struct stat st;
+
+ if (fn->cmdline == NULL)
+ die("custom merge driver %s lacks command line.", fn->name);
+
+ result->ptr = NULL;
+ result->size = 0;
+ create_temp(orig, temp[0]);
+ create_temp(src1, temp[1]);
+ create_temp(src2, temp[2]);
+
+ interp_set_entry(table, 0, temp[0]);
+ interp_set_entry(table, 1, temp[1]);
+ interp_set_entry(table, 2, temp[2]);
+
+ interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
+
+ memset(&child, 0, sizeof(child));
+ child.argv = args;
+ args[0] = "sh";
+ args[1] = "-c";
+ args[2] = cmdbuf;
+ args[3] = NULL;
+
+ status = run_command(&child);
+ if (status < -ERR_RUN_COMMAND_FORK)
+ ; /* failure in run-command */
+ else
+ status = -status;
+ fd = open(temp[1], O_RDONLY);
+ if (fd < 0)
+ goto bad;
+ if (fstat(fd, &st))
+ goto close_bad;
+ result->size = st.st_size;
+ result->ptr = xmalloc(result->size + 1);
+ if (read_in_full(fd, result->ptr, result->size) != result->size) {
+ free(result->ptr);
+ result->ptr = NULL;
+ result->size = 0;
+ }
+ close_bad:
+ close(fd);
+ bad:
+ for (i = 0; i < 3; i++)
+ unlink(temp[i]);
+ return status;
+}
+
+/*
+ * merge.default and merge.driver configuration items
+ */
+static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
+static const char *default_ll_merge;
+
+static int read_merge_config(const char *var, const char *value)
+{
+ struct ll_merge_driver *fn;
+ const char *ep, *name;
+ int namelen;
+
+ if (!strcmp(var, "merge.default")) {
+ if (value)
+ default_ll_merge = strdup(value);
+ return 0;
+ }
+
+ /*
+ * We are not interested in anything but "merge.<name>.variable";
+ * especially, we do not want to look at variables such as
+ * "merge.summary", "merge.tool", and "merge.verbosity".
+ */
+ if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
+ return 0;
+
+ /*
+ * Find existing one as we might be processing merge.<name>.var2
+ * after seeing merge.<name>.var1.
+ */
+ name = var + 6;
+ namelen = ep - name;
+ for (fn = ll_user_merge; fn; fn = fn->next)
+ if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
+ break;
+ if (!fn) {
+ fn = xcalloc(1, sizeof(struct ll_merge_driver));
+ fn->name = xmemdupz(name, namelen);
+ fn->fn = ll_ext_merge;
+ *ll_user_merge_tail = fn;
+ ll_user_merge_tail = &(fn->next);
+ }
+
+ ep++;
+
+ if (!strcmp("name", ep)) {
+ if (!value)
+ return error("%s: lacks value", var);
+ fn->description = strdup(value);
+ return 0;
+ }
+
+ if (!strcmp("driver", ep)) {
+ if (!value)
+ return error("%s: lacks value", var);
+ /*
+ * merge.<name>.driver specifies the command line:
+ *
+ * command-line
+ *
+ * The command-line will be interpolated with the following
+ * tokens and is given to the shell:
+ *
+ * %O - temporary file name for the merge base.
+ * %A - temporary file name for our version.
+ * %B - temporary file name for the other branches' version.
+ *
+ * The external merge driver should write the results in the
+ * file named by %A, and signal that it has done with zero exit
+ * status.
+ */
+ fn->cmdline = strdup(value);
+ return 0;
+ }
+
+ if (!strcmp("recursive", ep)) {
+ if (!value)
+ return error("%s: lacks value", var);
+ fn->recursive = strdup(value);
+ return 0;
+ }
+
+ return 0;
+}
+
+static void initialize_ll_merge(void)
+{
+ if (ll_user_merge_tail)
+ return;
+ ll_user_merge_tail = &ll_user_merge;
+ git_config(read_merge_config);
+}
+
+static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
+{
+ struct ll_merge_driver *fn;
+ const char *name;
+ int i;
+
+ initialize_ll_merge();
+
+ if (ATTR_TRUE(merge_attr))
+ return &ll_merge_drv[LL_TEXT_MERGE];
+ else if (ATTR_FALSE(merge_attr))
+ return &ll_merge_drv[LL_BINARY_MERGE];
+ else if (ATTR_UNSET(merge_attr)) {
+ if (!default_ll_merge)
+ return &ll_merge_drv[LL_TEXT_MERGE];
+ else
+ name = default_ll_merge;
+ }
+ else
+ name = merge_attr;
+
+ for (fn = ll_user_merge; fn; fn = fn->next)
+ if (!strcmp(fn->name, name))
+ return fn;
+
+ for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
+ if (!strcmp(ll_merge_drv[i].name, name))
+ return &ll_merge_drv[i];
+
+ /* default to the 3-way */
+ return &ll_merge_drv[LL_TEXT_MERGE];
+}
+
+static const char *git_path_check_merge(const char *path)
+{
+ static struct git_attr_check attr_merge_check;
+
+ if (!attr_merge_check.attr)
+ attr_merge_check.attr = git_attr("merge", 5);
+
+ if (git_checkattr(path, 1, &attr_merge_check))
+ return NULL;
+ return attr_merge_check.value;
+}
+
+int ll_merge(mmbuffer_t *result_buf,
+ const char *path,
+ mmfile_t *ancestor,
+ mmfile_t *ours, const char *our_label,
+ mmfile_t *theirs, const char *their_label,
+ int virtual_ancestor)
+{
+ const char *ll_driver_name;
+ const struct ll_merge_driver *driver;
+
+ ll_driver_name = git_path_check_merge(path);
+ driver = find_ll_merge_driver(ll_driver_name);
+
+ if (virtual_ancestor && driver->recursive)
+ driver = find_ll_merge_driver(driver->recursive);
+ return driver->fn(driver, result_buf, path,
+ ancestor,
+ ours, our_label,
+ theirs, their_label, virtual_ancestor);
+}
diff --git a/ll-merge.h b/ll-merge.h
new file mode 100644
index 0000000000..5388422d09
--- /dev/null
+++ b/ll-merge.h
@@ -0,0 +1,15 @@
+/*
+ * Low level 3-way in-core file merge.
+ */
+
+#ifndef LL_MERGE_H
+#define LL_MERGE_H
+
+int ll_merge(mmbuffer_t *result_buf,
+ const char *path,
+ mmfile_t *ancestor,
+ mmfile_t *ours, const char *our_label,
+ mmfile_t *theirs, const char *their_label,
+ int virtual_ancestor);
+
+#endif
diff --git a/log-tree.c b/log-tree.c
index 608f697cf3..5b2963998c 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -138,10 +138,14 @@ static int has_non_ascii(const char *s)
}
void log_write_email_headers(struct rev_info *opt, const char *name,
- const char **subject_p, const char **extra_headers_p)
+ const char **subject_p,
+ const char **extra_headers_p,
+ int *need_8bit_cte_p)
{
const char *subject = NULL;
const char *extra_headers = opt->extra_headers;
+
+ *need_8bit_cte_p = 0; /* unknown */
if (opt->total > 0) {
static char buffer[64];
snprintf(buffer, sizeof(buffer),
@@ -169,6 +173,7 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
if (opt->mime_boundary) {
static char subject_buffer[1024];
static char buffer[1024];
+ *need_8bit_cte_p = -1; /* NEVER */
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
"%s"
"MIME-Version: 1.0\n"
@@ -212,6 +217,7 @@ void show_log(struct rev_info *opt, const char *sep)
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
const char *extra;
const char *subject = NULL, *extra_headers = opt->extra_headers;
+ int need_8bit_cte = 0;
opt->loginfo = NULL;
if (!opt->verbose_header) {
@@ -255,7 +261,8 @@ void show_log(struct rev_info *opt, const char *sep)
if (opt->commit_format == CMIT_FMT_EMAIL) {
log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
- &subject, &extra_headers);
+ &subject, &extra_headers,
+ &need_8bit_cte);
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
if (opt->commit_format != CMIT_FMT_ONELINE)
@@ -299,9 +306,11 @@ void show_log(struct rev_info *opt, const char *sep)
* And then the pretty-printed message itself
*/
strbuf_init(&msgbuf, 0);
+ if (need_8bit_cte >= 0)
+ need_8bit_cte = has_non_ascii(opt->add_signoff);
pretty_print_commit(opt->commit_format, commit, &msgbuf,
abbrev, subject, extra_headers, opt->date_mode,
- has_non_ascii(opt->add_signoff));
+ need_8bit_cte);
if (opt->add_signoff)
append_signoff(&msgbuf, opt->add_signoff);
diff --git a/log-tree.h b/log-tree.h
index 0cc9344eab..8946ff377c 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -14,6 +14,8 @@ int log_tree_opt_parse(struct rev_info *, const char **, int);
void show_log(struct rev_info *opt, const char *sep);
void show_decorations(struct commit *commit);
void log_write_email_headers(struct rev_info *opt, const char *name,
- const char **subject_p, const char **extra_headers_p);
+ const char **subject_p,
+ const char **extra_headers_p,
+ int *need_8bit_cte_p);
#endif
diff --git a/merge-tree.c b/merge-tree.c
index e08324686c..02fc10f7e6 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -168,7 +168,13 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsi
return res;
}
-static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
+static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
+{
+ char *path = xmalloc(traverse_path_len(info, n) + 1);
+ return make_traverse_path(path, info, n);
+}
+
+static void resolve(const struct traverse_info *info, struct name_entry *branch1, struct name_entry *result)
{
struct merge_list *orig, *final;
const char *path;
@@ -177,7 +183,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
if (!branch1)
return;
- path = xstrdup(mkpath("%s%s", base, result->path));
+ path = traverse_path(info, result);
orig = create_entry(2, branch1->mode, branch1->sha1, path);
final = create_entry(0, result->mode, result->sha1, path);
@@ -186,9 +192,8 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
add_merge_entry(final);
}
-static int unresolved_directory(const char *base, struct name_entry n[3])
+static int unresolved_directory(const struct traverse_info *info, struct name_entry n[3])
{
- int baselen, pathlen;
char *newbase;
struct name_entry *p;
struct tree_desc t[3];
@@ -204,13 +209,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
}
if (!S_ISDIR(p->mode))
return 0;
- baselen = strlen(base);
- pathlen = tree_entry_len(p->path, p->sha1);
- newbase = xmalloc(baselen + pathlen + 2);
- memcpy(newbase, base, baselen);
- memcpy(newbase + baselen, p->path, pathlen);
- memcpy(newbase + baselen + pathlen, "/", 2);
-
+ newbase = traverse_path(info, p);
buf0 = fill_tree_descriptor(t+0, n[0].sha1);
buf1 = fill_tree_descriptor(t+1, n[1].sha1);
buf2 = fill_tree_descriptor(t+2, n[2].sha1);
@@ -224,7 +223,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
}
-static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry)
+static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry)
{
const char *path;
struct merge_list *link;
@@ -234,17 +233,17 @@ static struct merge_list *link_entry(unsigned stage, const char *base, struct na
if (entry)
path = entry->path;
else
- path = xstrdup(mkpath("%s%s", base, n->path));
+ path = traverse_path(info, n);
link = create_entry(stage, n->mode, n->sha1, path);
link->link = entry;
return link;
}
-static void unresolved(const char *base, struct name_entry n[3])
+static void unresolved(const struct traverse_info *info, struct name_entry n[3])
{
struct merge_list *entry = NULL;
- if (unresolved_directory(base, n))
+ if (unresolved_directory(info, n))
return;
/*
@@ -252,9 +251,9 @@ static void unresolved(const char *base, struct name_entry n[3])
* list has the stages in order - link_entry adds new
* links at the front.
*/
- entry = link_entry(3, base, n + 2, entry);
- entry = link_entry(2, base, n + 1, entry);
- entry = link_entry(1, base, n + 0, entry);
+ entry = link_entry(3, info, n + 2, entry);
+ entry = link_entry(2, info, n + 1, entry);
+ entry = link_entry(1, info, n + 0, entry);
add_merge_entry(entry);
}
@@ -288,36 +287,41 @@ static void unresolved(const char *base, struct name_entry n[3])
* The successful merge rules are the same as for the three-way merge
* in git-read-tree.
*/
-static void threeway_callback(int n, unsigned long mask, struct name_entry *entry, const char *base)
+static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
{
/* Same in both? */
if (same_entry(entry+1, entry+2)) {
if (entry[0].sha1) {
- resolve(base, NULL, entry+1);
- return;
+ resolve(info, NULL, entry+1);
+ return mask;
}
}
if (same_entry(entry+0, entry+1)) {
if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
- resolve(base, entry+1, entry+2);
- return;
+ resolve(info, entry+1, entry+2);
+ return mask;
}
}
if (same_entry(entry+0, entry+2)) {
if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
- resolve(base, NULL, entry+1);
- return;
+ resolve(info, NULL, entry+1);
+ return mask;
}
}
- unresolved(base, entry);
+ unresolved(info, entry);
+ return mask;
}
static void merge_trees(struct tree_desc t[3], const char *base)
{
- traverse_trees(3, t, base, threeway_callback);
+ struct traverse_info info;
+
+ setup_traverse_info(&info, base);
+ info.fn = threeway_callback;
+ traverse_trees(3, t, &info);
}
static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
diff --git a/parse-options.c b/parse-options.c
index d9562ba504..8e64316fe0 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -6,7 +6,8 @@
struct optparse_t {
const char **argv;
- int argc;
+ const char **out;
+ int argc, cpidx;
const char *opt;
};
@@ -159,6 +160,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
continue;
rest = skip_prefix(arg, options->long_name);
+ if (options->type == OPTION_ARGUMENT) {
+ if (!rest)
+ continue;
+ if (*rest == '=')
+ return opterror(options, "takes no value", flags);
+ if (*rest)
+ continue;
+ p->out[p->cpidx++] = arg - 2;
+ return 0;
+ }
if (!rest) {
/* abbreviated? */
if (!strncmp(options->long_name, arg, arg_end - arg)) {
@@ -242,14 +253,15 @@ static NORETURN void usage_with_options_internal(const char * const *,
int parse_options(int argc, const char **argv, const struct option *options,
const char * const usagestr[], int flags)
{
- struct optparse_t args = { argv + 1, argc - 1, NULL };
- int j = 0;
+ struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
for (; args.argc; args.argc--, args.argv++) {
const char *arg = args.argv[0];
if (*arg != '-' || !arg[1]) {
- argv[j++] = args.argv[0];
+ if (flags & PARSE_OPT_STOP_AT_NON_OPTION)
+ break;
+ args.out[args.cpidx++] = args.argv[0];
continue;
}
@@ -286,9 +298,9 @@ int parse_options(int argc, const char **argv, const struct option *options,
usage_with_options(usagestr, options);
}
- memmove(argv + j, args.argv, args.argc * sizeof(*argv));
- argv[j + args.argc] = NULL;
- return j + args.argc;
+ memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
+ args.out[args.cpidx + args.argc] = NULL;
+ return args.cpidx + args.argc;
}
#define USAGE_OPTS_WIDTH 24
@@ -328,6 +340,8 @@ void usage_with_options_internal(const char * const *usagestr,
pos += fprintf(stderr, "--%s", opts->long_name);
switch (opts->type) {
+ case OPTION_ARGUMENT:
+ break;
case OPTION_INTEGER:
if (opts->flags & PARSE_OPT_OPTARG)
pos += fprintf(stderr, " [<n>]");
diff --git a/parse-options.h b/parse-options.h
index 102ac31fb7..1af62b0485 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -4,6 +4,7 @@
enum parse_opt_type {
/* special types */
OPTION_END,
+ OPTION_ARGUMENT,
OPTION_GROUP,
/* options with no arguments */
OPTION_BIT,
@@ -18,6 +19,7 @@ enum parse_opt_type {
enum parse_opt_flags {
PARSE_OPT_KEEP_DASHDASH = 1,
+ PARSE_OPT_STOP_AT_NON_OPTION = 2,
};
enum parse_opt_option_flags {
@@ -84,6 +86,7 @@ struct option {
};
#define OPT_END() { OPTION_END }
+#define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) }
#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
#define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
diff --git a/path-list.c b/path-list.c
index 3d83b7ba9e..92e5cf20fe 100644
--- a/path-list.c
+++ b/path-list.c
@@ -102,3 +102,33 @@ void print_path_list(const char *text, const struct path_list *p)
for (i = 0; i < p->nr; i++)
printf("%s:%p\n", p->items[i].path, p->items[i].util);
}
+
+struct path_list_item *path_list_append(const char *path, struct path_list *list)
+{
+ ALLOC_GROW(list->items, list->nr + 1, list->alloc);
+ list->items[list->nr].path =
+ list->strdup_paths ? xstrdup(path) : (char *)path;
+ return list->items + list->nr++;
+}
+
+static int cmp_items(const void *a, const void *b)
+{
+ const struct path_list_item *one = a;
+ const struct path_list_item *two = b;
+ return strcmp(one->path, two->path);
+}
+
+void sort_path_list(struct path_list *list)
+{
+ qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
+}
+
+int unsorted_path_list_has_path(struct path_list *list, const char *path)
+{
+ int i;
+ for (i = 0; i < list->nr; i++)
+ if (!strcmp(path, list->items[i].path))
+ return 1;
+ return 0;
+}
+
diff --git a/path-list.h b/path-list.h
index 5931e2cc0c..ca2cbbaa4d 100644
--- a/path-list.h
+++ b/path-list.h
@@ -13,10 +13,16 @@ struct path_list
};
void print_path_list(const char *text, const struct path_list *p);
+void path_list_clear(struct path_list *list, int free_util);
+/* Use these functions only on sorted lists: */
int path_list_has_path(const struct path_list *list, const char *path);
-void path_list_clear(struct path_list *list, int free_util);
struct path_list_item *path_list_insert(const char *path, struct path_list *list);
struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
+/* Use these functions only on unsorted lists: */
+struct path_list_item *path_list_append(const char *path, struct path_list *list);
+void sort_path_list(struct path_list *list);
+int unsorted_path_list_has_path(struct path_list *list, const char *path);
+
#endif /* PATH_LIST_H */
diff --git a/path.c b/path.c
index af2716199b..f4ed979997 100644
--- a/path.c
+++ b/path.c
@@ -283,7 +283,7 @@ int adjust_shared_perm(const char *path)
? (S_IXGRP|S_IXOTH)
: 0));
if (S_ISDIR(mode))
- mode |= S_ISGID;
+ mode |= FORCE_DIR_SET_GID;
if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
return -2;
return 0;
diff --git a/pretty.c b/pretty.c
index 703f52176b..16bfb86cd3 100644
--- a/pretty.c
+++ b/pretty.c
@@ -636,7 +636,7 @@ void pp_title_line(enum cmit_fmt fmt,
const char *subject,
const char *after_subject,
const char *encoding,
- int plain_non_ascii)
+ int need_8bit_cte)
{
struct strbuf title;
@@ -669,7 +669,7 @@ void pp_title_line(enum cmit_fmt fmt,
}
strbuf_addch(sb, '\n');
- if (plain_non_ascii) {
+ if (need_8bit_cte > 0) {
const char *header_fmt =
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=%s\n"
@@ -718,9 +718,9 @@ void pp_remainder(enum cmit_fmt fmt,
}
void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
- struct strbuf *sb, int abbrev,
- const char *subject, const char *after_subject,
- enum date_mode dmode, int plain_non_ascii)
+ struct strbuf *sb, int abbrev,
+ const char *subject, const char *after_subject,
+ enum date_mode dmode, int need_8bit_cte)
{
unsigned long beginning_of_body;
int indent = 4;
@@ -746,13 +746,11 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
indent = 0;
- /* After-subject is used to pass in Content-Type: multipart
- * MIME header; in that case we do not have to do the
- * plaintext content type even if the commit message has
- * non 7-bit ASCII character. Otherwise, check if we need
- * to say this is not a 7-bit ASCII.
+ /*
+ * We need to check and emit Content-type: to mark it
+ * as 8-bit if we haven't done so.
*/
- if (fmt == CMIT_FMT_EMAIL && !after_subject) {
+ if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
int i, ch, in_body;
for (in_body = i = 0; (ch = msg[i]); i++) {
@@ -765,7 +763,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
in_body = 1;
}
else if (non_ascii(ch)) {
- plain_non_ascii = 1;
+ need_8bit_cte = 1;
break;
}
}
@@ -790,7 +788,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
/* These formats treat the title line specially. */
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
pp_title_line(fmt, &msg, sb, subject,
- after_subject, encoding, plain_non_ascii);
+ after_subject, encoding, need_8bit_cte);
beginning_of_body = sb->len;
if (fmt != CMIT_FMT_ONELINE)
diff --git a/quote.c b/quote.c
index d061626c34..d5cf9d8f94 100644
--- a/quote.c
+++ b/quote.c
@@ -260,6 +260,48 @@ extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
fputc(terminator, fp);
}
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+ struct strbuf *out, const char *prefix)
+{
+ int needquote;
+
+ if (len < 0)
+ len = strlen(in);
+
+ /* "../" prefix itself does not need quoting, but "in" might. */
+ needquote = next_quote_pos(in, len) < len;
+ strbuf_setlen(out, 0);
+ strbuf_grow(out, len);
+
+ if (needquote)
+ strbuf_addch(out, '"');
+ if (prefix) {
+ int off = 0;
+ while (prefix[off] && off < len && prefix[off] == in[off])
+ if (prefix[off] == '/') {
+ prefix += off + 1;
+ in += off + 1;
+ len -= off + 1;
+ off = 0;
+ } else
+ off++;
+
+ for (; *prefix; prefix++)
+ if (*prefix == '/')
+ strbuf_addstr(out, "../");
+ }
+
+ quote_c_style_counted (in, len, out, NULL, 1);
+
+ if (needquote)
+ strbuf_addch(out, '"');
+ if (!out->len)
+ strbuf_addstr(out, "./");
+
+ return out->buf;
+}
+
/*
* C-style name unquoting.
*
@@ -288,7 +330,7 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
switch (*quoted++) {
case '"':
if (endp)
- *endp = quoted + 1;
+ *endp = quoted;
return 0;
case '\\':
break;
diff --git a/quote.h b/quote.h
index 4da110ec01..c5eea6f18e 100644
--- a/quote.h
+++ b/quote.h
@@ -47,6 +47,10 @@ extern void write_name_quoted(const char *name, FILE *, int terminator);
extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
const char *name, FILE *, int terminator);
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+ struct strbuf *out, const char *prefix);
+
/* quoting as a string literal for other languages */
extern void perl_quote_print(FILE *stream, const char *src);
extern void python_quote_print(FILE *stream, const char *src);
diff --git a/read-cache.c b/read-cache.c
index 657f0c5894..a92b25b59b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -255,13 +255,13 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
return changed;
}
-static int is_racy_timestamp(struct index_state *istate, struct cache_entry *ce)
+static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
{
return (istate->timestamp &&
((unsigned int)istate->timestamp) <= ce->ce_mtime);
}
-int ie_match_stat(struct index_state *istate,
+int ie_match_stat(const struct index_state *istate,
struct cache_entry *ce, struct stat *st,
unsigned int options)
{
@@ -304,7 +304,7 @@ int ie_match_stat(struct index_state *istate,
return changed;
}
-int ie_modified(struct index_state *istate,
+int ie_modified(const struct index_state *istate,
struct cache_entry *ce, struct stat *st, unsigned int options)
{
int changed, changed_fs;
@@ -351,6 +351,41 @@ int base_name_compare(const char *name1, int len1, int mode1,
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
+/*
+ * df_name_compare() is identical to base_name_compare(), except it
+ * compares conflicting directory/file entries as equal. Note that
+ * while a directory name compares as equal to a regular file, they
+ * then individually compare _differently_ to a filename that has
+ * a dot after the basename (because '\0' < '.' < '/').
+ *
+ * This is used by routines that want to traverse the git namespace
+ * but then handle conflicting entries together when possible.
+ */
+int df_name_compare(const char *name1, int len1, int mode1,
+ const char *name2, int len2, int mode2)
+{
+ int len = len1 < len2 ? len1 : len2, cmp;
+ unsigned char c1, c2;
+
+ cmp = memcmp(name1, name2, len);
+ if (cmp)
+ return cmp;
+ /* Directories and files compare equal (same length, same name) */
+ if (len1 == len2)
+ return 0;
+ c1 = name1[len];
+ if (!c1 && S_ISDIR(mode1))
+ c1 = '/';
+ c2 = name2[len];
+ if (!c2 && S_ISDIR(mode2))
+ c2 = '/';
+ if (c1 == '/' && !c2)
+ return 0;
+ if (c2 == '/' && !c1)
+ return 0;
+ return c1 - c2;
+}
+
int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
{
int len1 = flags1 & CE_NAMEMASK;
@@ -377,7 +412,7 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
return 0;
}
-int index_name_pos(struct index_state *istate, const char *name, int namelen)
+int index_name_pos(const struct index_state *istate, const char *name, int namelen)
{
int first, last;
@@ -1166,7 +1201,7 @@ int discard_index(struct index_state *istate)
return 0;
}
-int unmerged_index(struct index_state *istate)
+int unmerged_index(const struct index_state *istate)
{
int i;
for (i = 0; i < istate->cache_nr; i++) {
@@ -1311,7 +1346,7 @@ static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)
return ce_write(c, fd, ondisk, size);
}
-int write_index(struct index_state *istate, int newfd)
+int write_index(const struct index_state *istate, int newfd)
{
SHA_CTX c;
struct cache_header hdr;
diff --git a/receive-pack.c b/receive-pack.c
index de7e18c49e..f83ae87e15 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -10,7 +10,6 @@
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
static int deny_non_fast_forwards = 0;
-static int receive_fsck_objects = 1;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
@@ -36,11 +35,6 @@ static int receive_pack_config(const char *var, const char *value)
return 0;
}
- if (strcmp(var, "receive.fsckobjects") == 0) {
- receive_fsck_objects = git_config_bool(var, value);
- return 0;
- }
-
return git_default_config(var, value);
}
@@ -374,13 +368,11 @@ static const char *unpack(void)
ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
if (ntohl(hdr.hdr_entries) < unpack_limit) {
- int code, i = 0;
- const char *unpacker[4];
- unpacker[i++] = "unpack-objects";
- if (receive_fsck_objects)
- unpacker[i++] = "--strict";
- unpacker[i++] = hdr_arg;
- unpacker[i++] = NULL;
+ int code;
+ const char *unpacker[3];
+ unpacker[0] = "unpack-objects";
+ unpacker[1] = hdr_arg;
+ unpacker[2] = NULL;
code = run_command_v_opt(unpacker, RUN_GIT_CMD);
switch (code) {
case 0:
@@ -401,8 +393,8 @@ static const char *unpack(void)
return "unpacker exited with error code";
}
} else {
- const char *keeper[7];
- int s, status, i = 0;
+ const char *keeper[6];
+ int s, status;
char keep_arg[256];
struct child_process ip;
@@ -410,14 +402,12 @@ static const char *unpack(void)
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
- keeper[i++] = "index-pack";
- keeper[i++] = "--stdin";
- if (receive_fsck_objects)
- keeper[i++] = "--strict";
- keeper[i++] = "--fix-thin";
- keeper[i++] = hdr_arg;
- keeper[i++] = keep_arg;
- keeper[i++] = NULL;
+ keeper[0] = "index-pack";
+ keeper[1] = "--stdin";
+ keeper[2] = "--fix-thin";
+ keeper[3] = hdr_arg;
+ keeper[4] = keep_arg;
+ keeper[5] = NULL;
memset(&ip, 0, sizeof(ip));
ip.argv = keeper;
ip.out = -1;
diff --git a/refs.c b/refs.c
index c979fb1d95..1b0050eee4 100644
--- a/refs.c
+++ b/refs.c
@@ -1033,7 +1033,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
return 1;
}
-static int close_ref(struct ref_lock *lock)
+int close_ref(struct ref_lock *lock)
{
if (close_lock_file(lock->lk))
return -1;
@@ -1041,7 +1041,7 @@ static int close_ref(struct ref_lock *lock)
return 0;
}
-static int commit_ref(struct ref_lock *lock)
+int commit_ref(struct ref_lock *lock)
{
if (commit_lock_file(lock->lk))
return -1;
diff --git a/refs.h b/refs.h
index 9cd16f8295..06abee1526 100644
--- a/refs.h
+++ b/refs.h
@@ -33,6 +33,12 @@ extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_
#define REF_NODEREF 0x01
extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
+/** Close the file descriptor owned by a lock and return the status */
+extern int close_ref(struct ref_lock *lock);
+
+/** Close and commit the ref locked by the lock */
+extern int commit_ref(struct ref_lock *lock);
+
/** Release any lock taken but not written. **/
extern void unlock_ref(struct ref_lock *lock);
diff --git a/remote.c b/remote.c
index 7e1937286b..f3f7375eea 100644
--- a/remote.c
+++ b/remote.c
@@ -357,7 +357,8 @@ static int handle_config(const char *key, const char *value)
remote->fetch_tags = -1;
} else if (!strcmp(subkey, ".proxy")) {
remote->http_proxy = xstrdup(value);
- }
+ } else if (!strcmp(subkey, ".skipdefaultupdate"))
+ remote->skip_default_update = 1;
return 0;
}
diff --git a/remote.h b/remote.h
index 0f6033fb25..f1dedf15f6 100644
--- a/remote.h
+++ b/remote.h
@@ -25,6 +25,7 @@ struct remote {
* 2 to always fetch tags
*/
int fetch_tags;
+ int skip_default_update;
const char *receivepack;
const char *uploadpack;
diff --git a/run-command.c b/run-command.c
index 743757c36e..44100a749b 100644
--- a/run-command.c
+++ b/run-command.c
@@ -91,6 +91,13 @@ int start_command(struct child_process *cmd)
close(cmd->in);
}
+ if (cmd->no_stderr)
+ dup_devnull(2);
+ else if (need_err) {
+ dup2(fderr[1], 2);
+ close_pair(fderr);
+ }
+
if (cmd->no_stdout)
dup_devnull(1);
else if (cmd->stdout_to_stderr)
@@ -103,13 +110,6 @@ int start_command(struct child_process *cmd)
close(cmd->out);
}
- if (cmd->no_stderr)
- dup_devnull(2);
- else if (need_err) {
- dup2(fderr[1], 2);
- close_pair(fderr);
- }
-
if (cmd->dir && chdir(cmd->dir))
die("exec %s: cd to %s failed (%s)", cmd->argv[0],
cmd->dir, strerror(errno));
diff --git a/setup.c b/setup.c
index 89c81e54e6..41e298b8f5 100644
--- a/setup.c
+++ b/setup.c
@@ -202,6 +202,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
const char *p = prefix_path(prefix, prefixlen, *src);
if (p)
*(dst++) = p;
+ else
+ exit(128); /* error message already given */
src++;
}
*dst = NULL;
diff --git a/sha1_name.c b/sha1_name.c
index 8358ba2069..491d2e7ebf 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -407,21 +407,56 @@ static int get_nth_ancestor(const char *name, int len,
unsigned char *result, int generation)
{
unsigned char sha1[20];
- int ret = get_sha1_1(name, len, sha1);
+ struct commit *commit;
+ int ret;
+
+ ret = get_sha1_1(name, len, sha1);
if (ret)
return ret;
+ commit = lookup_commit_reference(sha1);
+ if (!commit)
+ return -1;
while (generation--) {
- struct commit *commit = lookup_commit_reference(sha1);
-
- if (!commit || parse_commit(commit) || !commit->parents)
+ if (parse_commit(commit) || !commit->parents)
return -1;
- hashcpy(sha1, commit->parents->item->object.sha1);
+ commit = commit->parents->item;
}
- hashcpy(result, sha1);
+ hashcpy(result, commit->object.sha1);
return 0;
}
+struct object *peel_to_type(const char *name, int namelen,
+ struct object *o, enum object_type expected_type)
+{
+ if (name && !namelen)
+ namelen = strlen(name);
+ if (!o) {
+ unsigned char sha1[20];
+ if (get_sha1_1(name, namelen, sha1))
+ return NULL;
+ o = parse_object(sha1);
+ }
+ while (1) {
+ if (!o || (!o->parsed && !parse_object(o->sha1)))
+ return NULL;
+ if (o->type == expected_type)
+ return o;
+ if (o->type == OBJ_TAG)
+ o = ((struct tag*) o)->tagged;
+ else if (o->type == OBJ_COMMIT)
+ o = &(((struct commit *) o)->tree->object);
+ else {
+ if (name)
+ error("%.*s: expected %s type, but the object "
+ "dereferences to %s type",
+ namelen, name, typename(expected_type),
+ typename(o->type));
+ return NULL;
+ }
+ }
+}
+
static int peel_onion(const char *name, int len, unsigned char *sha1)
{
unsigned char outer[20];
@@ -473,32 +508,17 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
hashcpy(sha1, o->sha1);
}
else {
- /* At this point, the syntax look correct, so
+ /*
+ * At this point, the syntax look correct, so
* if we do not get the needed object, we should
* barf.
*/
-
- while (1) {
- if (!o || (!o->parsed && !parse_object(o->sha1)))
- return -1;
- if (o->type == expected_type) {
- hashcpy(sha1, o->sha1);
- return 0;
- }
- if (o->type == OBJ_TAG)
- o = ((struct tag*) o)->tagged;
- else if (o->type == OBJ_COMMIT)
- o = &(((struct commit *) o)->tree->object);
- else
- return error("%.*s: expected %s type, but the object dereferences to %s type",
- len, name, typename(expected_type),
- typename(o->type));
- if (!o)
- return -1;
- if (!o->parsed)
- if (!parse_object(o->sha1))
- return -1;
+ o = peel_to_type(name, len, o, expected_type);
+ if (o) {
+ hashcpy(sha1, o->sha1);
+ return 0;
}
+ return -1;
}
return 0;
}
@@ -528,9 +548,8 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
int ret, has_suffix;
const char *cp;
- /* "name~3" is "name^^^",
- * "name~" and "name~0" are name -- not "name^0"!
- * "name^" is not "name^0"; it is "name^1".
+ /*
+ * "name~3" is "name^^^", "name~" is "name~1", and "name^" is "name^1".
*/
has_suffix = 0;
for (cp = name + len - 1; name <= cp; cp--) {
@@ -548,11 +567,10 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
cp++;
while (cp < name + len)
num = num * 10 + *cp++ - '0';
- if (has_suffix == '^') {
- if (!num && len1 == len - 1)
- num = 1;
+ if (!num && len1 == len - 1)
+ num = 1;
+ if (has_suffix == '^')
return get_parent(name, len1, sha1, num);
- }
/* else if (has_suffix == '~') -- goes without saying */
return get_nth_ancestor(name, len1, sha1, num);
}
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index cb860296ed..8fc39d77ce 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -5,7 +5,9 @@ test_description='blob conversion via gitattributes'
. ./test-lib.sh
cat <<\EOF >rot13.sh
-tr '[a-zA-Z]' '[n-za-mN-ZA-M]'
+tr \
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
+ 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
EOF
chmod +x rot13.sh
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 0e2933a984..c23f0ace85 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -21,6 +21,9 @@ string options
--st <st> get another string (pervert ordering)
-o <str> get another string
+magic arguments
+ --quux means --quux
+
EOF
test_expect_success 'test help' '
@@ -114,4 +117,17 @@ test_expect_success 'detect possible typos' '
git diff expect.err output.err
'
+cat > expect <<EOF
+boolean: 0
+integer: 0
+string: (not set)
+arg 00: --quux
+EOF
+
+test_expect_success 'keep some options as arguments' '
+ test-parse-options --quux > output 2> output.err &&
+ test ! -s output.err &&
+ git diff expect output
+'
+
test_done
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
new file mode 100755
index 0000000000..8c4556408e
--- /dev/null
+++ b/t/t1005-read-tree-reset.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='read-tree -u --reset'
+
+. ./test-lib.sh
+
+# two-tree test
+
+test_expect_success 'setup' '
+ git init &&
+ mkdir df &&
+ echo content >df/file &&
+ git add df/file &&
+ git commit -m one &&
+ git ls-files >expect &&
+ rm -rf df &&
+ echo content >df &&
+ git add df &&
+ echo content >new &&
+ git add new &&
+ git commit -m two
+'
+
+test_expect_success 'reset should work' '
+ git read-tree -u --reset HEAD^ &&
+ git ls-files >actual &&
+ diff -u expect actual
+'
+
+test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index f959aae846..73f830db23 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -175,21 +175,30 @@ test_expect_success 'recover and check' '
'
-test_expect_success 'prune --expire' '
-
- before=$(git count-objects | sed "s/ .*//") &&
- BLOB=$(echo aleph | git hash-object -w --stdin) &&
- BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
- git reset --hard &&
- git prune --expire=1.hour.ago &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
- test-chmtime -86500 $BLOB_FILE &&
- git prune --expire 1.day &&
- test $before = $(git count-objects | sed "s/ .*//") &&
- ! test -f $BLOB_FILE
+test_expect_success 'delete' '
+ echo 1 > C &&
+ test_tick &&
+ git commit -m rat C &&
+
+ echo 2 > C &&
+ test_tick &&
+ git commit -m ox C &&
+
+ echo 3 > C &&
+ test_tick &&
+ git commit -m tiger C &&
+
+ test 5 = $(git reflog | wc -l) &&
+
+ git reflog delete master@{1} &&
+ git reflog show master > output &&
+ test 4 = $(wc -l < output) &&
+ ! grep ox < output &&
+
+ git reflog delete master@{07.04.2005.15:15:00.-0700} &&
+ git reflog show master > output &&
+ test 3 = $(wc -l < output) &&
+ ! grep dragon < output
'
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 39fe2676dc..70f9ce9d52 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -120,7 +120,7 @@ EOF
# having 1.txt and path3
test_expect_success \
'ls-tree filter odd names' \
- 'git ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
+ 'git ls-tree $tree 1.txt ./1.txt .//1.txt path3/1.txt path3/./1.txt path3 path3// >current &&
cat >expected <<\EOF &&
100644 blob X 1.txt
100644 blob X path3/1.txt
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 3417138a80..37944c39a3 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -23,37 +23,49 @@ test_expect_success setup '
git branch pre-rebase
'
-test_expect_success 'rebase --abort' '
- test_must_fail git rebase master &&
- git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
-'
+testrebase() {
+ type=$1
+ dotest=$2
-test_expect_success 'rebase --abort after --skip' '
- # Clean up the state from the previous one
- git reset --hard pre-rebase
- rm -rf .dotest
+ test_expect_success "rebase$type --abort" '
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase
+ test_must_fail git rebase'"$type"' master &&
+ test -d '$dotest' &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+ test ! -d '$dotest'
+ '
- test_must_fail git rebase master &&
- test_must_fail git rebase --skip &&
- test $(git rev-parse HEAD) = $(git rev-parse master) &&
- git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
-'
+ test_expect_success "rebase$type --abort after --skip" '
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase
+ test_must_fail git rebase'"$type"' master &&
+ test -d '$dotest' &&
+ test_must_fail git rebase --skip &&
+ test $(git rev-parse HEAD) = $(git rev-parse master) &&
+ git-rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+ test ! -d '$dotest'
+ '
-test_expect_success 'rebase --abort after --continue' '
- # Clean up the state from the previous one
- git reset --hard pre-rebase
- rm -rf .dotest
+ test_expect_success "rebase$type --abort after --continue" '
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase
+ test_must_fail git rebase'"$type"' master &&
+ test -d '$dotest' &&
+ echo c > a &&
+ echo d >> a &&
+ git add a &&
+ test_must_fail git rebase --continue &&
+ test $(git rev-parse HEAD) != $(git rev-parse master) &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+ test ! -d '$dotest'
+ '
+}
- test_must_fail git rebase master &&
- echo c > a &&
- echo d >> a &&
- git add a &&
- test_must_fail git rebase --continue &&
- test $(git rev-parse HEAD) != $(git rev-parse master) &&
- git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase)
-'
+testrebase "" .dotest
+testrebase " --merge" .git/.dotest-merge
test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 9a9a250d2c..aa282e1bc1 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -40,8 +40,8 @@ test_expect_success 'parents of stash' '
test_expect_success 'apply needs clean working directory' '
echo 4 > other-file &&
git add other-file &&
- echo 5 > other-file
- ! git stash apply
+ echo 5 > other-file &&
+ test_must_fail git stash apply
'
test_expect_success 'apply stashed changes' '
@@ -70,7 +70,51 @@ test_expect_success 'unstashing in a subdirectory' '
git reset --hard HEAD &&
mkdir subdir &&
cd subdir &&
- git stash apply
+ git stash apply &&
+ cd ..
+'
+
+test_expect_success 'drop top stash' '
+ git reset --hard &&
+ git stash list > stashlist1 &&
+ echo 7 > file &&
+ git stash &&
+ git stash drop &&
+ git stash list > stashlist2 &&
+ diff stashlist1 stashlist2 &&
+ git stash apply &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'drop middle stash' '
+ git reset --hard &&
+ echo 8 > file &&
+ git stash &&
+ echo 9 > file &&
+ git stash &&
+ git stash drop stash@{1} &&
+ test 2 = $(git stash list | wc -l) &&
+ git stash apply &&
+ test 9 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file) &&
+ git reset --hard &&
+ git stash drop &&
+ git stash apply &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'stash pop' '
+ git reset --hard &&
+ git stash pop &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file) &&
+ test 0 = $(git stash list | wc -l)
'
test_done
diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4021-format-patch-signer-mime.sh
index 67a70fadab..ba43f18549 100755
--- a/t/t4021-format-patch-signer-mime.sh
+++ b/t/t4021-format-patch-signer-mime.sh
@@ -32,11 +32,19 @@ test_expect_success 'format with signoff without funny signer name' '
test_expect_success 'format with non ASCII signer name' '
- GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(B" \
+ GIT_COMMITTER_NAME="はまの ふにおう" \
git format-patch -s --stdout -1 >output &&
grep Content-Type output
'
+test_expect_success 'attach and signoff do not duplicate mime headers' '
+
+ GIT_COMMITTER_NAME="はまの ふにおう" \
+ git format-patch -s --stdout -1 --attach >output &&
+ test `grep -ci ^MIME-Version: output` = 1
+
+'
+
test_done
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
new file mode 100755
index 0000000000..204ba673cb
--- /dev/null
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='format-patch mime headers and extra headers do not conflict'
+. ./test-lib.sh
+
+test_expect_success 'create commit with utf-8 body' '
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+ echo more >>file &&
+ git commit -a -m "two
+
+ utf-8 body: ñ"
+'
+
+test_expect_success 'patch has mime headers' '
+ rm -f 0001-two.patch &&
+ git format-patch HEAD^ &&
+ grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+test_expect_success 'patch has mime and extra headers' '
+ rm -f 0001-two.patch &&
+ git config format.headers "x-foo: bar" &&
+ git format-patch HEAD^ &&
+ grep -i "x-foo: bar" 0001-two.patch &&
+ grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+test_done
diff --git a/t/t4150-am-subdir.sh b/t/t4150-am-subdir.sh
new file mode 100755
index 0000000000..929d2cbd87
--- /dev/null
+++ b/t/t4150-am-subdir.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='git am running from a subdirectory'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello >world &&
+ git add world &&
+ test_tick &&
+ git commit -m initial &&
+ git tag initial &&
+ echo goodbye >world &&
+ git add world &&
+ test_tick &&
+ git commit -m second &&
+ git format-patch --stdout HEAD^ >patchfile &&
+ : >expect
+'
+
+test_expect_success 'am regularly from stdin' '
+ git checkout initial &&
+ git am <patchfile &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from file' '
+ git checkout initial &&
+ git am patchfile &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from stdin in subdirectory' '
+ rm -fr subdir &&
+ git checkout initial &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am <../patchfile
+ ) &&
+ git diff master>actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory' '
+ rm -fr subdir &&
+ git checkout initial &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am ../patchfile
+ ) &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory with full path' '
+ rm -fr subdir &&
+ git checkout initial &&
+ P=$(pwd) &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am "$P/patchfile"
+ ) &&
+ git diff master >actual &&
+ diff -u expect actual
+'
+
+test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 6d12efb74d..eef4cafda9 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -47,4 +47,9 @@ EOF
test_expect_success 'shortlog wrapping' 'diff -u expect out'
+git log HEAD > log
+GIT_DIR=non-existing git shortlog -w < log > out
+
+test_expect_success 'shortlog from non-git directory' 'diff -u expect out'
+
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 6560af756e..47090c4cf5 100644
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -29,4 +29,53 @@ test_expect_success 'prune stale packs' '
'
+test_expect_success 'prune --expire' '
+
+ before=$(git count-objects | sed "s/ .*//") &&
+ BLOB=$(echo aleph | git hash-object -w --stdin) &&
+ BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ git prune --expire=1.hour.ago &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ test-chmtime -86500 $BLOB_FILE &&
+ git prune --expire 1.day &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
+ ! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: implicit prune --expire' '
+
+ before=$(git count-objects | sed "s/ .*//") &&
+ BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+ BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ test-chmtime -$((86400*14-30)) $BLOB_FILE &&
+ git gc &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ test-chmtime -$((86400*14+1)) $BLOB_FILE &&
+ git gc &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
+ ! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
+
+ git config gc.pruneExpire invalid &&
+ test_must_fail git gc
+
+'
+
+test_expect_success 'gc: start with ok gc.pruneExpire' '
+
+ git config gc.pruneExpire 2.days.ago &&
+ git gc
+
+'
+
test_done
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
new file mode 100755
index 0000000000..0db27547ac
--- /dev/null
+++ b/t/t5305-include-tag.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='git-pack-object --include-tag'
+. ./test-lib.sh
+
+TRASH=`pwd`
+
+test_expect_success setup '
+ echo c >d &&
+ git update-index --add d &&
+ tree=`git write-tree` &&
+ commit=`git commit-tree $tree </dev/null` &&
+ echo "object $commit" >sig &&
+ echo "type commit" >>sig &&
+ echo "tag mytag" >>sig &&
+ echo "tagger $(git var GIT_COMMITTER_IDENT)" >>sig &&
+ echo >>sig &&
+ echo "our test tag" >>sig &&
+ tag=`git mktag <sig` &&
+ rm d sig &&
+ git update-ref refs/tags/mytag $tag && {
+ echo $tree &&
+ echo $commit &&
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list
+'
+
+rm -rf clone.git
+test_expect_success 'pack without --include-tag' '
+ packname_1=$(git pack-objects \
+ --window=0 \
+ test-1 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git init &&
+ git unpack-objects -n <test-1-${packname_1}.pack &&
+ git unpack-objects <test-1-${packname_1}.pack
+ )
+'
+
+test_expect_success 'check unpacked result (have commit, no tag)' '
+ git rev-list --objects $commit >list.expect &&
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ test_must_fail git cat-file -e $tag &&
+ git rev-list --objects $commit
+ ) >list.actual &&
+ git diff list.expect list.actual
+'
+
+rm -rf clone.git
+test_expect_success 'pack with --include-tag' '
+ packname_1=$(git pack-objects \
+ --window=0 \
+ --include-tag \
+ test-2 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git init &&
+ git unpack-objects -n <test-2-${packname_1}.pack &&
+ git unpack-objects <test-2-${packname_1}.pack
+ )
+'
+
+test_expect_success 'check unpacked result (have commit, have tag)' '
+ git rev-list --objects mytag >list.expect &&
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git rev-list --objects $tag
+ ) >list.actual &&
+ git diff list.expect list.actual
+'
+
+test_done
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
new file mode 100755
index 0000000000..86e5b9bc26
--- /dev/null
+++ b/t/t5503-tagfollow.sh
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+test_description='test automatic tag following'
+
+. ./test-lib.sh
+
+# End state of the repository:
+#
+# T - tag1 S - tag2
+# / /
+# L - A ------ O ------ B
+# \ \ \
+# \ C - origin/cat \
+# origin/master master
+
+test_expect_success setup '
+ test_tick &&
+ echo ichi >file &&
+ git add file &&
+ git commit -m L &&
+ L=$(git rev-parse --verify HEAD) &&
+
+ (
+ mkdir cloned &&
+ cd cloned &&
+ git init-db &&
+ git remote add -f origin ..
+ ) &&
+
+ test_tick &&
+ echo A >file &&
+ git add file &&
+ git commit -m A &&
+ A=$(git rev-parse --verify HEAD)
+'
+
+U=UPLOAD_LOG
+
+cat - <<EOF >expect
+#S
+want $A
+#E
+EOF
+test_expect_success 'fetch A (new commit : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $A = $(git rev-parse --verify origin/master)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+test_expect_success "create tag T on A, create C on branch cat" '
+ git tag -a -m tag1 tag1 $A &&
+ T=$(git rev-parse --verify tag1) &&
+
+ git checkout -b cat &&
+ echo C >file &&
+ git add file &&
+ git commit -m C &&
+ C=$(git rev-parse --verify HEAD) &&
+ git checkout master
+'
+
+cat - <<EOF >expect
+#S
+want $C
+want $T
+#E
+EOF
+test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $C = $(git rev-parse --verify origin/cat) &&
+ test $T = $(git rev-parse --verify tag1) &&
+ test $A = $(git rev-parse --verify tag1^0)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+test_expect_success "create commits O, B, tag S on B" '
+ test_tick &&
+ echo O >file &&
+ git add file &&
+ git commit -m O &&
+
+ test_tick &&
+ echo B >file &&
+ git add file &&
+ git commit -m B &&
+ B=$(git rev-parse --verify HEAD) &&
+
+ git tag -a -m tag2 tag2 $B &&
+ S=$(git rev-parse --verify tag2)
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $B = $(git rev-parse --verify origin/master) &&
+ test $B = $(git rev-parse --verify tag2^0) &&
+ test $S = $(git rev-parse --verify tag2)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'new clone fetch master and tags' '
+ git branch -D cat
+ rm -f $U
+ (
+ mkdir clone2 &&
+ cd clone2 &&
+ git init &&
+ git remote add origin .. &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $B = $(git rev-parse --verify origin/master) &&
+ test $S = $(git rev-parse --verify tag2) &&
+ test $B = $(git rev-parse --verify tag2^0) &&
+ test $T = $(git rev-parse --verify tag1) &&
+ test $A = $(git rev-parse --verify tag1^0)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ git diff expect actual
+'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 4fc62f550c..2822a651b5 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -10,10 +10,12 @@ setup_repository () {
git init &&
>file &&
git add file &&
+ test_tick &&
git commit -m "Initial" &&
git checkout -b side &&
>elif &&
git add elif &&
+ test_tick &&
git commit -m "Second" &&
git checkout master
)
@@ -78,6 +80,7 @@ test_expect_success 'add another remote' '
test_expect_success 'remove remote' '
(
cd test &&
+ git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
git remote rm second
)
'
@@ -94,4 +97,144 @@ test_expect_success 'remove remote' '
)
'
+cat > test/expect << EOF
+* remote origin
+ URL: $(pwd)/one/.git
+ Remote branch merged with 'git pull' while on branch master
+ master
+ New remote branch (next fetch will store in remotes/origin)
+ master
+ Tracked remote branches
+ side master
+EOF
+
+test_expect_success 'show' '
+ (cd test &&
+ git config --add remote.origin.fetch \
+ refs/heads/master:refs/heads/upstream &&
+ git fetch &&
+ git branch -d -r origin/master &&
+ (cd ../one &&
+ echo 1 > file &&
+ test_tick &&
+ git commit -m update file) &&
+ git remote show origin > output &&
+ git diff expect output)
+'
+
+test_expect_success 'prune' '
+ (cd one &&
+ git branch -m side side2) &&
+ (cd test &&
+ git fetch origin &&
+ git remote prune origin &&
+ git rev-parse refs/remotes/origin/side2 &&
+ ! git rev-parse refs/remotes/origin/side)
+'
+
+test_expect_success 'add --mirror && prune' '
+ (mkdir mirror &&
+ cd mirror &&
+ git init &&
+ git remote add --mirror -f origin ../one) &&
+ (cd one &&
+ git branch -m side2 side) &&
+ (cd mirror &&
+ git rev-parse --verify refs/heads/side2 &&
+ ! git rev-parse --verify refs/heads/side &&
+ git fetch origin &&
+ git remote prune origin &&
+ ! git rev-parse --verify refs/heads/side2 &&
+ git rev-parse --verify refs/heads/side)
+'
+
+cat > one/expect << EOF
+ apis/master
+ apis/side
+ drosophila/another
+ drosophila/master
+ drosophila/side
+EOF
+
+test_expect_success 'update' '
+
+ (cd one &&
+ git remote add drosophila ../two &&
+ git remote add apis ../mirror &&
+ git remote update &&
+ git branch -r > output &&
+ git diff expect output)
+
+'
+
+cat > one/expect << EOF
+ drosophila/another
+ drosophila/master
+ drosophila/side
+ manduca/master
+ manduca/side
+ megaloprepus/master
+ megaloprepus/side
+EOF
+
+test_expect_success 'update with arguments' '
+
+ (cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git remote add manduca ../mirror &&
+ git remote add megaloprepus ../mirror &&
+ git config remotes.phobaeticus "drosophila megaloprepus" &&
+ git config remotes.titanus manduca &&
+ git remote update phobaeticus titanus &&
+ git branch -r > output &&
+ git diff expect output)
+
+'
+
+cat > one/expect << EOF
+ apis/master
+ apis/side
+ manduca/master
+ manduca/side
+ megaloprepus/master
+ megaloprepus/side
+EOF
+
+test_expect_success 'update default' '
+
+ (cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git config remote.drosophila.skipDefaultUpdate true &&
+ git remote update default &&
+ git branch -r > output &&
+ git diff expect output)
+
+'
+
+cat > one/expect << EOF
+ drosophila/another
+ drosophila/master
+ drosophila/side
+EOF
+
+test_expect_success 'update default (overridden, with funny whitespace)' '
+
+ (cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git config remotes.default "$(printf "\t drosophila \n")" &&
+ git remote update default &&
+ git branch -r > output &&
+ git diff expect output)
+
+'
+
test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
new file mode 100755
index 0000000000..5bb6b93780
--- /dev/null
+++ b/t/t6031-merge-recursive.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='merge-recursive: handle file mode'
+. ./test-lib.sh
+
+test_expect_success 'mode change in one branch: keep changed version' '
+ : >file1 &&
+ git add file1 &&
+ git commit -m initial &&
+ git checkout -b a1 master &&
+ : >dummy &&
+ git add dummy &&
+ git commit -m a &&
+ git checkout -b b1 master &&
+ chmod +x file1 &&
+ git add file1 &&
+ git commit -m b1 &&
+ git checkout a1 &&
+ git merge-recursive master -- a1 b1 &&
+ test -x file1
+'
+
+test_expect_success 'mode change in both branches: expect conflict' '
+ git reset --hard HEAD &&
+ git checkout -b a2 master &&
+ : >file2 &&
+ H=$(git hash-object file2) &&
+ chmod +x file2 &&
+ git add file2 &&
+ git commit -m a2 &&
+ git checkout -b b2 master &&
+ : >file2 &&
+ git add file2 &&
+ git commit -m b2 &&
+ git checkout a2 &&
+ (
+ git merge-recursive master -- a2 b2
+ test $? = 1
+ ) &&
+ git ls-files -u >actual &&
+ (
+ echo "100755 $H 2 file2"
+ echo "100644 $H 3 file2"
+ ) >expect &&
+ diff -u actual expect &&
+ test -x file2
+'
+
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index a7557bdc79..56bbd8519d 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -15,8 +15,11 @@ test_description='test describe
check_describe () {
expect="$1"
shift
- R=$(git describe "$@") &&
+ R=$(git describe "$@" 2>err.actual)
+ S=$?
+ cat err.actual >&3
test_expect_success "describe $*" '
+ test $S = 0 &&
case "$R" in
$expect) echo happy ;;
*) echo "Oops - $R is not $expect";
@@ -95,5 +98,23 @@ check_describe A-* --tags HEAD^^2
check_describe B --tags HEAD^^2^
check_describe B-0-* --long HEAD^^2^
+check_describe A-3-* --long HEAD^^2
+
+test_expect_success 'rename tag A to Q locally' '
+ mv .git/refs/tags/A .git/refs/tags/Q
+'
+cat - >err.expect <<EOF
+warning: tag 'A' is really 'Q' here
+EOF
+check_describe A-* HEAD
+test_expect_success 'warning was displayed for Q' '
+ git diff err.expect err.actual
+'
+test_expect_success 'rename tag Q back to A' '
+ mv .git/refs/tags/Q .git/refs/tags/A
+'
+
+test_expect_success 'pack tag refs' 'git pack-refs'
+check_describe A-* HEAD
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 868babc4b2..6e14bf1c7f 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -179,4 +179,28 @@ test_expect_success 'Name needing quotes' '
'
+test_expect_success 'Subdirectory filter with disappearing trees' '
+ git reset --hard &&
+ git checkout master &&
+
+ mkdir foo &&
+ touch foo/bar &&
+ git add foo &&
+ test_tick &&
+ git commit -m "Adding foo" &&
+
+ git rm -r foo &&
+ test_tick &&
+ git commit -m "Removing foo" &&
+
+ mkdir foo &&
+ touch foo/bar &&
+ git add foo &&
+ test_tick &&
+ git commit -m "Re-adding foo" &&
+
+ git filter-branch -f --subdirectory-filter foo &&
+ test $(git rev-list master | wc -l) = 3
+'
+
test_done
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index c1cec55306..6a74b3acfd 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -89,6 +89,33 @@ do
'
done
+test_expect_success 'editor with a space' '
+
+ if echo "echo space > \"\$1\"" > "e space.sh"
+ then
+ chmod a+x "e space.sh" &&
+ GIT_EDITOR="./e\ space.sh" git commit --amend &&
+ test space = "$(git show -s --pretty=format:%s)"
+ else
+ say "Skipping; FS does not support spaces in filenames"
+ fi
+
+'
+
+unset GIT_EDITOR
+test_expect_success 'core.editor with a space' '
+
+ if test -f "e space.sh"
+ then
+ git config core.editor \"./e\ space.sh\" &&
+ git commit --amend &&
+ test space = "$(git show -s --pretty=format:%s)"
+ else
+ say "Skipping; FS does not support spaces in filenames"
+ fi
+
+'
+
TERM="$OLD_TERM"
test_done
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
index e809e0e2c9..bc8ab6a619 100755
--- a/t/t7010-setup.sh
+++ b/t/t7010-setup.sh
@@ -142,15 +142,16 @@ test_expect_success 'setup deeper work tree' '
test_expect_success 'add a directory outside the work tree' '(
cd tester &&
d1="$(cd .. ; pwd)" &&
- git add "$d1"
+ test_must_fail git add "$d1"
)'
+
test_expect_success 'add a file outside the work tree, nasty case 1' '(
cd tester &&
f="$(pwd)x" &&
echo "$f" &&
touch "$f" &&
- git add "$f"
+ test_must_fail git add "$f"
)'
test_expect_success 'add a file outside the work tree, nasty case 2' '(
@@ -158,7 +159,7 @@ test_expect_success 'add a file outside the work tree, nasty case 2' '(
f="$(pwd | sed "s/.$//")x" &&
echo "$f" &&
touch "$f" &&
- git add "$f"
+ test_must_fail git add "$f"
)'
test_done
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 38403643a6..afccfc9973 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -89,6 +89,58 @@ test_expect_success 'git-clean with prefix' '
test -f build/lib.so
'
+
+test_expect_success 'git-clean with relative prefix' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ would_clean=$(
+ cd docs &&
+ git clean -n ../src |
+ sed -n -e "s|^Would remove ||p"
+ ) &&
+ test "$would_clean" = ../src/part3.c || {
+ echo "OOps <$would_clean>"
+ false
+ }
+'
+
+test_expect_success 'git-clean with absolute path' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ would_clean=$(
+ cd docs &&
+ git clean -n $(pwd)/../src |
+ sed -n -e "s|^Would remove ||p"
+ ) &&
+ test "$would_clean" = ../src/part3.c || {
+ echo "OOps <$would_clean>"
+ false
+ }
+'
+
+test_expect_success 'git-clean with out of work tree relative path' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ (
+ cd docs &&
+ test_must_fail git clean -n ../..
+ )
+'
+
+test_expect_success 'git-clean with out of work tree absolute path' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ dd=$(cd .. && pwd) &&
+ (
+ cd docs &&
+ test_must_fail git clean -n $dd
+ )
+'
+
test_expect_success 'git-clean -d with prefix and path' '
mkdir -p build docs src/feature &&
@@ -320,8 +372,9 @@ test_expect_success 'removal failure' '
mkdir foo &&
touch foo/bar &&
+ exec <foo/bar &&
chmod 0 foo &&
- ! git clean -f -d
+ test_must_fail git clean -f -d
'
chmod 755 foo
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 50c51c82fa..5d166280cb 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -419,6 +419,7 @@ test_debug 'gitk --all'
test_expect_success 'merge c0 with c1 (no-ff)' '
git reset --hard c0 &&
+ git config branch.master.mergeoptions "" &&
test_tick &&
git merge --no-ff c1 &&
verify_merge file result.1 &&
@@ -427,6 +428,11 @@ test_expect_success 'merge c0 with c1 (no-ff)' '
test_debug 'gitk --all'
+test_expect_success 'combining --squash and --no-ff is refused' '
+ test_must_fail git merge --squash --no-ff c1 &&
+ test_must_fail git merge --no-ff --squash c1
+'
+
test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
git reset --hard c0 &&
git config branch.master.mergeoptions "--no-ff" &&
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
new file mode 100644
index 0000000000..6b0483f3e9
--- /dev/null
+++ b/t/t7610-mergetool.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Charles Bailey
+#
+
+test_description='git-mergetool
+
+Testing basic merge tool invocation'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo master >file1 &&
+ git add file1 &&
+ git commit -m "added file1" &&
+ git checkout -b branch1 master &&
+ echo branch1 change >file1 &&
+ echo branch1 newfile >file2 &&
+ git add file1 file2 &&
+ git commit -m "branch1 changes" &&
+ git checkout -b branch2 master &&
+ echo branch2 change >file1 &&
+ echo branch2 newfile >file2 &&
+ git add file1 file2 &&
+ git commit -m "branch2 changes" &&
+ git checkout master &&
+ echo master updated >file1 &&
+ echo master new >file2 &&
+ git add file1 file2 &&
+ git commit -m "master updates"
+'
+
+test_expect_success 'custom mergetool' '
+ git config merge.tool mytool &&
+ git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ git config mergetool.mytool.trustExitCode true &&
+ git checkout branch1 &&
+ ! git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file1>/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file2>/dev/null 2>&1 ) &&
+ test "$(cat file1)" = "master updated" &&
+ test "$(cat file2)" = "master new" &&
+ git commit -m "branch1 resolved with mergetool"
+'
+
+test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index cceedbb2b7..c4f4465dc6 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -869,6 +869,8 @@ zcommits
COMMIT
reset refs/tags/O3-2nd
from :5
+reset refs/tags/O3-3rd
+from :5
INPUT_END
cat >expect <<INPUT_END
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 87a5ea4a6a..6aea0ea0a5 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -3,12 +3,16 @@
# Copyright (c) 2005 Junio C Hamano
#
+# Keep the original TERM for say_color
+ORIGINAL_TERM=$TERM
+
# For repeatability, reset the environment to known value.
LANG=C
LC_ALL=C
PAGER=cat
TZ=UTC
-export LANG LC_ALL PAGER TZ
+TERM=dumb
+export LANG LC_ALL PAGER TERM TZ
EDITOR=:
VISUAL=:
unset GIT_EDITOR
@@ -58,12 +62,14 @@ esac
# This test checks if command xyzzy does the right thing...
# '
# . ./test-lib.sh
-
-[ "x$TERM" != "xdumb" ] &&
- [ -t 1 ] &&
- tput bold >/dev/null 2>&1 &&
- tput setaf 1 >/dev/null 2>&1 &&
- tput sgr0 >/dev/null 2>&1 &&
+[ "x$ORIGINAL_TERM" != "xdumb" ] && (
+ TERM=$ORIGINAL_TERM &&
+ export TERM &&
+ [ -t 1 ] &&
+ tput bold >/dev/null 2>&1 &&
+ tput setaf 1 >/dev/null 2>&1 &&
+ tput sgr0 >/dev/null 2>&1
+ ) &&
color=t
while test "$#" -ne 0
@@ -91,6 +97,9 @@ done
if test -n "$color"; then
say_color () {
+ (
+ TERM=$ORIGINAL_TERM
+ export TERM
case "$1" in
error) tput bold; tput setaf 1;; # bold red
skip) tput bold; tput setaf 2;; # bold green
@@ -101,6 +110,7 @@ if test -n "$color"; then
shift
echo "* $*"
tput sgr0
+ )
}
else
say_color() {
diff --git a/test-parse-options.c b/test-parse-options.c
index eed8a02c65..73360d7512 100644
--- a/test-parse-options.c
+++ b/test-parse-options.c
@@ -20,6 +20,8 @@ int main(int argc, const char **argv)
OPT_STRING(0, "string2", &string, "str", "get another string"),
OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
OPT_STRING('o', NULL, &string, "str", "get another string"),
+ OPT_GROUP("magic arguments"),
+ OPT_ARGUMENT("quux", "means --quux"),
OPT_END(),
};
int i;
diff --git a/transport.c b/transport.c
index 166c1d1d46..393e0e8fe2 100644
--- a/transport.c
+++ b/transport.c
@@ -560,6 +560,7 @@ static int close_bundle(struct transport *transport)
struct git_transport_data {
unsigned thin : 1;
unsigned keep : 1;
+ unsigned followtags : 1;
int depth;
struct child_process *conn;
int fd[2];
@@ -580,6 +581,9 @@ static int set_git_option(struct transport *connection,
} else if (!strcmp(name, TRANS_OPT_THIN)) {
data->thin = !!value;
return 0;
+ } else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
+ data->followtags = !!value;
+ return 0;
} else if (!strcmp(name, TRANS_OPT_KEEP)) {
data->keep = !!value;
return 0;
@@ -628,6 +632,7 @@ static int fetch_refs_via_pack(struct transport *transport,
args.keep_pack = data->keep;
args.lock_pack = 1;
args.use_thin_pack = data->thin;
+ args.include_tag = data->followtags;
args.verbose = transport->verbose > 0;
args.depth = data->depth;
diff --git a/transport.h b/transport.h
index 6fb4526cda..8abfc0ae60 100644
--- a/transport.h
+++ b/transport.h
@@ -53,6 +53,9 @@ struct transport *transport_get(struct remote *, const char *);
/* Limit the depth of the fetch if not null */
#define TRANS_OPT_DEPTH "depth"
+/* Aggressively fetch annotated tags if possible */
+#define TRANS_OPT_FOLLOWTAGS "followtags"
+
/**
* Returns 0 if the option was used, non-zero otherwise. Prints a
* message to stderr if the option is not used.
diff --git a/tree-walk.c b/tree-walk.c
index 142205ddc3..02e2aed773 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -62,7 +62,7 @@ void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
static int entry_compare(struct name_entry *a, struct name_entry *b)
{
- return base_name_compare(
+ return df_name_compare(
a->path, tree_entry_len(a->path, a->sha1), a->mode,
b->path, tree_entry_len(b->path, b->sha1), b->mode);
}
@@ -104,12 +104,48 @@ int tree_entry(struct tree_desc *desc, struct name_entry *entry)
return 1;
}
-void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback)
+void setup_traverse_info(struct traverse_info *info, const char *base)
{
+ int pathlen = strlen(base);
+ static struct traverse_info dummy;
+
+ memset(info, 0, sizeof(*info));
+ if (pathlen && base[pathlen-1] == '/')
+ pathlen--;
+ info->pathlen = pathlen ? pathlen + 1 : 0;
+ info->name.path = base;
+ info->name.sha1 = (void *)(base + pathlen + 1);
+ if (pathlen)
+ info->prev = &dummy;
+}
+
+char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
+{
+ int len = tree_entry_len(n->path, n->sha1);
+ int pathlen = info->pathlen;
+
+ path[pathlen + len] = 0;
+ for (;;) {
+ memcpy(path + pathlen, n->path, len);
+ if (!pathlen)
+ break;
+ path[--pathlen] = '/';
+ n = &info->name;
+ len = tree_entry_len(n->path, n->sha1);
+ info = info->prev;
+ pathlen -= len;
+ }
+ return path;
+}
+
+int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
+{
+ int ret = 0;
struct name_entry *entry = xmalloc(n*sizeof(*entry));
for (;;) {
unsigned long mask = 0;
+ unsigned long dirmask = 0;
int i, last;
last = -1;
@@ -134,25 +170,35 @@ void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callb
mask = 0;
}
mask |= 1ul << i;
+ if (S_ISDIR(entry[i].mode))
+ dirmask |= 1ul << i;
last = i;
}
if (!mask)
break;
+ dirmask &= mask;
/*
- * Update the tree entries we've walked, and clear
- * all the unused name-entries.
+ * Clear all the unused name-entries.
*/
for (i = 0; i < n; i++) {
- if (mask & (1ul << i)) {
- update_tree_entry(t+i);
+ if (mask & (1ul << i))
continue;
- }
entry_clear(entry + i);
}
- callback(n, mask, entry, base);
+ ret = info->fn(n, mask, dirmask, entry, info);
+ if (ret < 0)
+ break;
+ if (ret)
+ mask &= ret;
+ ret = 0;
+ for (i = 0; i < n; i++) {
+ if (mask & (1ul << i))
+ update_tree_entry(t + i);
+ }
}
free(entry);
+ return ret;
}
static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
diff --git a/tree-walk.h b/tree-walk.h
index db0fbdc701..42110a465f 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -33,10 +33,27 @@ int tree_entry(struct tree_desc *, struct name_entry *);
void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
-typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry *entry, const char *base);
-
-void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback);
+struct traverse_info;
+typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
+int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
+
+struct traverse_info {
+ struct traverse_info *prev;
+ struct name_entry name;
+ int pathlen;
+
+ unsigned long conflicts;
+ traverse_callback_t fn;
+ void *data;
+};
int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
+extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
+extern void setup_traverse_info(struct traverse_info *info, const char *base);
+
+static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
+{
+ return info->pathlen + tree_entry_len(n->path, n->sha1);
+}
#endif
diff --git a/unpack-trees.c b/unpack-trees.c
index 3e448d8974..93019abdc1 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
#include "dir.h"
#include "tree.h"
@@ -7,268 +8,18 @@
#include "progress.h"
#include "refs.h"
-#define DBRT_DEBUG 1
-
-struct tree_entry_list {
- struct tree_entry_list *next;
- unsigned int mode;
- const char *name;
- const unsigned char *sha1;
-};
-
-static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
-{
- struct name_entry one;
- struct tree_entry_list *ret = NULL;
- struct tree_entry_list **list_p = &ret;
-
- while (tree_entry(desc, &one)) {
- struct tree_entry_list *entry;
-
- entry = xmalloc(sizeof(struct tree_entry_list));
- entry->name = one.path;
- entry->sha1 = one.sha1;
- entry->mode = one.mode;
- entry->next = NULL;
-
- *list_p = entry;
- list_p = &entry->next;
- }
- return ret;
-}
-
-static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
+static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
+ unsigned int set, unsigned int clear)
{
- int len1 = strlen(name1);
- int len2 = strlen(name2);
- int len = len1 < len2 ? len1 : len2;
- int ret = memcmp(name1, name2, len);
- unsigned char c1, c2;
- if (ret)
- return ret;
- c1 = name1[len];
- c2 = name2[len];
- if (!c1 && dir1)
- c1 = '/';
- if (!c2 && dir2)
- c2 = '/';
- ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
- if (c1 && c2 && !ret)
- ret = len1 - len2;
- return ret;
-}
-
-static inline void remove_entry(int remove)
-{
- if (remove >= 0)
- remove_cache_entry_at(remove);
-}
-
-static int unpack_trees_rec(struct tree_entry_list **posns, int len,
- const char *base, struct unpack_trees_options *o,
- struct tree_entry_list *df_conflict_list)
-{
- int remove;
- int baselen = strlen(base);
- int src_size = len + 1;
- int retval = 0;
-
- do {
- int i;
- const char *first;
- int firstdir = 0;
- int pathlen;
- unsigned ce_size;
- struct tree_entry_list **subposns;
- struct cache_entry **src;
- int any_files = 0;
- int any_dirs = 0;
- char *cache_name;
- int ce_stage;
- int skip_entry = 0;
-
- /* Find the first name in the input. */
-
- first = NULL;
- cache_name = NULL;
-
- /* Check the cache */
- if (o->merge && o->pos < active_nr) {
- /* This is a bit tricky: */
- /* If the index has a subdirectory (with
- * contents) as the first name, it'll get a
- * filename like "foo/bar". But that's after
- * "foo", so the entry in trees will get
- * handled first, at which point we'll go into
- * "foo", and deal with "bar" from the index,
- * because the base will be "foo/". The only
- * way we can actually have "foo/bar" first of
- * all the things is if the trees don't
- * contain "foo" at all, in which case we'll
- * handle "foo/bar" without going into the
- * directory, but that's fine (and will return
- * an error anyway, with the added unknown
- * file case.
- */
-
- cache_name = active_cache[o->pos]->name;
- if (strlen(cache_name) > baselen &&
- !memcmp(cache_name, base, baselen)) {
- cache_name += baselen;
- first = cache_name;
- } else {
- cache_name = NULL;
- }
- }
-
-#if DBRT_DEBUG > 1
- if (first)
- fprintf(stderr, "index %s\n", first);
-#endif
- for (i = 0; i < len; i++) {
- if (!posns[i] || posns[i] == df_conflict_list)
- continue;
-#if DBRT_DEBUG > 1
- fprintf(stderr, "%d %s\n", i + 1, posns[i]->name);
-#endif
- if (!first || entcmp(first, firstdir,
- posns[i]->name,
- S_ISDIR(posns[i]->mode)) > 0) {
- first = posns[i]->name;
- firstdir = S_ISDIR(posns[i]->mode);
- }
- }
- /* No name means we're done */
- if (!first)
- goto leave_directory;
-
- pathlen = strlen(first);
- ce_size = cache_entry_size(baselen + pathlen);
-
- src = xcalloc(src_size, sizeof(struct cache_entry *));
-
- subposns = xcalloc(len, sizeof(struct tree_list_entry *));
-
- remove = -1;
- if (cache_name && !strcmp(cache_name, first)) {
- any_files = 1;
- src[0] = active_cache[o->pos];
- remove = o->pos;
- if (o->skip_unmerged && ce_stage(src[0]))
- skip_entry = 1;
- }
-
- for (i = 0; i < len; i++) {
- struct cache_entry *ce;
-
- if (!posns[i] ||
- (posns[i] != df_conflict_list &&
- strcmp(first, posns[i]->name))) {
- continue;
- }
-
- if (posns[i] == df_conflict_list) {
- src[i + o->merge] = o->df_conflict_entry;
- continue;
- }
+ unsigned int size = ce_size(ce);
+ struct cache_entry *new = xmalloc(size);
- if (S_ISDIR(posns[i]->mode)) {
- struct tree *tree = lookup_tree(posns[i]->sha1);
- struct tree_desc t;
- any_dirs = 1;
- parse_tree(tree);
- init_tree_desc(&t, tree->buffer, tree->size);
- subposns[i] = create_tree_entry_list(&t);
- posns[i] = posns[i]->next;
- src[i + o->merge] = o->df_conflict_entry;
- continue;
- }
-
- if (skip_entry) {
- subposns[i] = df_conflict_list;
- posns[i] = posns[i]->next;
- continue;
- }
-
- if (!o->merge)
- ce_stage = 0;
- else if (i + 1 < o->head_idx)
- ce_stage = 1;
- else if (i + 1 > o->head_idx)
- ce_stage = 3;
- else
- ce_stage = 2;
-
- ce = xcalloc(1, ce_size);
- ce->ce_mode = create_ce_mode(posns[i]->mode);
- ce->ce_flags = create_ce_flags(baselen + pathlen,
- ce_stage);
- memcpy(ce->name, base, baselen);
- memcpy(ce->name + baselen, first, pathlen + 1);
-
- any_files = 1;
-
- hashcpy(ce->sha1, posns[i]->sha1);
- src[i + o->merge] = ce;
- subposns[i] = df_conflict_list;
- posns[i] = posns[i]->next;
- }
- if (any_files) {
- if (skip_entry) {
- o->pos++;
- while (o->pos < active_nr &&
- !strcmp(active_cache[o->pos]->name,
- src[0]->name))
- o->pos++;
- } else if (o->merge) {
- int ret;
-
-#if DBRT_DEBUG > 1
- fprintf(stderr, "%s:\n", first);
- for (i = 0; i < src_size; i++) {
- fprintf(stderr, " %d ", i);
- if (src[i])
- fprintf(stderr, "%06x %s\n", src[i]->ce_mode, sha1_to_hex(src[i]->sha1));
- else
- fprintf(stderr, "\n");
- }
-#endif
- ret = o->fn(src, o, remove);
- if (ret < 0)
- return ret;
+ clear |= CE_HASHED | CE_UNHASHED;
-#if DBRT_DEBUG > 1
- fprintf(stderr, "Added %d entries\n", ret);
-#endif
- o->pos += ret;
- } else {
- remove_entry(remove);
- for (i = 0; i < src_size; i++) {
- if (src[i]) {
- add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
- }
- }
- }
- }
- if (any_dirs) {
- char *newbase = xmalloc(baselen + 2 + pathlen);
- memcpy(newbase, base, baselen);
- memcpy(newbase + baselen, first, pathlen);
- newbase[baselen + pathlen] = '/';
- newbase[baselen + pathlen + 1] = '\0';
- if (unpack_trees_rec(subposns, len, newbase, o,
- df_conflict_list)) {
- retval = -1;
- goto leave_directory;
- }
- free(newbase);
- }
- free(subposns);
- free(src);
- } while (1);
-
- leave_directory:
- return retval;
+ memcpy(new, ce, size);
+ new->next = NULL;
+ new->ce_flags = (new->ce_flags & ~clear) | set;
+ add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
}
/* Unlink the last component and attempt to remove leading
@@ -308,11 +59,12 @@ static void check_updates(struct unpack_trees_options *o)
unsigned cnt = 0, total = 0;
struct progress *progress = NULL;
char last_symlink[PATH_MAX];
+ struct index_state *index = &o->result;
int i;
if (o->update && o->verbose_update) {
- for (total = cnt = 0; cnt < active_nr; cnt++) {
- struct cache_entry *ce = active_cache[cnt];
+ for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
+ struct cache_entry *ce = index->cache[cnt];
if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
total++;
}
@@ -323,15 +75,15 @@ static void check_updates(struct unpack_trees_options *o)
}
*last_symlink = '\0';
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < index->cache_nr; i++) {
+ struct cache_entry *ce = index->cache[i];
if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
display_progress(progress, ++cnt);
if (ce->ce_flags & CE_REMOVE) {
if (o->update)
unlink_entry(ce->name, last_symlink);
- remove_cache_entry_at(i);
+ remove_index_entry_at(&o->result, i);
i--;
continue;
}
@@ -346,21 +98,246 @@ static void check_updates(struct unpack_trees_options *o)
stop_progress(&progress);
}
-int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
+static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
+{
+ int ret = o->fn(src, o);
+ if (ret > 0)
+ ret = 0;
+ return ret;
+}
+
+static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+{
+ struct cache_entry *src[5] = { ce, };
+
+ o->pos++;
+ if (ce_stage(ce)) {
+ if (o->skip_unmerged) {
+ add_entry(o, ce, 0, 0);
+ return 0;
+ }
+ }
+ return call_unpack_fn(src, o);
+}
+
+int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
{
- struct tree_entry_list **posns;
int i;
- struct tree_entry_list df_conflict_list;
+ struct tree_desc t[MAX_UNPACK_TREES];
+ struct traverse_info newinfo;
+ struct name_entry *p;
+
+ p = names;
+ while (!p->mode)
+ p++;
+
+ newinfo = *info;
+ newinfo.prev = info;
+ newinfo.name = *p;
+ newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1;
+ newinfo.conflicts |= df_conflicts;
+
+ for (i = 0; i < n; i++, dirmask >>= 1) {
+ const unsigned char *sha1 = NULL;
+ if (dirmask & 1)
+ sha1 = names[i].sha1;
+ fill_tree_descriptor(t+i, sha1);
+ }
+ return traverse_trees(n, t, &newinfo);
+}
+
+/*
+ * Compare the traverse-path to the cache entry without actually
+ * having to generate the textual representation of the traverse
+ * path.
+ *
+ * NOTE! This *only* compares up to the size of the traverse path
+ * itself - the caller needs to do the final check for the cache
+ * entry having more data at the end!
+ */
+static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+{
+ int len, pathlen, ce_len;
+ const char *ce_name;
+
+ if (info->prev) {
+ int cmp = do_compare_entry(ce, info->prev, &info->name);
+ if (cmp)
+ return cmp;
+ }
+ pathlen = info->pathlen;
+ ce_len = ce_namelen(ce);
+
+ /* If ce_len < pathlen then we must have previously hit "name == directory" entry */
+ if (ce_len < pathlen)
+ return -1;
+
+ ce_len -= pathlen;
+ ce_name = ce->name + pathlen;
+
+ len = tree_entry_len(n->path, n->sha1);
+ return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
+}
+
+static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+{
+ int cmp = do_compare_entry(ce, info, n);
+ if (cmp)
+ return cmp;
+
+ /*
+ * Even if the beginning compared identically, the ce should
+ * compare as bigger than a directory leading up to it!
+ */
+ return ce_namelen(ce) > traverse_path_len(info, n);
+}
+
+static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
+{
+ int len = traverse_path_len(info, n);
+ struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
+
+ ce->ce_mode = create_ce_mode(n->mode);
+ ce->ce_flags = create_ce_flags(len, stage);
+ hashcpy(ce->sha1, n->sha1);
+ make_traverse_path(ce->name, info, n);
+
+ return ce;
+}
+
+static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmask, struct cache_entry *src[5],
+ const struct name_entry *names, const struct traverse_info *info)
+{
+ int i;
+ struct unpack_trees_options *o = info->data;
+ unsigned long conflicts;
+
+ /* Do we have *only* directories? Nothing to do */
+ if (mask == dirmask && !src[0])
+ return 0;
+
+ conflicts = info->conflicts;
+ if (o->merge)
+ conflicts >>= 1;
+ conflicts |= dirmask;
+
+ /*
+ * Ok, we've filled in up to any potential index entry in src[0],
+ * now do the rest.
+ */
+ for (i = 0; i < n; i++) {
+ int stage;
+ unsigned int bit = 1ul << i;
+ if (conflicts & bit) {
+ src[i + o->merge] = o->df_conflict_entry;
+ continue;
+ }
+ if (!(mask & bit))
+ continue;
+ if (!o->merge)
+ stage = 0;
+ else if (i + 1 < o->head_idx)
+ stage = 1;
+ else if (i + 1 > o->head_idx)
+ stage = 3;
+ else
+ stage = 2;
+ src[i + o->merge] = create_ce_entry(info, names + i, stage);
+ }
+
+ if (o->merge)
+ return call_unpack_fn(src, o);
+
+ n += o->merge;
+ for (i = 0; i < n; i++)
+ add_entry(o, src[i], 0, 0);
+ return 0;
+}
+
+static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
+{
+ struct cache_entry *src[5] = { NULL, };
+ struct unpack_trees_options *o = info->data;
+ const struct name_entry *p = names;
+
+ /* Find first entry with a real name (we could use "mask" too) */
+ while (!p->mode)
+ p++;
+
+ /* Are we supposed to look at the index too? */
+ if (o->merge) {
+ while (o->pos < o->src_index->cache_nr) {
+ struct cache_entry *ce = o->src_index->cache[o->pos];
+ int cmp = compare_entry(ce, info, p);
+ if (cmp < 0) {
+ if (unpack_index_entry(ce, o) < 0)
+ return -1;
+ continue;
+ }
+ if (!cmp) {
+ o->pos++;
+ if (ce_stage(ce)) {
+ /*
+ * If we skip unmerged index entries, we'll skip this
+ * entry *and* the tree entries associated with it!
+ */
+ if (o->skip_unmerged) {
+ add_entry(o, ce, 0, 0);
+ return mask;
+ }
+ }
+ src[0] = ce;
+ }
+ break;
+ }
+ }
+
+ if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
+ return -1;
+
+ /* Now handle any directories.. */
+ if (dirmask) {
+ unsigned long conflicts = mask & ~dirmask;
+ if (o->merge) {
+ conflicts <<= 1;
+ if (src[0])
+ conflicts |= 1;
+ }
+ if (traverse_trees_recursive(n, dirmask, conflicts,
+ names, info) < 0)
+ return -1;
+ return mask;
+ }
+
+ return mask;
+}
+
+static int unpack_failed(struct unpack_trees_options *o, const char *message)
+{
+ discard_index(&o->result);
+ if (!o->gently) {
+ if (message)
+ return error(message);
+ return -1;
+ }
+ return -1;
+}
+
+int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
+{
static struct cache_entry *dfc;
- memset(&df_conflict_list, 0, sizeof(df_conflict_list));
- df_conflict_list.next = &df_conflict_list;
+ if (len > MAX_UNPACK_TREES)
+ die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
memset(&state, 0, sizeof(state));
state.base_dir = "";
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
+ memset(&o->result, 0, sizeof(o->result));
+ if (o->src_index)
+ o->result.timestamp = o->src_index->timestamp;
o->merge_size = len;
if (!dfc)
@@ -368,30 +345,33 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
o->df_conflict_entry = dfc;
if (len) {
- posns = xmalloc(len * sizeof(struct tree_entry_list *));
- for (i = 0; i < len; i++)
- posns[i] = create_tree_entry_list(t+i);
-
- if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
- o, &df_conflict_list)) {
- if (o->gently) {
- discard_cache();
- read_cache();
- }
- return -1;
- }
+ const char *prefix = o->prefix ? o->prefix : "";
+ struct traverse_info info;
+
+ setup_traverse_info(&info, prefix);
+ info.fn = unpack_callback;
+ info.data = o;
+
+ if (traverse_trees(len, t, &info) < 0)
+ return unpack_failed(o, NULL);
}
- if (o->trivial_merges_only && o->nontrivial_merge) {
- if (o->gently) {
- discard_cache();
- read_cache();
+ /* Any left-over entries in the index? */
+ if (o->merge) {
+ while (o->pos < o->src_index->cache_nr) {
+ struct cache_entry *ce = o->src_index->cache[o->pos];
+ if (unpack_index_entry(ce, o) < 0)
+ return unpack_failed(o, NULL);
}
- return o->gently ? -1 :
- error("Merge requires file-level merging");
}
+ if (o->trivial_merges_only && o->nontrivial_merge)
+ return unpack_failed(o, "Merge requires file-level merging");
+
+ o->src_index = NULL;
check_updates(o);
+ if (o->dst_index)
+ *o->dst_index = o->result;
return 0;
}
@@ -427,7 +407,7 @@ static int verify_uptodate(struct cache_entry *ce,
return 0;
if (!lstat(ce->name, &st)) {
- unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
+ unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID);
if (!changed)
return 0;
/*
@@ -447,10 +427,10 @@ static int verify_uptodate(struct cache_entry *ce,
error("Entry '%s' not uptodate. Cannot merge.", ce->name);
}
-static void invalidate_ce_path(struct cache_entry *ce)
+static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
{
if (ce)
- cache_tree_invalidate_path(active_cache_tree, ce->name);
+ cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
}
/*
@@ -495,12 +475,12 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
* in that directory.
*/
namelen = strlen(ce->name);
- pos = cache_name_pos(ce->name, namelen);
+ pos = index_name_pos(o->src_index, ce->name, namelen);
if (0 <= pos)
return cnt; /* we have it as nondirectory */
pos = -pos - 1;
- for (i = pos; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = pos; i < o->src_index->cache_nr; i++) {
+ struct cache_entry *ce = o->src_index->cache[i];
int len = ce_namelen(ce);
if (len < namelen ||
strncmp(ce->name, ce->name, namelen) ||
@@ -512,7 +492,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
if (!ce_stage(ce)) {
if (verify_uptodate(ce, o))
return -1;
- ce->ce_flags |= CE_REMOVE;
+ add_entry(o, ce, CE_REMOVE, 0);
}
cnt++;
}
@@ -598,9 +578,9 @@ static int verify_absent(struct cache_entry *ce, const char *action,
* delete this path, which is in a subdirectory that
* is being replaced with a blob.
*/
- cnt = cache_name_pos(ce->name, strlen(ce->name));
+ cnt = index_name_pos(&o->result, ce->name, strlen(ce->name));
if (0 <= cnt) {
- struct cache_entry *ce = active_cache[cnt];
+ struct cache_entry *ce = o->result.cache[cnt];
if (ce->ce_flags & CE_REMOVE)
return 0;
}
@@ -615,7 +595,6 @@ static int verify_absent(struct cache_entry *ce, const char *action,
static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
struct unpack_trees_options *o)
{
- merge->ce_flags |= CE_UPDATE;
if (old) {
/*
* See if we can re-use the old CE directly?
@@ -629,38 +608,38 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
} else {
if (verify_uptodate(old, o))
return -1;
- invalidate_ce_path(old);
+ invalidate_ce_path(old, o);
}
}
else {
if (verify_absent(merge, "overwritten", o))
return -1;
- invalidate_ce_path(merge);
+ invalidate_ce_path(merge, o);
}
- merge->ce_flags &= ~CE_STAGEMASK;
- add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+ add_entry(o, merge, CE_UPDATE, CE_STAGEMASK);
return 1;
}
static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
struct unpack_trees_options *o)
{
- if (old) {
- if (verify_uptodate(old, o))
- return -1;
- } else
+ /* Did it exist in the index? */
+ if (!old) {
if (verify_absent(ce, "removed", o))
return -1;
- ce->ce_flags |= CE_REMOVE;
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
- invalidate_ce_path(ce);
+ return 0;
+ }
+ if (verify_uptodate(old, o))
+ return -1;
+ add_entry(o, ce, CE_REMOVE, 0);
+ invalidate_ce_path(ce, o);
return 1;
}
static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
{
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+ add_entry(o, ce, 0, 0);
return 1;
}
@@ -680,9 +659,7 @@ static void show_stage_entry(FILE *o,
}
#endif
-int threeway_merge(struct cache_entry **stages,
- struct unpack_trees_options *o,
- int remove)
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
{
struct cache_entry *index;
struct cache_entry *head;
@@ -759,10 +736,8 @@ int threeway_merge(struct cache_entry **stages,
}
/* #1 */
- if (!head && !remote && any_anc_missing) {
- remove_entry(remove);
+ if (!head && !remote && any_anc_missing)
return 0;
- }
/* Under the new "aggressive" rule, we resolve mostly trivial
* cases that we historically had git-merge-one-file resolve.
@@ -794,10 +769,9 @@ int threeway_merge(struct cache_entry **stages,
if ((head_deleted && remote_deleted) ||
(head_deleted && remote && remote_match) ||
(remote_deleted && head && head_match)) {
- remove_entry(remove);
if (index)
return deleted_entry(index, index, o);
- else if (ce && !head_deleted) {
+ if (ce && !head_deleted) {
if (verify_absent(ce, "removed", o))
return -1;
}
@@ -820,7 +794,6 @@ int threeway_merge(struct cache_entry **stages,
return -1;
}
- remove_entry(remove);
o->nontrivial_merge = 1;
/* #2, #3, #4, #6, #7, #9, #10, #11. */
@@ -855,9 +828,7 @@ int threeway_merge(struct cache_entry **stages,
* "carry forward" rule, please see <Documentation/git-read-tree.txt>.
*
*/
-int twoway_merge(struct cache_entry **src,
- struct unpack_trees_options *o,
- int remove)
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
{
struct cache_entry *current = src[0];
struct cache_entry *oldtree = src[1];
@@ -885,7 +856,6 @@ int twoway_merge(struct cache_entry **src,
}
else if (oldtree && !newtree && same(current, oldtree)) {
/* 10 or 11 */
- remove_entry(remove);
return deleted_entry(oldtree, current, o);
}
else if (oldtree && newtree &&
@@ -895,7 +865,6 @@ int twoway_merge(struct cache_entry **src,
}
else {
/* all other failures */
- remove_entry(remove);
if (oldtree)
return o->gently ? -1 : reject_merge(oldtree);
if (current)
@@ -907,7 +876,6 @@ int twoway_merge(struct cache_entry **src,
}
else if (newtree)
return merged_entry(newtree, current, o);
- remove_entry(remove);
return deleted_entry(oldtree, current, o);
}
@@ -918,8 +886,7 @@ int twoway_merge(struct cache_entry **src,
* stage0 does not have anything there.
*/
int bind_merge(struct cache_entry **src,
- struct unpack_trees_options *o,
- int remove)
+ struct unpack_trees_options *o)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -929,7 +896,7 @@ int bind_merge(struct cache_entry **src,
o->merge_size);
if (a && old)
return o->gently ? -1 :
- error("Entry '%s' overlaps. Cannot bind.", a->name);
+ error("Entry '%s' overlaps with '%s'. Cannot bind.", a->name, old->name);
if (!a)
return keep_entry(old, o);
else
@@ -942,9 +909,7 @@ int bind_merge(struct cache_entry **src,
* The rule is:
* - take the stat information from stage0, take the data from stage1
*/
-int oneway_merge(struct cache_entry **src,
- struct unpack_trees_options *o,
- int remove)
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -953,18 +918,19 @@ int oneway_merge(struct cache_entry **src,
return error("Cannot do a oneway merge of %d trees",
o->merge_size);
- if (!a) {
- remove_entry(remove);
+ if (!a)
return deleted_entry(old, old, o);
- }
+
if (old && same(old, a)) {
+ int update = 0;
if (o->reset) {
struct stat st;
if (lstat(old->name, &st) ||
- ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))
- old->ce_flags |= CE_UPDATE;
+ ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
+ update |= CE_UPDATE;
}
- return keep_entry(old, o);
+ add_entry(o, old, update, 0);
+ return 0;
}
return merged_entry(a, old, o);
}
diff --git a/unpack-trees.h b/unpack-trees.h
index a2df544d04..50453ed20f 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -1,11 +1,12 @@
#ifndef UNPACK_TREES_H
#define UNPACK_TREES_H
+#define MAX_UNPACK_TREES 8
+
struct unpack_trees_options;
typedef int (*merge_fn_t)(struct cache_entry **src,
- struct unpack_trees_options *options,
- int remove);
+ struct unpack_trees_options *options);
struct unpack_trees_options {
int reset;
@@ -28,14 +29,18 @@ struct unpack_trees_options {
struct cache_entry *df_conflict_entry;
void *unpack_data;
+
+ struct index_state *dst_index;
+ const struct index_state *src_index;
+ struct index_state result;
};
extern int unpack_trees(unsigned n, struct tree_desc *t,
struct unpack_trees_options *options);
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
#endif
diff --git a/upload-pack.c b/upload-pack.c
index e5421db9c5..b46dd365ea 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -27,7 +27,8 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
static unsigned long oldest_have;
static int multi_ack, nr_our_refs;
-static int use_thin_pack, use_ofs_delta, no_progress;
+static int use_thin_pack, use_ofs_delta, use_include_tag;
+static int no_progress;
static struct object_array have_obj;
static struct object_array want_obj;
static unsigned int timeout;
@@ -35,6 +36,7 @@ static unsigned int timeout;
* otherwise maximum packet size (up to 65520 bytes).
*/
static int use_sideband;
+static int debug_fd;
static void reset_timeout(void)
{
@@ -161,6 +163,8 @@ static void create_pack_file(void)
argv[arg++] = "--progress";
if (use_ofs_delta)
argv[arg++] = "--delta-base-offset";
+ if (use_include_tag)
+ argv[arg++] = "--include-tag";
argv[arg++] = NULL;
memset(&pack_objects, 0, sizeof(pack_objects));
@@ -444,6 +448,8 @@ static void receive_needs(void)
static char line[1000];
int len, depth = 0;
+ if (debug_fd)
+ write_in_full(debug_fd, "#S\n", 3);
for (;;) {
struct object *o;
unsigned char sha1_buf[20];
@@ -451,6 +457,8 @@ static void receive_needs(void)
reset_timeout();
if (!len)
break;
+ if (debug_fd)
+ write_in_full(debug_fd, line, len);
if (!prefixcmp(line, "shallow ")) {
unsigned char sha1[20];
@@ -489,6 +497,8 @@ static void receive_needs(void)
use_sideband = DEFAULT_PACKET_MAX;
if (strstr(line+45, "no-progress"))
no_progress = 1;
+ if (strstr(line+45, "include-tag"))
+ use_include_tag = 1;
/* We have sent all our refs already, and the other end
* should have chosen out of them; otherwise they are
@@ -506,6 +516,8 @@ static void receive_needs(void)
add_object_array(o, NULL, &want_obj);
}
}
+ if (debug_fd)
+ write_in_full(debug_fd, "#E\n", 3);
if (depth == 0 && shallows.nr == 0)
return;
if (depth > 0) {
@@ -558,7 +570,8 @@ static void receive_needs(void)
static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
static const char *capabilities = "multi_ack thin-pack side-band"
- " side-band-64k ofs-delta shallow no-progress";
+ " side-band-64k ofs-delta shallow no-progress"
+ " include-tag";
struct object *o = parse_object(sha1);
if (!o)
@@ -631,6 +644,8 @@ int main(int argc, char **argv)
die("'%s': unable to chdir or not a git archive", dir);
if (is_repository_shallow())
die("attempt to fetch/clone from a shallow repository");
+ if (getenv("GIT_DEBUG_SEND_PACK"))
+ debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
upload_pack();
return 0;
}
diff --git a/wt-status.c b/wt-status.c
index 32d780af1e..b3fd57b79d 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -7,6 +7,7 @@
#include "diff.h"
#include "revision.h"
#include "diffcore.h"
+#include "quote.h"
int wt_status_relative_paths = 1;
int wt_status_use_color = -1;
@@ -82,51 +83,7 @@ static void wt_status_print_trailer(struct wt_status *s)
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
}
-static char *quote_path(const char *in, int len,
- struct strbuf *out, const char *prefix)
-{
- if (len < 0)
- len = strlen(in);
-
- strbuf_grow(out, len);
- strbuf_setlen(out, 0);
- if (prefix) {
- int off = 0;
- while (prefix[off] && off < len && prefix[off] == in[off])
- if (prefix[off] == '/') {
- prefix += off + 1;
- in += off + 1;
- len -= off + 1;
- off = 0;
- } else
- off++;
-
- for (; *prefix; prefix++)
- if (*prefix == '/')
- strbuf_addstr(out, "../");
- }
-
- for ( ; len > 0; in++, len--) {
- int ch = *in;
-
- switch (ch) {
- case '\n':
- strbuf_addstr(out, "\\n");
- break;
- case '\r':
- strbuf_addstr(out, "\\r");
- break;
- default:
- strbuf_addch(out, ch);
- continue;
- }
- }
-
- if (!out->len)
- strbuf_addstr(out, "./");
-
- return out->buf;
-}
+#define quote_path quote_path_relative
static void wt_status_print_filepair(struct wt_status *s,
int t, struct diff_filepair *p)
@@ -312,27 +269,14 @@ static void wt_status_print_untracked(struct wt_status *s)
static void wt_status_print_verbose(struct wt_status *s)
{
struct rev_info rev;
- int saved_stdout;
-
- fflush(s->fp);
-
- /* Sigh, the entire diff machinery is hardcoded to output to
- * stdout. Do the dup-dance...*/
- saved_stdout = dup(STDOUT_FILENO);
- if (saved_stdout < 0 ||dup2(fileno(s->fp), STDOUT_FILENO) < 0)
- die("couldn't redirect stdout\n");
init_revisions(&rev, NULL);
setup_revisions(0, NULL, &rev, s->reference);
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
rev.diffopt.detect_rename = 1;
+ rev.diffopt.file = s->fp;
+ rev.diffopt.close_file = 0;
run_diff_index(&rev, 1);
-
- fflush(stdout);
-
- if (dup2(saved_stdout, STDOUT_FILENO) < 0)
- die("couldn't restore stdout\n");
- close(saved_stdout);
}
void wt_status_print(struct wt_status *s)
diff --git a/xdiff-interface.c b/xdiff-interface.c
index bba236428a..61dc5c5470 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -152,8 +152,8 @@ int read_mmfile(mmfile_t *ptr, const char *filename)
if ((f = fopen(filename, "rb")) == NULL)
return error("Could not open %s", filename);
sz = xsize_t(st.st_size);
- ptr->ptr = xmalloc(sz);
- if (fread(ptr->ptr, sz, 1, f) != 1)
+ ptr->ptr = xmalloc(sz ? sz : 1);
+ if (sz && fread(ptr->ptr, sz, 1, f) != 1)
return error("Could not read %s", filename);
fclose(f);
ptr->size = sz;