mirror of
https://github.com/git/git.git
synced 2026-03-13 18:33:25 +01:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
@@ -30,7 +30,7 @@ Fixes since v1.6.0.3
|
||||
* 'git status' incorrectly reported a submodule directory as an untracked
|
||||
directory.
|
||||
|
||||
* 'git svn' used deprecated 'git-foo' form of subcommand invocaition.
|
||||
* 'git svn' used deprecated 'git-foo' form of subcommand invocation.
|
||||
|
||||
* 'git update-ref -d' to remove a reference did not honor --no-deref option.
|
||||
|
||||
|
||||
@@ -10,12 +10,34 @@ Fixes since v1.6.0.4
|
||||
* 'git checkout' from an un-checked-out state did not allow switching out
|
||||
of the current branch.
|
||||
|
||||
* 'git diff' always allowed GIT_EXTERNAL_DIFF and --no-ext-diff was no-op for
|
||||
the command.
|
||||
|
||||
* 'git fast-export' did not export all tags.
|
||||
|
||||
* 'git ls-files --with-tree=<tree>' did not work with options other
|
||||
than -c, most notably with -m.
|
||||
|
||||
* 'git pack-objects' did not make its best effort to honor --max-pack-size
|
||||
option when a single first object already busted the given limit and
|
||||
placed many objects in a single pack.
|
||||
|
||||
* 'git-p4' fast import frontend was too eager to trigger its keyword expansion
|
||||
logic, even on a keyword-looking string that does not have closing '$' on the
|
||||
same line.
|
||||
|
||||
* 'git push $there' when the remote $there is defined in $GIT_DIR/branches/$there
|
||||
behaves more like what cg-push from Cogito used to work.
|
||||
|
||||
* 'git tag' did not complain when given mutually incompatible set of options.
|
||||
|
||||
* 'make check' cannot be run without sparse; people may have meant to say
|
||||
'make test' instead, so suggest that.
|
||||
|
||||
* Many unsafe call to sprintf() style varargs functions are corrected.
|
||||
|
||||
* Also contains quite a few documentation updates.
|
||||
|
||||
--
|
||||
O=v1.6.0.4-39-g27f6496
|
||||
|
||||
|
||||
@@ -55,9 +55,9 @@ on.
|
||||
to a non-zero value to accept the suggestion when git can uniquely
|
||||
guess.
|
||||
|
||||
* The packfile machinery hopefully is more robust when dealilng with
|
||||
* The packfile machinery hopefully is more robust when dealing with
|
||||
corrupt packs if redundant objects involved in the corruption are
|
||||
available elsehwere.
|
||||
available elsewhere.
|
||||
|
||||
* "git add -N path..." adds the named paths as an empty blob, so that
|
||||
subsequent "git diff" will show a diff as if they are creation events.
|
||||
@@ -157,7 +157,7 @@ on.
|
||||
* "git log" learned "--source" to show what ref each commit was reached
|
||||
from.
|
||||
|
||||
* "git log" also learned "--simplify-by-decration" to show the
|
||||
* "git log" also learned "--simplify-by-decoration" to show the
|
||||
birds-eye-view of the topology of the history.
|
||||
|
||||
* "git log --pretty=format:" learned "%d" format element that inserts
|
||||
|
||||
@@ -413,6 +413,15 @@ data writes properly, but can be useful for filesystems that do not use
|
||||
journalling (traditional UNIX filesystems) or that only journal metadata
|
||||
and not file contents (OS X's HFS+, or Linux ext3 with "data=writeback").
|
||||
|
||||
core.preloadindex::
|
||||
Enable parallel index preload for operations like 'git diff'
|
||||
+
|
||||
This can speed up operations like 'git diff' and 'git status' especially
|
||||
on filesystems like NFS that have weak caching semantics and thus
|
||||
relatively high IO latencies. With this set to 'true', git will do the
|
||||
index comparison to the filesystem data in parallel, allowing
|
||||
overlapping IO's.
|
||||
|
||||
alias.*::
|
||||
Command aliases for the linkgit:git[1] command wrapper - e.g.
|
||||
after defining "alias.last = cat-file commit HEAD", the invocation
|
||||
@@ -572,9 +581,6 @@ color.status.<slot>::
|
||||
to red). The values of these variables may be specified as in
|
||||
color.branch.<slot>.
|
||||
|
||||
commit.template::
|
||||
Specify a file to use as the template for new commit messages.
|
||||
|
||||
color.ui::
|
||||
When set to `always`, always use colors in all git commands which
|
||||
are capable of colored output. When false (or `never`), never. When
|
||||
@@ -582,6 +588,9 @@ color.ui::
|
||||
terminal. When more specific variables of color.* are set, they always
|
||||
take precedence over this setting. Defaults to false.
|
||||
|
||||
commit.template::
|
||||
Specify a file to use as the template for new commit messages.
|
||||
|
||||
diff.autorefreshindex::
|
||||
When using 'git-diff' to compare with work tree
|
||||
files, do not consider stat-only change as changed.
|
||||
@@ -714,18 +723,6 @@ gc.rerereunresolved::
|
||||
kept for this many days when 'git-rerere gc' is run.
|
||||
The default is 15 days. See linkgit:git-rerere[1].
|
||||
|
||||
rerere.autoupdate::
|
||||
When set to true, `git-rerere` updates the index with the
|
||||
resulting contents after it cleanly resolves conflicts using
|
||||
previously recorded resolution. Defaults to false.
|
||||
|
||||
rerere.enabled::
|
||||
Activate recording of resolved conflicts, so that identical
|
||||
conflict hunks can be resolved automatically, should they
|
||||
be encountered again. linkgit:git-rerere[1] command is by
|
||||
default enabled if you create `rr-cache` directory under
|
||||
`$GIT_DIR`, but can be disabled by setting this option to false.
|
||||
|
||||
gitcvs.enabled::
|
||||
Whether the CVS server interface is enabled for this repository.
|
||||
See linkgit:git-cvsserver[1].
|
||||
@@ -832,7 +829,7 @@ gui.fastcopyblame::
|
||||
repositories at the expense of less thorough copy detection.
|
||||
|
||||
gui.copyblamethreshold::
|
||||
Specifies the theshold to use in 'git gui blame' original location
|
||||
Specifies the threshold to use in 'git gui blame' original location
|
||||
detection, measured in alphanumeric characters. See the
|
||||
linkgit:git-blame[1] manual for more information on copy detection.
|
||||
|
||||
@@ -917,6 +914,10 @@ i18n.logOutputEncoding::
|
||||
Character encoding the commit messages are converted to when
|
||||
running 'git-log' and friends.
|
||||
|
||||
imap::
|
||||
The configuration variables in the 'imap' section are described
|
||||
in linkgit:git-imap-send[1].
|
||||
|
||||
instaweb.browser::
|
||||
Specify the program that will be used to browse your working
|
||||
repository in gitweb. See linkgit:git-instaweb[1].
|
||||
@@ -952,8 +953,6 @@ man.viewer::
|
||||
Specify the programs that may be used to display help in the
|
||||
'man' format. See linkgit:git-help[1].
|
||||
|
||||
include::merge-config.txt[]
|
||||
|
||||
man.<tool>.cmd::
|
||||
Specify the command to invoke the specified man viewer. The
|
||||
specified command is evaluated in shell with the man page
|
||||
@@ -963,13 +962,7 @@ man.<tool>.path::
|
||||
Override the path for the given tool that may be used to
|
||||
display help in the 'man' format. See linkgit:git-help[1].
|
||||
|
||||
merge.conflictstyle::
|
||||
Specify the style in which conflicted hunks are written out to
|
||||
working tree files upon merge. The default is "merge", which
|
||||
shows `<<<<<<<` conflict marker, change made by one side,
|
||||
`=======` marker, change made by the other side, and then
|
||||
`>>>>>>>` marker. An alternate style, "diff3", adds `|||||||`
|
||||
marker and the original text before `=======` marker.
|
||||
include::merge-config.txt[]
|
||||
|
||||
mergetool.<tool>.path::
|
||||
Override the path for the given tool. This is useful in case
|
||||
@@ -1079,6 +1072,41 @@ pull.octopus::
|
||||
pull.twohead::
|
||||
The default merge strategy to use when pulling a single branch.
|
||||
|
||||
receive.fsckObjects::
|
||||
If it is set to true, git-receive-pack will check all received
|
||||
objects. It will abort in the case of a malformed object or a
|
||||
broken link. The result of an abort are only dangling objects.
|
||||
Defaults to false.
|
||||
|
||||
receive.unpackLimit::
|
||||
If the number of objects received in a push is below this
|
||||
limit then the objects will be unpacked into loose object
|
||||
files. However if the number of received objects equals or
|
||||
exceeds this limit then the received pack will be stored as
|
||||
a pack, after adding any missing delta bases. Storing the
|
||||
pack from a push can make the push operation complete faster,
|
||||
especially on slow filesystems. If not set, the value of
|
||||
`transfer.unpackLimit` is used instead.
|
||||
|
||||
receive.denyDeletes::
|
||||
If set to true, git-receive-pack will deny a ref update that deletes
|
||||
the ref. Use this to prevent such a ref deletion via a push.
|
||||
|
||||
receive.denyCurrentBranch::
|
||||
If set to true or "refuse", receive-pack will deny a ref update
|
||||
to the currently checked out branch of a non-bare repository.
|
||||
Such a push is potentially dangerous because it brings the HEAD
|
||||
out of sync with the index and working tree. If set to "warn",
|
||||
print a warning of such a push to stderr, but allow the push to
|
||||
proceed. If set to false or "ignore", allow such pushes with no
|
||||
message. Defaults to "warn".
|
||||
|
||||
receive.denyNonFastForwards::
|
||||
If set to true, git-receive-pack will deny a ref update which is
|
||||
not a fast forward. Use this to prevent such an update via a push,
|
||||
even if that push is forced. This configuration variable is
|
||||
set when initializing a shared repository.
|
||||
|
||||
remote.<name>.url::
|
||||
The URL of a remote repository. See linkgit:git-fetch[1] or
|
||||
linkgit:git-push[1].
|
||||
@@ -1128,6 +1156,18 @@ repack.usedeltabaseoffset::
|
||||
"false" and repack. Access from old git versions over the
|
||||
native protocol are unaffected by this option.
|
||||
|
||||
rerere.autoupdate::
|
||||
When set to true, `git-rerere` updates the index with the
|
||||
resulting contents after it cleanly resolves conflicts using
|
||||
previously recorded resolution. Defaults to false.
|
||||
|
||||
rerere.enabled::
|
||||
Activate recording of resolved conflicts, so that identical
|
||||
conflict hunks can be resolved automatically, should they
|
||||
be encountered again. linkgit:git-rerere[1] command is by
|
||||
default enabled if you create `rr-cache` directory under
|
||||
`$GIT_DIR`, but can be disabled by setting this option to false.
|
||||
|
||||
showbranch.default::
|
||||
The default set of branches for linkgit:git-show-branch[1].
|
||||
See linkgit:git-show-branch[1].
|
||||
@@ -1164,6 +1204,11 @@ tar.umask::
|
||||
archiving user's umask will be used instead. See umask(2) and
|
||||
linkgit:git-archive[1].
|
||||
|
||||
transfer.unpackLimit::
|
||||
When `fetch.unpackLimit` or `receive.unpackLimit` are
|
||||
not set, the value of this variable is used instead.
|
||||
The default value is 100.
|
||||
|
||||
url.<base>.insteadOf::
|
||||
Any URL that starts with this value will be rewritten to
|
||||
start, instead, with <base>. In cases where some site serves a
|
||||
@@ -1192,50 +1237,6 @@ user.signingkey::
|
||||
unchanged to gpg's --local-user parameter, so you may specify a key
|
||||
using any method that gpg supports.
|
||||
|
||||
imap::
|
||||
The configuration variables in the 'imap' section are described
|
||||
in linkgit:git-imap-send[1].
|
||||
|
||||
receive.fsckObjects::
|
||||
If it is set to true, git-receive-pack will check all received
|
||||
objects. It will abort in the case of a malformed object or a
|
||||
broken link. The result of an abort are only dangling objects.
|
||||
Defaults to false.
|
||||
|
||||
receive.unpackLimit::
|
||||
If the number of objects received in a push is below this
|
||||
limit then the objects will be unpacked into loose object
|
||||
files. However if the number of received objects equals or
|
||||
exceeds this limit then the received pack will be stored as
|
||||
a pack, after adding any missing delta bases. Storing the
|
||||
pack from a push can make the push operation complete faster,
|
||||
especially on slow filesystems. If not set, the value of
|
||||
`transfer.unpackLimit` is used instead.
|
||||
|
||||
receive.denyDeletes::
|
||||
If set to true, git-receive-pack will deny a ref update that deletes
|
||||
the ref. Use this to prevent such a ref deletion via a push.
|
||||
|
||||
receive.denyNonFastForwards::
|
||||
If set to true, git-receive-pack will deny a ref update which is
|
||||
not a fast forward. Use this to prevent such an update via a push,
|
||||
even if that push is forced. This configuration variable is
|
||||
set when initializing a shared repository.
|
||||
|
||||
receive.denyCurrentBranch::
|
||||
If set to true or "refuse", receive-pack will deny a ref update
|
||||
to the currently checked out branch of a non-bare repository.
|
||||
Such a push is potentially dangerous because it brings the HEAD
|
||||
out of sync with the index and working tree. If set to "warn",
|
||||
print a warning of such a push to stderr, but allow the push to
|
||||
proceed. If set to false or "ignore", allow such pushes with no
|
||||
message. Defaults to "warn".
|
||||
|
||||
transfer.unpackLimit::
|
||||
When `fetch.unpackLimit` or `receive.unpackLimit` are
|
||||
not set, the value of this variable is used instead.
|
||||
The default value is 100.
|
||||
|
||||
web.browser::
|
||||
Specify a web browser that may be used by some commands.
|
||||
Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
|
||||
|
||||
@@ -98,7 +98,7 @@ OPTIONS
|
||||
Record only the fact that the path will be added later. An entry
|
||||
for the path is placed in the index with no content. This is
|
||||
useful for, among other things, showing the unstaged content of
|
||||
such files with 'git diff' and commiting them with 'git commit
|
||||
such files with 'git diff' and committing them with 'git commit
|
||||
-a'.
|
||||
|
||||
--refresh::
|
||||
|
||||
@@ -55,13 +55,12 @@ OPTIONS
|
||||
|
||||
-n::
|
||||
--no-commit::
|
||||
Usually the command automatically creates a commit with
|
||||
a commit log message stating which commit was
|
||||
cherry-picked. This flag applies the change necessary
|
||||
to cherry-pick the named commit to your working tree
|
||||
and the index, but does not make the commit. In addition,
|
||||
when this option is used, your index does not have to match
|
||||
the HEAD commit. The cherry-pick is done against the
|
||||
Usually the command automatically creates a commit.
|
||||
This flag applies the change necessary to cherry-pick
|
||||
the named commit to your working tree and the index,
|
||||
but does not make the commit. In addition, when this
|
||||
option is used, your index does not have to match the
|
||||
HEAD commit. The cherry-pick is done against the
|
||||
beginning state of your index.
|
||||
+
|
||||
This is useful when cherry-picking more than one commits'
|
||||
|
||||
@@ -29,7 +29,8 @@ The content to be added can be specified in several ways:
|
||||
|
||||
3. by listing files as arguments to the 'commit' command, in which
|
||||
case the commit will ignore changes staged in the index, and instead
|
||||
record the current content of the listed files;
|
||||
record the current content of the listed files (which must already
|
||||
be known to git);
|
||||
|
||||
4. by using the -a switch with the 'commit' command to automatically
|
||||
"add" changes from all known files (i.e. all files that are already
|
||||
@@ -94,7 +95,7 @@ OPTIONS
|
||||
|
||||
-s::
|
||||
--signoff::
|
||||
Add Signed-off-by line by the commiter at the end of the commit
|
||||
Add Signed-off-by line by the committer at the end of the commit
|
||||
log message.
|
||||
|
||||
-n::
|
||||
|
||||
@@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git send-email' [options] <file|directory> [... file|directory]
|
||||
'git send-email' [options] <file|directory|rev-list options>...
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@@ -37,9 +37,23 @@ The --bcc option must be repeated for each user you want on the bcc list.
|
||||
+
|
||||
The --cc option must be repeated for each user you want on the cc list.
|
||||
|
||||
--annotate::
|
||||
Review each patch you're about to send in an editor. The setting
|
||||
'sendemail.multiedit' defines if this will spawn one editor per patch
|
||||
or one for all of them at once.
|
||||
|
||||
--compose::
|
||||
Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
|
||||
introductory message for the patch series.
|
||||
+
|
||||
When compose is in used, git send-email gets less interactive will use the
|
||||
values of the headers you set there. If the body of the email (what you type
|
||||
after the headers and a blank line) only contains blank (or GIT: prefixed)
|
||||
lines, the summary won't be sent, but git-send-email will still use the
|
||||
Headers values if you don't removed them.
|
||||
+
|
||||
If it wasn't able to see a header in the summary it will ask you about it
|
||||
interactively after quitting your editor.
|
||||
|
||||
--from::
|
||||
Specify the sender of the emails. This will default to
|
||||
@@ -183,6 +197,12 @@ Administering
|
||||
--[no-]validate::
|
||||
Perform sanity checks on patches.
|
||||
Currently, validation means the following:
|
||||
|
||||
--[no-]format-patch::
|
||||
When an argument may be understood either as a reference or as a file name,
|
||||
choose to understand it as a format-patch argument ('--format-patch')
|
||||
or as a file name ('--no-format-patch'). By default, when such a conflict
|
||||
occurs, git send-email will fail.
|
||||
+
|
||||
--
|
||||
* Warn of patches that contain lines longer than 998 characters; this
|
||||
@@ -204,6 +224,12 @@ sendemail.aliasfiletype::
|
||||
Format of the file(s) specified in sendemail.aliasesfile. Must be
|
||||
one of 'mutt', 'mailrc', 'pine', or 'gnus'.
|
||||
|
||||
sendemail.multiedit::
|
||||
If true (default), a single editor instance will be spawned to edit
|
||||
files you have to edit (patches when '--annotate' is used, and the
|
||||
summary when '--compose' is used). If false, files will be edited one
|
||||
after the other, spawning a new editor each time.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
@@ -109,7 +109,7 @@ COMMANDS
|
||||
|
||||
This works similarly to `svn update` or 'git-pull' except that
|
||||
it preserves linear history with 'git-rebase' instead of
|
||||
'git-merge' for ease of dcommiting with 'git-svn'.
|
||||
'git-merge' for ease of dcommitting with 'git-svn'.
|
||||
|
||||
This accepts all options that 'git-svn fetch' and 'git-rebase'
|
||||
accept. However, '--fetch-all' only fetches from the current
|
||||
|
||||
@@ -1693,6 +1693,7 @@ SEE ALSO
|
||||
linkgit:gittutorial[7],
|
||||
linkgit:gittutorial-2[7],
|
||||
linkgit:gitcvs-migration[7],
|
||||
linkgit:git-help[1],
|
||||
link:everyday.html[Everyday git],
|
||||
link:user-manual.html[The Git User's Manual]
|
||||
|
||||
|
||||
@@ -425,6 +425,7 @@ linkgit:gittutorial[7],
|
||||
linkgit:gitcvs-migration[7],
|
||||
linkgit:gitcore-tutorial[7],
|
||||
linkgit:gitglossary[7],
|
||||
linkgit:git-help[1],
|
||||
link:everyday.html[Everyday git],
|
||||
link:user-manual.html[The Git User's Manual]
|
||||
|
||||
|
||||
@@ -26,6 +26,15 @@ First, note that you can get documentation for a command such as
|
||||
$ man git-log
|
||||
------------------------------------------------
|
||||
|
||||
or:
|
||||
|
||||
------------------------------------------------
|
||||
$ git help log
|
||||
------------------------------------------------
|
||||
|
||||
With the latter, you can use the manual viewer of your choice; see
|
||||
linkgit:git-help[1] for more information.
|
||||
|
||||
It is a good idea to introduce yourself to git with your name and
|
||||
public email address before doing any operation. The easiest
|
||||
way to do so is:
|
||||
@@ -653,6 +662,7 @@ linkgit:gittutorial-2[7],
|
||||
linkgit:gitcvs-migration[7],
|
||||
linkgit:gitcore-tutorial[7],
|
||||
linkgit:gitglossary[7],
|
||||
linkgit:git-help[1],
|
||||
link:everyday.html[Everyday git],
|
||||
link:user-manual.html[The Git User's Manual]
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
merge.stat::
|
||||
Whether to print the diffstat between ORIG_HEAD and the merge result
|
||||
at the end of the merge. True by default.
|
||||
merge.conflictstyle::
|
||||
Specify the style in which conflicted hunks are written out to
|
||||
working tree files upon merge. The default is "merge", which
|
||||
shows `<<<<<<<` conflict marker, change made by one side,
|
||||
`=======` marker, change made by the other side, and then
|
||||
`>>>>>>>` marker. An alternate style, "diff3", adds `|||||||`
|
||||
marker and the original text before `=======` marker.
|
||||
|
||||
merge.log::
|
||||
Whether to include summaries of merged commits in newly created
|
||||
@@ -11,6 +15,10 @@ merge.renameLimit::
|
||||
during a merge; if not specified, defaults to the value of
|
||||
diff.renameLimit.
|
||||
|
||||
merge.stat::
|
||||
Whether to print the diffstat between ORIG_HEAD and the merge result
|
||||
at the end of the merge. True by default.
|
||||
|
||||
merge.tool::
|
||||
Controls which merge resolution program is used by
|
||||
linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3",
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
-q::
|
||||
--quiet::
|
||||
Operate quietly.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be verbose.
|
||||
|
||||
--stat::
|
||||
Show a diffstat at the end of the merge. The diffstat is also
|
||||
controlled by the configuration option merge.stat.
|
||||
|
||||
@@ -222,6 +222,21 @@ endif::git-rev-list[]
|
||||
Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
|
||||
command line as '<commit>'.
|
||||
|
||||
--branches::
|
||||
|
||||
Pretend as if all the refs in `$GIT_DIR/refs/heads` are listed
|
||||
on the command line as '<commit>'.
|
||||
|
||||
--tags::
|
||||
|
||||
Pretend as if all the refs in `$GIT_DIR/refs/tags` are listed
|
||||
on the command line as '<commit>'.
|
||||
|
||||
--remotes::
|
||||
|
||||
Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
|
||||
on the command line as '<commit>'.
|
||||
|
||||
ifdef::git-rev-list[]
|
||||
--stdin::
|
||||
|
||||
|
||||
@@ -205,6 +205,13 @@ In order to facilitate caching and to make it possible to give
|
||||
parameters to the callback, `strbuf_expand()` passes a context pointer,
|
||||
which can be used by the programmer of the callback as she sees fit.
|
||||
|
||||
`strbuf_expand_dict_cb`::
|
||||
|
||||
Used as callback for `strbuf_expand()`, expects an array of
|
||||
struct strbuf_expand_dict_entry as context, i.e. pairs of
|
||||
placeholder and replacement string. The array needs to be
|
||||
terminated by an entry with placeholder set to NULL.
|
||||
|
||||
`strbuf_addf`::
|
||||
|
||||
Add a formatted string to the buffer.
|
||||
|
||||
@@ -18,12 +18,22 @@ People needing to do actual development will also want to read
|
||||
Further chapters cover more specialized topics.
|
||||
|
||||
Comprehensive reference documentation is available through the man
|
||||
pages. For a command such as "git clone <repo>", just use
|
||||
pages, or linkgit:git-help[1] command. For example, for the command
|
||||
"git clone <repo>", you can either use:
|
||||
|
||||
------------------------------------------------
|
||||
$ man git-clone
|
||||
------------------------------------------------
|
||||
|
||||
or:
|
||||
|
||||
------------------------------------------------
|
||||
$ git help clone
|
||||
------------------------------------------------
|
||||
|
||||
With the latter, you can use the manual viewer of your choice; see
|
||||
linkgit:git-help[1] for more information.
|
||||
|
||||
See also <<git-quick-start>> for a brief overview of git commands,
|
||||
without any explanation.
|
||||
|
||||
@@ -536,7 +546,7 @@ $ git bisect skip
|
||||
-------------------------------------------------
|
||||
|
||||
In this case, though, git may not eventually be able to tell the first
|
||||
bad one between some first skipped commits and a latter bad commit.
|
||||
bad one between some first skipped commits and a later bad commit.
|
||||
|
||||
There are also ways to automate the bisecting process if you have a
|
||||
test script that can tell a good from a bad commit. See
|
||||
|
||||
2
Makefile
2
Makefile
@@ -437,7 +437,6 @@ LIB_OBJS += grep.o
|
||||
LIB_OBJS += hash.o
|
||||
LIB_OBJS += help.o
|
||||
LIB_OBJS += ident.o
|
||||
LIB_OBJS += interpolate.o
|
||||
LIB_OBJS += levenshtein.o
|
||||
LIB_OBJS += list-objects.o
|
||||
LIB_OBJS += ll-merge.o
|
||||
@@ -496,6 +495,7 @@ LIB_OBJS += write_or_die.o
|
||||
LIB_OBJS += ws.o
|
||||
LIB_OBJS += wt-status.o
|
||||
LIB_OBJS += xdiff-interface.o
|
||||
LIB_OBJS += preload-index.o
|
||||
|
||||
BUILTIN_OBJS += builtin-add.o
|
||||
BUILTIN_OBJS += builtin-annotate.o
|
||||
|
||||
@@ -97,7 +97,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||
unsigned char sha1[20];
|
||||
char *name = NULL;
|
||||
const char *fmt, *remote;
|
||||
char section[PATH_MAX];
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
@@ -165,11 +164,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||
argv[i]);
|
||||
ret = 1;
|
||||
} else {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
printf("Deleted %sbranch %s.\n", remote, argv[i]);
|
||||
snprintf(section, sizeof(section), "branch.%s",
|
||||
argv[i]);
|
||||
if (git_config_rename_section(section, NULL) < 0)
|
||||
strbuf_addf(&buf, "branch.%s", argv[i]);
|
||||
if (git_config_rename_section(buf.buf, NULL) < 0)
|
||||
warning("Update of config-file failed");
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ static int ref_cmp(const void *r1, const void *r2)
|
||||
return strcmp(c1->name, c2->name);
|
||||
}
|
||||
|
||||
static void fill_tracking_info(char *stat, const char *branch_name)
|
||||
static void fill_tracking_info(struct strbuf *stat, const char *branch_name)
|
||||
{
|
||||
int ours, theirs;
|
||||
struct branch *branch = branch_get(branch_name);
|
||||
@@ -287,11 +287,11 @@ static void fill_tracking_info(char *stat, const char *branch_name)
|
||||
if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
|
||||
return;
|
||||
if (!ours)
|
||||
sprintf(stat, "[behind %d] ", theirs);
|
||||
strbuf_addf(stat, "[behind %d] ", theirs);
|
||||
else if (!theirs)
|
||||
sprintf(stat, "[ahead %d] ", ours);
|
||||
strbuf_addf(stat, "[ahead %d] ", ours);
|
||||
else
|
||||
sprintf(stat, "[ahead %d, behind %d] ", ours, theirs);
|
||||
strbuf_addf(stat, "[ahead %d, behind %d] ", ours, theirs);
|
||||
}
|
||||
|
||||
static int matches_merge_filter(struct commit *commit)
|
||||
@@ -334,11 +334,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
struct strbuf subject = STRBUF_INIT;
|
||||
struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
|
||||
const char *sub = " **** invalid ref ****";
|
||||
char stat[128];
|
||||
|
||||
stat[0] = '\0';
|
||||
|
||||
commit = item->commit;
|
||||
if (commit && !parse_commit(commit)) {
|
||||
@@ -348,13 +345,14 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
}
|
||||
|
||||
if (item->kind == REF_LOCAL_BRANCH)
|
||||
fill_tracking_info(stat, item->name);
|
||||
fill_tracking_info(&stat, item->name);
|
||||
|
||||
printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
|
||||
maxwidth, item->name,
|
||||
branch_get_color(COLOR_BRANCH_RESET),
|
||||
find_unique_abbrev(item->commit->object.sha1, abbrev),
|
||||
stat, sub);
|
||||
stat.buf, sub);
|
||||
strbuf_release(&stat);
|
||||
strbuf_release(&subject);
|
||||
} else {
|
||||
printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
|
||||
@@ -426,42 +424,45 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
|
||||
|
||||
static void rename_branch(const char *oldname, const char *newname, int force)
|
||||
{
|
||||
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
|
||||
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
|
||||
unsigned char sha1[20];
|
||||
char oldsection[PATH_MAX], newsection[PATH_MAX];
|
||||
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
|
||||
|
||||
if (!oldname)
|
||||
die("cannot rename the current branch while not on any.");
|
||||
|
||||
if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
|
||||
die("Old branchname too long");
|
||||
strbuf_addf(&oldref, "refs/heads/%s", oldname);
|
||||
|
||||
if (check_ref_format(oldref))
|
||||
die("Invalid branch name: %s", oldref);
|
||||
if (check_ref_format(oldref.buf))
|
||||
die("Invalid branch name: %s", oldref.buf);
|
||||
|
||||
if (snprintf(newref, sizeof(newref), "refs/heads/%s", newname) > sizeof(newref))
|
||||
die("New branchname too long");
|
||||
strbuf_addf(&newref, "refs/heads/%s", newname);
|
||||
|
||||
if (check_ref_format(newref))
|
||||
die("Invalid branch name: %s", newref);
|
||||
if (check_ref_format(newref.buf))
|
||||
die("Invalid branch name: %s", newref.buf);
|
||||
|
||||
if (resolve_ref(newref, sha1, 1, NULL) && !force)
|
||||
if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
|
||||
die("A branch named '%s' already exists.", newname);
|
||||
|
||||
snprintf(logmsg, sizeof(logmsg), "Branch: renamed %s to %s",
|
||||
oldref, newref);
|
||||
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
|
||||
oldref.buf, newref.buf);
|
||||
|
||||
if (rename_ref(oldref, newref, logmsg))
|
||||
if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
|
||||
die("Branch rename failed");
|
||||
strbuf_release(&logmsg);
|
||||
|
||||
/* no need to pass logmsg here as HEAD didn't really move */
|
||||
if (!strcmp(oldname, head) && create_symref("HEAD", newref, NULL))
|
||||
if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
|
||||
die("Branch renamed to %s, but HEAD is not updated!", newname);
|
||||
|
||||
snprintf(oldsection, sizeof(oldsection), "branch.%s", oldref + 11);
|
||||
snprintf(newsection, sizeof(newsection), "branch.%s", newref + 11);
|
||||
if (git_config_rename_section(oldsection, newsection) < 0)
|
||||
strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
|
||||
strbuf_release(&oldref);
|
||||
strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
|
||||
strbuf_release(&newref);
|
||||
if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
|
||||
die("Branch is renamed, but update of config-file failed");
|
||||
strbuf_release(&oldsection);
|
||||
strbuf_release(&newsection);
|
||||
}
|
||||
|
||||
static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
|
||||
|
||||
@@ -553,7 +553,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
|
||||
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
|
||||
describe_detached_head("Previous HEAD position was", old.commit);
|
||||
|
||||
if (!old.commit) {
|
||||
if (!old.commit && !opts->force) {
|
||||
if (!opts->quiet) {
|
||||
fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
|
||||
fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
|
||||
|
||||
109
builtin-clone.c
109
builtin-clone.c
@@ -134,9 +134,9 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
|
||||
}
|
||||
|
||||
if (is_bare) {
|
||||
char *result = xmalloc(end - start + 5);
|
||||
sprintf(result, "%.*s.git", (int)(end - start), start);
|
||||
return result;
|
||||
struct strbuf result = STRBUF_INIT;
|
||||
strbuf_addf(&result, "%.*s.git", (int)(end - start), start);
|
||||
return strbuf_detach(&result, 0);
|
||||
}
|
||||
|
||||
return xstrndup(start, end - start);
|
||||
@@ -183,36 +183,38 @@ static void setup_reference(const char *repo)
|
||||
free(ref_git_copy);
|
||||
}
|
||||
|
||||
static void copy_or_link_directory(char *src, char *dest)
|
||||
static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
|
||||
{
|
||||
struct dirent *de;
|
||||
struct stat buf;
|
||||
int src_len, dest_len;
|
||||
DIR *dir;
|
||||
|
||||
dir = opendir(src);
|
||||
dir = opendir(src->buf);
|
||||
if (!dir)
|
||||
die("failed to open %s\n", src);
|
||||
die("failed to open %s\n", src->buf);
|
||||
|
||||
if (mkdir(dest, 0777)) {
|
||||
if (mkdir(dest->buf, 0777)) {
|
||||
if (errno != EEXIST)
|
||||
die("failed to create directory %s\n", dest);
|
||||
else if (stat(dest, &buf))
|
||||
die("failed to stat %s\n", dest);
|
||||
die("failed to create directory %s\n", dest->buf);
|
||||
else if (stat(dest->buf, &buf))
|
||||
die("failed to stat %s\n", dest->buf);
|
||||
else if (!S_ISDIR(buf.st_mode))
|
||||
die("%s exists and is not a directory\n", dest);
|
||||
die("%s exists and is not a directory\n", dest->buf);
|
||||
}
|
||||
|
||||
src_len = strlen(src);
|
||||
src[src_len] = '/';
|
||||
dest_len = strlen(dest);
|
||||
dest[dest_len] = '/';
|
||||
strbuf_addch(src, '/');
|
||||
src_len = src->len;
|
||||
strbuf_addch(dest, '/');
|
||||
dest_len = dest->len;
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
strcpy(src + src_len + 1, de->d_name);
|
||||
strcpy(dest + dest_len + 1, de->d_name);
|
||||
if (stat(src, &buf)) {
|
||||
warning ("failed to stat %s\n", src);
|
||||
strbuf_setlen(src, src_len);
|
||||
strbuf_addstr(src, de->d_name);
|
||||
strbuf_setlen(dest, dest_len);
|
||||
strbuf_addstr(dest, de->d_name);
|
||||
if (stat(src->buf, &buf)) {
|
||||
warning ("failed to stat %s\n", src->buf);
|
||||
continue;
|
||||
}
|
||||
if (S_ISDIR(buf.st_mode)) {
|
||||
@@ -221,17 +223,17 @@ static void copy_or_link_directory(char *src, char *dest)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlink(dest) && errno != ENOENT)
|
||||
die("failed to unlink %s\n", dest);
|
||||
if (unlink(dest->buf) && errno != ENOENT)
|
||||
die("failed to unlink %s\n", dest->buf);
|
||||
if (!option_no_hardlinks) {
|
||||
if (!link(src, dest))
|
||||
if (!link(src->buf, dest->buf))
|
||||
continue;
|
||||
if (option_local)
|
||||
die("failed to create link %s\n", dest);
|
||||
die("failed to create link %s\n", dest->buf);
|
||||
option_no_hardlinks = 1;
|
||||
}
|
||||
if (copy_file(dest, src, 0666))
|
||||
die("failed to copy file to %s\n", dest);
|
||||
if (copy_file(dest->buf, src->buf, 0666))
|
||||
die("failed to copy file to %s\n", dest->buf);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
@@ -240,17 +242,19 @@ static const struct ref *clone_local(const char *src_repo,
|
||||
const char *dest_repo)
|
||||
{
|
||||
const struct ref *ret;
|
||||
char src[PATH_MAX];
|
||||
char dest[PATH_MAX];
|
||||
struct strbuf src = STRBUF_INIT;
|
||||
struct strbuf dest = STRBUF_INIT;
|
||||
struct remote *remote;
|
||||
struct transport *transport;
|
||||
|
||||
if (option_shared)
|
||||
add_to_alternates_file(src_repo);
|
||||
else {
|
||||
snprintf(src, PATH_MAX, "%s/objects", src_repo);
|
||||
snprintf(dest, PATH_MAX, "%s/objects", dest_repo);
|
||||
copy_or_link_directory(src, dest);
|
||||
strbuf_addf(&src, "%s/objects", src_repo);
|
||||
strbuf_addf(&dest, "%s/objects", dest_repo);
|
||||
copy_or_link_directory(&src, &dest);
|
||||
strbuf_release(&src);
|
||||
strbuf_release(&dest);
|
||||
}
|
||||
|
||||
remote = remote_get(src_repo);
|
||||
@@ -354,8 +358,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
const char *repo_name, *repo, *work_tree, *git_dir;
|
||||
char *path, *dir;
|
||||
const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
|
||||
char branch_top[256], key[256], value[256];
|
||||
struct strbuf reflog_msg = STRBUF_INIT;
|
||||
struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
|
||||
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
|
||||
struct transport *transport = NULL;
|
||||
char *src_ref_prefix = "refs/heads/";
|
||||
|
||||
@@ -459,35 +463,36 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
if (option_bare) {
|
||||
if (option_mirror)
|
||||
src_ref_prefix = "refs/";
|
||||
strcpy(branch_top, src_ref_prefix);
|
||||
strbuf_addstr(&branch_top, src_ref_prefix);
|
||||
|
||||
git_config_set("core.bare", "true");
|
||||
} else {
|
||||
snprintf(branch_top, sizeof(branch_top),
|
||||
"refs/remotes/%s/", option_origin);
|
||||
strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
|
||||
}
|
||||
|
||||
if (option_mirror || !option_bare) {
|
||||
/* Configure the remote */
|
||||
if (option_mirror) {
|
||||
snprintf(key, sizeof(key),
|
||||
"remote.%s.mirror", option_origin);
|
||||
git_config_set(key, "true");
|
||||
strbuf_addf(&key, "remote.%s.mirror", option_origin);
|
||||
git_config_set(key.buf, "true");
|
||||
strbuf_reset(&key);
|
||||
}
|
||||
|
||||
snprintf(key, sizeof(key), "remote.%s.url", option_origin);
|
||||
git_config_set(key, repo);
|
||||
strbuf_addf(&key, "remote.%s.url", option_origin);
|
||||
git_config_set(key.buf, repo);
|
||||
strbuf_reset(&key);
|
||||
|
||||
snprintf(key, sizeof(key), "remote.%s.fetch", option_origin);
|
||||
snprintf(value, sizeof(value),
|
||||
"+%s*:%s*", src_ref_prefix, branch_top);
|
||||
git_config_set_multivar(key, value, "^$", 0);
|
||||
strbuf_addf(&key, "remote.%s.fetch", option_origin);
|
||||
strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
|
||||
git_config_set_multivar(key.buf, value.buf, "^$", 0);
|
||||
strbuf_reset(&key);
|
||||
strbuf_reset(&value);
|
||||
}
|
||||
|
||||
refspec.force = 0;
|
||||
refspec.pattern = 1;
|
||||
refspec.src = src_ref_prefix;
|
||||
refspec.dst = branch_top;
|
||||
refspec.dst = branch_top.buf;
|
||||
|
||||
if (path && !is_bundle)
|
||||
refs = clone_local(path, git_dir);
|
||||
@@ -541,7 +546,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
head_points_at->old_sha1,
|
||||
NULL, 0, DIE_ON_ERR);
|
||||
|
||||
strbuf_addstr(&head_ref, branch_top);
|
||||
strbuf_addstr(&head_ref, branch_top.buf);
|
||||
strbuf_addstr(&head_ref, "HEAD");
|
||||
|
||||
/* Remote branch link */
|
||||
@@ -549,10 +554,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
head_points_at->peer_ref->name,
|
||||
reflog_msg.buf);
|
||||
|
||||
snprintf(key, sizeof(key), "branch.%s.remote", head);
|
||||
git_config_set(key, option_origin);
|
||||
snprintf(key, sizeof(key), "branch.%s.merge", head);
|
||||
git_config_set(key, head_points_at->name);
|
||||
strbuf_addf(&key, "branch.%s.remote", head);
|
||||
git_config_set(key.buf, option_origin);
|
||||
strbuf_reset(&key);
|
||||
strbuf_addf(&key, "branch.%s.merge", head);
|
||||
git_config_set(key.buf, head_points_at->name);
|
||||
}
|
||||
} else if (remote_head) {
|
||||
/* Source had detached HEAD pointing somewhere. */
|
||||
@@ -602,6 +608,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
strbuf_release(&reflog_msg);
|
||||
strbuf_release(&branch_top);
|
||||
strbuf_release(&key);
|
||||
strbuf_release(&value);
|
||||
junk_pid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -225,18 +225,18 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (interactive) {
|
||||
interactive_add(argc, argv, prefix);
|
||||
if (read_cache() < 0)
|
||||
if (read_cache_preload(NULL) < 0)
|
||||
die("index file corrupt");
|
||||
commit_style = COMMIT_AS_IS;
|
||||
return get_index_file();
|
||||
}
|
||||
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
|
||||
if (*argv)
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
if (read_cache_preload(pathspec) < 0)
|
||||
die("index file corrupt");
|
||||
|
||||
/*
|
||||
* Non partial, non as-is commit.
|
||||
*
|
||||
|
||||
@@ -59,8 +59,8 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
|
||||
(rev.diffopt.output_format & DIFF_FORMAT_PATCH))
|
||||
rev.combine_merges = rev.dense_combined_merges = 1;
|
||||
|
||||
if (read_cache() < 0) {
|
||||
perror("read_cache");
|
||||
if (read_cache_preload(rev.diffopt.paths) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
result = run_diff_files(&rev, options);
|
||||
|
||||
@@ -134,8 +134,8 @@ static int builtin_diff_index(struct rev_info *revs,
|
||||
revs->max_count != -1 || revs->min_age != -1 ||
|
||||
revs->max_age != -1)
|
||||
usage(builtin_diff_usage);
|
||||
if (read_cache() < 0) {
|
||||
perror("read_cache");
|
||||
if (read_cache_preload(revs->diffopt.paths) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
return run_diff_index(revs, cached);
|
||||
@@ -234,8 +234,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
|
||||
revs->combine_merges = revs->dense_combined_merges = 1;
|
||||
|
||||
setup_work_tree();
|
||||
if (read_cache() < 0) {
|
||||
perror("read_cache");
|
||||
if (read_cache_preload(revs->diffopt.paths) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
result = run_diff_files(revs, options);
|
||||
@@ -290,6 +290,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
/* Otherwise, we are doing the usual "git" diff */
|
||||
rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
|
||||
|
||||
/* Default to let external be used */
|
||||
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
|
||||
|
||||
if (nongit)
|
||||
die("Not a git repository");
|
||||
argc = setup_revisions(argc, argv, &rev, NULL);
|
||||
@@ -298,7 +301,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
if (diff_setup_done(&rev.diffopt) < 0)
|
||||
die("diff_setup_done failed");
|
||||
}
|
||||
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
|
||||
|
||||
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
||||
DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
|
||||
|
||||
|
||||
@@ -354,7 +354,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
|
||||
case OBJ_TAG:
|
||||
tag = (struct tag *)e->item;
|
||||
while (tag && tag->object.type == OBJ_TAG) {
|
||||
string_list_insert(full_name, extra_refs)->util = tag;
|
||||
string_list_append(full_name, extra_refs)->util = tag;
|
||||
tag = (struct tag *)tag->tagged;
|
||||
}
|
||||
if (!tag)
|
||||
@@ -374,7 +374,7 @@ static void get_tags_and_duplicates(struct object_array *pending,
|
||||
}
|
||||
if (commit->util)
|
||||
/* more than one name for the same object */
|
||||
string_list_insert(full_name, extra_refs)->util = commit;
|
||||
string_list_append(full_name, extra_refs)->util = commit;
|
||||
else
|
||||
commit->util = full_name;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ enum {
|
||||
TAGS_SET = 2
|
||||
};
|
||||
|
||||
static int append, force, keep, update_head_ok, verbose, quiet;
|
||||
static int append, force, keep, update_head_ok, verbosity;
|
||||
static int tags = TAGS_DEFAULT;
|
||||
static const char *depth;
|
||||
static const char *upload_pack;
|
||||
@@ -30,8 +30,7 @@ static struct strbuf default_rla = STRBUF_INIT;
|
||||
static struct transport *transport;
|
||||
|
||||
static struct option builtin_fetch_options[] = {
|
||||
OPT__QUIET(&quiet),
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT__VERBOSITY(&verbosity),
|
||||
OPT_BOOLEAN('a', "append", &append,
|
||||
"append to .git/FETCH_HEAD instead of overwriting"),
|
||||
OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
|
||||
@@ -192,7 +191,6 @@ static int s_update_ref(const char *action,
|
||||
|
||||
static int update_local_ref(struct ref *ref,
|
||||
const char *remote,
|
||||
int verbose,
|
||||
char *display)
|
||||
{
|
||||
struct commit *current = NULL, *updated;
|
||||
@@ -210,7 +208,7 @@ static int update_local_ref(struct ref *ref,
|
||||
die("object %s not found", sha1_to_hex(ref->new_sha1));
|
||||
|
||||
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
|
||||
if (verbose)
|
||||
if (verbosity > 0)
|
||||
sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
|
||||
"[up to date]", REFCOL_WIDTH, remote,
|
||||
pretty_ref);
|
||||
@@ -366,18 +364,19 @@ static int store_updated_refs(const char *url, const char *remote_name,
|
||||
note);
|
||||
|
||||
if (ref)
|
||||
rc |= update_local_ref(ref, what, verbose, note);
|
||||
rc |= update_local_ref(ref, what, note);
|
||||
else
|
||||
sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
|
||||
SUMMARY_WIDTH, *kind ? kind : "branch",
|
||||
REFCOL_WIDTH, *what ? what : "HEAD");
|
||||
if (*note) {
|
||||
if (!shown_url) {
|
||||
if (verbosity >= 0 && !shown_url) {
|
||||
fprintf(stderr, "From %.*s\n",
|
||||
url_len, url);
|
||||
shown_url = 1;
|
||||
}
|
||||
fprintf(stderr, " %s\n", note);
|
||||
if (verbosity >= 0)
|
||||
fprintf(stderr, " %s\n", note);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
@@ -637,9 +636,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
remote = remote_get(argv[0]);
|
||||
|
||||
transport = transport_get(remote, remote->url[0]);
|
||||
if (verbose >= 2)
|
||||
if (verbosity >= 2)
|
||||
transport->verbose = 1;
|
||||
if (quiet)
|
||||
if (verbosity < 0)
|
||||
transport->verbose = -1;
|
||||
if (upload_pack)
|
||||
set_option(TRANS_OPT_UPLOADPACK, upload_pack);
|
||||
|
||||
@@ -50,6 +50,7 @@ static unsigned char head[20], stash[20];
|
||||
static struct strategy **use_strategies;
|
||||
static size_t use_strategies_nr, use_strategies_alloc;
|
||||
static const char *branch;
|
||||
static int verbosity;
|
||||
|
||||
static struct strategy all_strategy[] = {
|
||||
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
|
||||
@@ -171,6 +172,7 @@ static struct option builtin_merge_options[] = {
|
||||
OPT_CALLBACK('m', "message", &merge_msg, "message",
|
||||
"message to be used for the merge commit (if any)",
|
||||
option_parse_message),
|
||||
OPT__VERBOSITY(&verbosity),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -250,7 +252,8 @@ static void restore_state(void)
|
||||
/* This is called when no merge was necessary. */
|
||||
static void finish_up_to_date(const char *msg)
|
||||
{
|
||||
printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
|
||||
if (verbosity >= 0)
|
||||
printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
|
||||
drop_save();
|
||||
}
|
||||
|
||||
@@ -331,14 +334,15 @@ static void finish(const unsigned char *new_head, const char *msg)
|
||||
if (!msg)
|
||||
strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
|
||||
else {
|
||||
printf("%s\n", msg);
|
||||
if (verbosity >= 0)
|
||||
printf("%s\n", msg);
|
||||
strbuf_addf(&reflog_message, "%s: %s",
|
||||
getenv("GIT_REFLOG_ACTION"), msg);
|
||||
}
|
||||
if (squash) {
|
||||
squash_message();
|
||||
} else {
|
||||
if (!merge_msg.len)
|
||||
if (verbosity >= 0 && !merge_msg.len)
|
||||
printf("No merge message -- not updating HEAD\n");
|
||||
else {
|
||||
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
|
||||
@@ -872,6 +876,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
|
||||
argc = parse_options(argc, argv, builtin_merge_options,
|
||||
builtin_merge_usage, 0);
|
||||
if (verbosity < 0)
|
||||
show_diffstat = 0;
|
||||
|
||||
if (squash) {
|
||||
if (!allow_fast_forward)
|
||||
@@ -1013,10 +1019,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
|
||||
strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
|
||||
|
||||
printf("Updating %s..%s\n",
|
||||
hex,
|
||||
find_unique_abbrev(remoteheads->item->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
if (verbosity >= 0)
|
||||
printf("Updating %s..%s\n",
|
||||
hex,
|
||||
find_unique_abbrev(remoteheads->item->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
strbuf_addstr(&msg, "Fast forward");
|
||||
if (have_message)
|
||||
strbuf_addstr(&msg,
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#include "refs.h"
|
||||
|
||||
static const char * const builtin_remote_usage[] = {
|
||||
"git remote",
|
||||
"git remote add <name> <url>",
|
||||
"git remote [-v | --verbose]",
|
||||
"git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
|
||||
"git remote rename <old> <new>",
|
||||
"git remote rm <name>",
|
||||
"git remote show <name>",
|
||||
"git remote prune <name>",
|
||||
"git remote update [group]",
|
||||
"git remote show [-n] <name>",
|
||||
"git remote prune [-n | --dry-run] <name>",
|
||||
"git remote [-v | --verbose] update [group]",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -42,7 +42,11 @@ static int opt_parse_track(const struct option *opt, const char *arg, int not)
|
||||
|
||||
static int fetch_remote(const char *name)
|
||||
{
|
||||
const char *argv[] = { "fetch", name, NULL };
|
||||
const char *argv[] = { "fetch", name, NULL, NULL };
|
||||
if (verbose) {
|
||||
argv[1] = "-v";
|
||||
argv[2] = name;
|
||||
}
|
||||
printf("Updating %s\n", name);
|
||||
if (run_command_v_opt(argv, RUN_GIT_CMD))
|
||||
return error("Could not fetch %s", name);
|
||||
@@ -770,7 +774,7 @@ static int get_one_remote_for_update(struct remote *remote, void *priv)
|
||||
{
|
||||
struct string_list *list = priv;
|
||||
if (!remote->skip_default_update)
|
||||
string_list_append(xstrdup(remote->name), list);
|
||||
string_list_append(remote->name, list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
3
cache.h
3
cache.h
@@ -262,6 +262,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
|
||||
|
||||
#define read_cache() read_index(&the_index)
|
||||
#define read_cache_from(path) read_index_from(&the_index, (path))
|
||||
#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
|
||||
#define is_cache_unborn() is_index_unborn(&the_index)
|
||||
#define read_cache_unmerged() read_index_unmerged(&the_index)
|
||||
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
|
||||
@@ -368,6 +369,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
|
||||
|
||||
/* Initialize and use the cache information */
|
||||
extern int read_index(struct index_state *);
|
||||
extern int read_index_preload(struct index_state *, const char **pathspec);
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int is_index_unborn(struct index_state *);
|
||||
extern int read_index_unmerged(struct index_state *);
|
||||
@@ -458,6 +460,7 @@ extern size_t packed_git_limit;
|
||||
extern size_t delta_base_cache_limit;
|
||||
extern int auto_crlf;
|
||||
extern int fsync_object_files;
|
||||
extern int core_preload_index;
|
||||
|
||||
enum safe_crlf {
|
||||
SAFE_CRLF_FALSE = 0,
|
||||
|
||||
5
config.c
5
config.c
@@ -490,6 +490,11 @@ static int git_default_core_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.preloadindex")) {
|
||||
core_preload_index = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -188,11 +188,22 @@ __git_tags ()
|
||||
|
||||
__git_refs ()
|
||||
{
|
||||
local cmd i is_hash=y dir="$(__gitdir "$1")"
|
||||
local i is_hash=y dir="$(__gitdir "$1")"
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}" format refs
|
||||
if [ -d "$dir" ]; then
|
||||
if [ -e "$dir/HEAD" ]; then echo HEAD; fi
|
||||
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
|
||||
refs/tags refs/heads refs/remotes
|
||||
case "$cur" in
|
||||
refs|refs/*)
|
||||
format="refname"
|
||||
refs="${cur%/*}"
|
||||
;;
|
||||
*)
|
||||
if [ -e "$dir/HEAD" ]; then echo HEAD; fi
|
||||
format="refname:short"
|
||||
refs="refs/tags refs/heads refs/remotes"
|
||||
;;
|
||||
esac
|
||||
git --git-dir="$dir" for-each-ref --format="%($format)" \
|
||||
$refs
|
||||
return
|
||||
fi
|
||||
for i in $(git ls-remote "$dir" 2>/dev/null); do
|
||||
@@ -636,21 +647,12 @@ _git_branch ()
|
||||
|
||||
_git_bundle ()
|
||||
{
|
||||
local mycword="$COMP_CWORD"
|
||||
case "${COMP_WORDS[0]}" in
|
||||
git)
|
||||
local cmd="${COMP_WORDS[2]}"
|
||||
mycword="$((mycword-1))"
|
||||
;;
|
||||
git-bundle*)
|
||||
local cmd="${COMP_WORDS[1]}"
|
||||
;;
|
||||
esac
|
||||
case "$mycword" in
|
||||
1)
|
||||
local cmd="${COMP_WORDS[2]}"
|
||||
case "$COMP_CWORD" in
|
||||
2)
|
||||
__gitcomp "create list-heads verify unbundle"
|
||||
;;
|
||||
2)
|
||||
3)
|
||||
# looking for a file
|
||||
;;
|
||||
*)
|
||||
@@ -798,12 +800,7 @@ _git_fetch ()
|
||||
__gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
|
||||
;;
|
||||
*)
|
||||
local remote
|
||||
case "${COMP_WORDS[0]}" in
|
||||
git-fetch) remote="${COMP_WORDS[1]}" ;;
|
||||
git) remote="${COMP_WORDS[2]}" ;;
|
||||
esac
|
||||
__gitcomp "$(__git_refs2 "$remote")"
|
||||
__gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
@@ -1047,12 +1044,7 @@ _git_pull ()
|
||||
if [ "$COMP_CWORD" = 2 ]; then
|
||||
__gitcomp "$(__git_remotes)"
|
||||
else
|
||||
local remote
|
||||
case "${COMP_WORDS[0]}" in
|
||||
git-pull) remote="${COMP_WORDS[1]}" ;;
|
||||
git) remote="${COMP_WORDS[2]}" ;;
|
||||
esac
|
||||
__gitcomp "$(__git_refs "$remote")"
|
||||
__gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1065,19 +1057,13 @@ _git_push ()
|
||||
else
|
||||
case "$cur" in
|
||||
*:*)
|
||||
local remote
|
||||
case "${COMP_WORDS[0]}" in
|
||||
git-push) remote="${COMP_WORDS[1]}" ;;
|
||||
git) remote="${COMP_WORDS[2]}" ;;
|
||||
esac
|
||||
|
||||
local pfx=""
|
||||
case "$COMP_WORDBREAKS" in
|
||||
*:*) : great ;;
|
||||
*) pfx="${cur%%:*}:" ;;
|
||||
esac
|
||||
|
||||
__gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}"
|
||||
__gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
|
||||
;;
|
||||
+*)
|
||||
__gitcomp "$(__git_refs)" + "${cur#+}"
|
||||
@@ -1357,7 +1343,7 @@ _git_revert ()
|
||||
return
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=()
|
||||
__gitcomp "$(__git_refs)"
|
||||
}
|
||||
|
||||
_git_rm ()
|
||||
@@ -1579,7 +1565,7 @@ _git_tag ()
|
||||
-m|-F)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
-*|tag|git-tag)
|
||||
-*|tag)
|
||||
if [ $f = 1 ]; then
|
||||
__gitcomp "$(__git_tags)"
|
||||
else
|
||||
|
||||
@@ -173,7 +173,7 @@ if there is already one that displays the same directory."
|
||||
(defconst git-log-msg-separator "--- log message follows this line ---")
|
||||
|
||||
(defvar git-log-edit-font-lock-keywords
|
||||
`(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$"
|
||||
`(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$"
|
||||
(1 font-lock-keyword-face)
|
||||
(2 font-lock-function-name-face))
|
||||
(,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
|
||||
@@ -183,11 +183,9 @@ if there is already one that displays the same directory."
|
||||
"Build a list of NAME=VALUE strings from a list of environment strings."
|
||||
(mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env))
|
||||
|
||||
(defun git-call-process-env (buffer env &rest args)
|
||||
(defun git-call-process (buffer &rest args)
|
||||
"Wrapper for call-process that sets environment strings."
|
||||
(let ((process-environment (append (git-get-env-strings env)
|
||||
process-environment)))
|
||||
(apply #'call-process "git" nil buffer nil args)))
|
||||
(apply #'call-process "git" nil buffer nil args))
|
||||
|
||||
(defun git-call-process-display-error (&rest args)
|
||||
"Wrapper for call-process that displays error messages."
|
||||
@@ -197,17 +195,26 @@ if there is already one that displays the same directory."
|
||||
(let ((default-directory dir)
|
||||
(buffer-read-only nil))
|
||||
(erase-buffer)
|
||||
(eq 0 (apply 'call-process "git" nil (list buffer t) nil args))))))
|
||||
(eq 0 (apply #'git-call-process (list buffer t) args))))))
|
||||
(unless ok (display-message-or-buffer buffer))
|
||||
ok))
|
||||
|
||||
(defun git-call-process-env-string (env &rest args)
|
||||
"Wrapper for call-process that sets environment strings,
|
||||
and returns the process output as a string, or nil if the git failed."
|
||||
(defun git-call-process-string (&rest args)
|
||||
"Wrapper for call-process that returns the process output as a string,
|
||||
or nil if the git command failed."
|
||||
(with-temp-buffer
|
||||
(and (eq 0 (apply #' git-call-process-env t env args))
|
||||
(and (eq 0 (apply #'git-call-process t args))
|
||||
(buffer-string))))
|
||||
|
||||
(defun git-call-process-string-display-error (&rest args)
|
||||
"Wrapper for call-process that displays error message and returns
|
||||
the process output as a string, or nil if the git command failed."
|
||||
(with-temp-buffer
|
||||
(if (eq 0 (apply #'git-call-process (list t t) args))
|
||||
(buffer-string)
|
||||
(display-message-or-buffer (current-buffer))
|
||||
nil)))
|
||||
|
||||
(defun git-run-process-region (buffer start end program args)
|
||||
"Run a git process with a buffer region as input."
|
||||
(let ((output-buffer (current-buffer))
|
||||
@@ -226,7 +233,7 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(let ((default-directory dir)
|
||||
(buffer-read-only nil))
|
||||
(erase-buffer)
|
||||
(apply #'git-call-process-env buffer nil args)))
|
||||
(apply #'git-call-process buffer args)))
|
||||
(message "Running git %s...done" (car args))
|
||||
buffer))
|
||||
|
||||
@@ -327,7 +334,7 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(let ((cdup (with-output-to-string
|
||||
(with-current-buffer standard-output
|
||||
(cd dir)
|
||||
(unless (eq 0 (call-process "git" nil t nil "rev-parse" "--show-cdup"))
|
||||
(unless (eq 0 (git-call-process t "rev-parse" "--show-cdup"))
|
||||
(error "cannot find top-level git tree for %s." dir))))))
|
||||
(expand-file-name (concat (file-name-as-directory dir)
|
||||
(car (split-string cdup "\n"))))))
|
||||
@@ -348,8 +355,8 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(sort-lines nil (point-min) (point-max))
|
||||
(save-buffer))
|
||||
(when created
|
||||
(git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
|
||||
(git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
|
||||
(git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name)))
|
||||
(git-update-status-files (list (file-relative-name ignore-name)))))
|
||||
|
||||
; propertize definition for XEmacs, stolen from erc-compat
|
||||
(eval-when-compile
|
||||
@@ -367,38 +374,41 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(defun git-rev-parse (rev)
|
||||
"Parse a revision name and return its SHA1."
|
||||
(git-get-string-sha1
|
||||
(git-call-process-env-string nil "rev-parse" rev)))
|
||||
(git-call-process-string "rev-parse" rev)))
|
||||
|
||||
(defun git-config (key)
|
||||
"Retrieve the value associated to KEY in the git repository config file."
|
||||
(let ((str (git-call-process-env-string nil "config" key)))
|
||||
(let ((str (git-call-process-string "config" key)))
|
||||
(and str (car (split-string str "\n")))))
|
||||
|
||||
(defun git-symbolic-ref (ref)
|
||||
"Wrapper for the git-symbolic-ref command."
|
||||
(let ((str (git-call-process-env-string nil "symbolic-ref" ref)))
|
||||
(let ((str (git-call-process-string "symbolic-ref" ref)))
|
||||
(and str (car (split-string str "\n")))))
|
||||
|
||||
(defun git-update-ref (ref newval &optional oldval reason)
|
||||
"Update a reference by calling git-update-ref."
|
||||
(let ((args (and oldval (list oldval))))
|
||||
(push newval args)
|
||||
(when newval (push newval args))
|
||||
(push ref args)
|
||||
(when reason
|
||||
(push reason args)
|
||||
(push "-m" args))
|
||||
(unless newval (push "-d" args))
|
||||
(apply 'git-call-process-display-error "update-ref" args)))
|
||||
|
||||
(defun git-read-tree (tree &optional index-file)
|
||||
"Read a tree into the index file."
|
||||
(apply #'git-call-process-env nil
|
||||
(if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil)
|
||||
"read-tree" (if tree (list tree))))
|
||||
(let ((process-environment
|
||||
(append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
|
||||
(apply 'git-call-process-display-error "read-tree" (if tree (list tree)))))
|
||||
|
||||
(defun git-write-tree (&optional index-file)
|
||||
"Call git-write-tree and return the resulting tree SHA1 as a string."
|
||||
(git-get-string-sha1
|
||||
(git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree")))
|
||||
(let ((process-environment
|
||||
(append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
|
||||
(git-get-string-sha1
|
||||
(git-call-process-string-display-error "write-tree"))))
|
||||
|
||||
(defun git-commit-tree (buffer tree head)
|
||||
"Call git-commit-tree with buffer as input and return the resulting commit SHA1."
|
||||
@@ -424,11 +434,11 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(when (re-search-forward "^Date: +\\(.*\\)$" nil t)
|
||||
(setq author-date (match-string 1)))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t)
|
||||
(unless (string-equal head (match-string 1))
|
||||
(setq subject "commit (merge): ")
|
||||
(when (re-search-forward "^Merge: +\\(.*\\)" nil t)
|
||||
(setq subject "commit (merge): ")
|
||||
(dolist (parent (split-string (match-string 1) " +" t))
|
||||
(push "-p" args)
|
||||
(push (match-string 1) args))))
|
||||
(push parent args))))
|
||||
(setq log-start (point-min)))
|
||||
(setq log-end (point-max))
|
||||
(goto-char log-start)
|
||||
@@ -452,7 +462,7 @@ and returns the process output as a string, or nil if the git failed."
|
||||
|
||||
(defun git-empty-db-p ()
|
||||
"Check if the git db is empty (no commit done yet)."
|
||||
(not (eq 0 (call-process "git" nil nil nil "rev-parse" "--verify" "HEAD"))))
|
||||
(not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD"))))
|
||||
|
||||
(defun git-get-merge-heads ()
|
||||
"Retrieve the merge heads from the MERGE_HEAD file if present."
|
||||
@@ -468,7 +478,7 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(defun git-get-commit-description (commit)
|
||||
"Get a one-line description of COMMIT."
|
||||
(let ((coding-system-for-read (git-get-logoutput-coding-system)))
|
||||
(let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit)))
|
||||
(let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit)))
|
||||
(if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr))
|
||||
(concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr))
|
||||
descr))))
|
||||
@@ -487,14 +497,11 @@ and returns the process output as a string, or nil if the git failed."
|
||||
old-perm new-perm ;; permission flags
|
||||
rename-state ;; rename or copy state
|
||||
orig-name ;; original name for renames or copies
|
||||
needs-update ;; whether file needs to be updated
|
||||
needs-refresh) ;; whether file needs to be refreshed
|
||||
|
||||
(defvar git-status nil)
|
||||
|
||||
(defun git-clear-status (status)
|
||||
"Remove everything from the status list."
|
||||
(ewoc-filter status (lambda (info) nil)))
|
||||
|
||||
(defun git-set-fileinfo-state (info state)
|
||||
"Set the state of a file info."
|
||||
(unless (eq (git-fileinfo->state info) state)
|
||||
@@ -502,6 +509,7 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(git-fileinfo->new-perm info) (git-fileinfo->old-perm info)
|
||||
(git-fileinfo->rename-state info) nil
|
||||
(git-fileinfo->orig-name info) nil
|
||||
(git-fileinfo->needs-update info) nil
|
||||
(git-fileinfo->needs-refresh info) t)))
|
||||
|
||||
(defun git-status-filenames-map (status func files &rest args)
|
||||
@@ -511,10 +519,11 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(let ((file (pop files))
|
||||
(node (ewoc-nth status 0)))
|
||||
(while (and file node)
|
||||
(let ((info (ewoc-data node)))
|
||||
(if (string-lessp (git-fileinfo->name info) file)
|
||||
(let* ((info (ewoc-data node))
|
||||
(name (git-fileinfo->name info)))
|
||||
(if (string-lessp name file)
|
||||
(setq node (ewoc-next status node))
|
||||
(if (string-equal (git-fileinfo->name info) file)
|
||||
(if (string-equal name file)
|
||||
(apply func info args))
|
||||
(setq file (pop files))))))))
|
||||
|
||||
@@ -612,39 +621,52 @@ and returns the process output as a string, or nil if the git failed."
|
||||
(git-file-type-as-string old-perm new-perm)
|
||||
(git-rename-as-string info)))))
|
||||
|
||||
(defun git-insert-info-list (status infolist)
|
||||
"Insert a list of file infos in the status buffer, replacing existing ones if any."
|
||||
(setq infolist (sort infolist
|
||||
(lambda (info1 info2)
|
||||
(string-lessp (git-fileinfo->name info1)
|
||||
(git-fileinfo->name info2)))))
|
||||
(let ((info (pop infolist))
|
||||
(node (ewoc-nth status 0)))
|
||||
(defun git-update-node-fileinfo (node info)
|
||||
"Update the fileinfo of the specified node. The names are assumed to match already."
|
||||
(let ((data (ewoc-data node)))
|
||||
(setf
|
||||
;; preserve the marked flag
|
||||
(git-fileinfo->marked info) (git-fileinfo->marked data)
|
||||
(git-fileinfo->needs-update data) nil)
|
||||
(when (not (equal info data))
|
||||
(setf (git-fileinfo->needs-refresh info) t
|
||||
(ewoc-data node) info))))
|
||||
|
||||
(defun git-insert-info-list (status infolist files)
|
||||
"Insert a sorted list of file infos in the status buffer, replacing existing ones if any."
|
||||
(let* ((info (pop infolist))
|
||||
(node (ewoc-nth status 0))
|
||||
(name (and info (git-fileinfo->name info)))
|
||||
remaining)
|
||||
(while info
|
||||
(cond ((not node)
|
||||
(setq node (ewoc-enter-last status info))
|
||||
(setq info (pop infolist)))
|
||||
((string-lessp (git-fileinfo->name (ewoc-data node))
|
||||
(git-fileinfo->name info))
|
||||
(setq node (ewoc-next status node)))
|
||||
((string-equal (git-fileinfo->name (ewoc-data node))
|
||||
(git-fileinfo->name info))
|
||||
;; preserve the marked flag
|
||||
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
|
||||
(setf (git-fileinfo->needs-refresh info) t)
|
||||
(setf (ewoc-data node) info)
|
||||
(setq info (pop infolist)))
|
||||
(t
|
||||
(setq node (ewoc-enter-before status node info))
|
||||
(setq info (pop infolist)))))))
|
||||
(let ((nodename (and node (git-fileinfo->name (ewoc-data node)))))
|
||||
(while (and files (string-lessp (car files) name))
|
||||
(push (pop files) remaining))
|
||||
(when (and files (string-equal (car files) name))
|
||||
(setq files (cdr files)))
|
||||
(cond ((not nodename)
|
||||
(setq node (ewoc-enter-last status info))
|
||||
(setq info (pop infolist))
|
||||
(setq name (and info (git-fileinfo->name info))))
|
||||
((string-lessp nodename name)
|
||||
(setq node (ewoc-next status node)))
|
||||
((string-equal nodename name)
|
||||
;; preserve the marked flag
|
||||
(git-update-node-fileinfo node info)
|
||||
(setq info (pop infolist))
|
||||
(setq name (and info (git-fileinfo->name info))))
|
||||
(t
|
||||
(setq node (ewoc-enter-before status node info))
|
||||
(setq info (pop infolist))
|
||||
(setq name (and info (git-fileinfo->name info)))))))
|
||||
(nconc (nreverse remaining) files)))
|
||||
|
||||
(defun git-run-diff-index (status files)
|
||||
"Run git-diff-index on FILES and parse the results into STATUS.
|
||||
Return the list of files that haven't been handled."
|
||||
(let ((remaining (copy-sequence files))
|
||||
infolist)
|
||||
(let (infolist)
|
||||
(with-temp-buffer
|
||||
(apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files)
|
||||
(apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward
|
||||
":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
|
||||
@@ -659,11 +681,12 @@ Return the list of files that haven't been handled."
|
||||
(push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
|
||||
(push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
|
||||
(push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
|
||||
(push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
|
||||
(setq remaining (delete name remaining))
|
||||
(when new-name (setq remaining (delete new-name remaining))))))
|
||||
(git-insert-info-list status infolist)
|
||||
remaining))
|
||||
(push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)))))
|
||||
(setq infolist (sort (nreverse infolist)
|
||||
(lambda (info1 info2)
|
||||
(string-lessp (git-fileinfo->name info1)
|
||||
(git-fileinfo->name info2)))))
|
||||
(git-insert-info-list status infolist files)))
|
||||
|
||||
(defun git-find-status-file (status file)
|
||||
"Find a given file in the status ewoc and return its node."
|
||||
@@ -677,38 +700,35 @@ Return the list of files that haven't been handled."
|
||||
Return the list of files that haven't been handled."
|
||||
(let (infolist)
|
||||
(with-temp-buffer
|
||||
(apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
|
||||
(apply #'git-call-process t "ls-files" "-z" (append options (list "--") files))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1)
|
||||
(let ((name (match-string 1)))
|
||||
(push (git-create-fileinfo default-state name 0
|
||||
(if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0))
|
||||
infolist)
|
||||
(setq files (delete name files)))))
|
||||
(git-insert-info-list status infolist)
|
||||
files))
|
||||
infolist))))
|
||||
(setq infolist (nreverse infolist)) ;; assume it is sorted already
|
||||
(git-insert-info-list status infolist files)))
|
||||
|
||||
(defun git-run-ls-files-cached (status files default-state)
|
||||
"Run git-ls-files -c on FILES and parse the results into STATUS.
|
||||
Return the list of files that haven't been handled."
|
||||
(let ((remaining (copy-sequence files))
|
||||
infolist)
|
||||
(let (infolist)
|
||||
(with-temp-buffer
|
||||
(apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files)
|
||||
(apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
|
||||
(let* ((new-perm (string-to-number (match-string 1) 8))
|
||||
(old-perm (if (eq default-state 'added) 0 new-perm))
|
||||
(name (match-string 2)))
|
||||
(push (git-create-fileinfo default-state name old-perm new-perm) infolist)
|
||||
(setq remaining (delete name remaining)))))
|
||||
(git-insert-info-list status infolist)
|
||||
remaining))
|
||||
(push (git-create-fileinfo default-state name old-perm new-perm) infolist))))
|
||||
(setq infolist (nreverse infolist)) ;; assume it is sorted already
|
||||
(git-insert-info-list status infolist files)))
|
||||
|
||||
(defun git-run-ls-unmerged (status files)
|
||||
"Run git-ls-files -u on FILES and parse the results into STATUS."
|
||||
(with-temp-buffer
|
||||
(apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files)
|
||||
(apply #'git-call-process t "ls-files" "-z" "-u" "--" files)
|
||||
(goto-char (point-min))
|
||||
(let (unmerged-files)
|
||||
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
|
||||
@@ -732,11 +752,17 @@ Return the list of files that haven't been handled."
|
||||
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
|
||||
(append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
|
||||
|
||||
(defun git-update-status-files (files &optional default-state)
|
||||
(defun git-update-status-files (&optional files mark-files)
|
||||
"Update the status of FILES from the index."
|
||||
(unless git-status (error "Not in git-status buffer."))
|
||||
(when (or git-show-uptodate files)
|
||||
(git-run-ls-files-cached git-status files 'uptodate))
|
||||
;; set the needs-update flag on existing files
|
||||
(if (setq files (sort files #'string-lessp))
|
||||
(git-status-filenames-map
|
||||
git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files)
|
||||
(ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status)
|
||||
(git-call-process nil "update-index" "--refresh")
|
||||
(when git-show-uptodate
|
||||
(git-run-ls-files-cached git-status nil 'uptodate)))
|
||||
(let* ((remaining-files
|
||||
(if (git-empty-db-p) ; we need some special handling for an empty db
|
||||
(git-run-ls-files-cached git-status files 'added)
|
||||
@@ -746,13 +772,17 @@ Return the list of files that haven't been handled."
|
||||
(setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o")))
|
||||
(when (or remaining-files (and git-show-ignored (not files)))
|
||||
(setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i")))
|
||||
(git-set-filenames-state git-status remaining-files default-state)
|
||||
(unless files
|
||||
(setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update))))
|
||||
(when remaining-files
|
||||
(setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate)))
|
||||
(git-set-filenames-state git-status remaining-files nil)
|
||||
(when mark-files (git-mark-files git-status files))
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf git-status)))
|
||||
|
||||
(defun git-mark-files (status files)
|
||||
"Mark all the specified FILES, and unmark the others."
|
||||
(setq files (sort files #'string-lessp))
|
||||
(let ((file (and files (pop files)))
|
||||
(node (ewoc-nth status 0)))
|
||||
(while node
|
||||
@@ -824,19 +854,18 @@ Return the list of files that haven't been handled."
|
||||
|
||||
(defun git-update-index (index-file files)
|
||||
"Run git-update-index on a list of files."
|
||||
(let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file))))
|
||||
(let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file)))
|
||||
process-environment))
|
||||
added deleted modified)
|
||||
(dolist (info files)
|
||||
(case (git-fileinfo->state info)
|
||||
('added (push info added))
|
||||
('deleted (push info deleted))
|
||||
('modified (push info modified))))
|
||||
(when added
|
||||
(apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added)))
|
||||
(when deleted
|
||||
(apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
|
||||
(when modified
|
||||
(apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified)))))
|
||||
(and
|
||||
(or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added)))
|
||||
(or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted)))
|
||||
(or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified))))))
|
||||
|
||||
(defun git-run-pre-commit-hook ()
|
||||
"Run the pre-commit hook if any."
|
||||
@@ -862,33 +891,30 @@ Return the list of files that haven't been handled."
|
||||
(message "You cannot commit unmerged files, resolve them first.")
|
||||
(unwind-protect
|
||||
(let ((files (git-marked-files-state 'added 'deleted 'modified))
|
||||
head head-tree)
|
||||
head tree head-tree)
|
||||
(unless (git-empty-db-p)
|
||||
(setq head (git-rev-parse "HEAD")
|
||||
head-tree (git-rev-parse "HEAD^{tree}")))
|
||||
(if files
|
||||
(progn
|
||||
(message "Running git commit...")
|
||||
(git-read-tree head-tree index-file)
|
||||
(git-update-index nil files) ;update both the default index
|
||||
(git-update-index index-file files) ;and the temporary one
|
||||
(let ((tree (git-write-tree index-file)))
|
||||
(if (or (not (string-equal tree head-tree))
|
||||
(yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
|
||||
(let ((commit (git-commit-tree buffer tree head)))
|
||||
(when commit
|
||||
(condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
|
||||
(condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
|
||||
(with-current-buffer buffer (erase-buffer))
|
||||
(git-update-status-files (git-get-filenames files) 'uptodate)
|
||||
(git-call-process-env nil nil "rerere")
|
||||
(git-call-process-env nil nil "gc" "--auto")
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf git-status)
|
||||
(message "Committed %s." commit)
|
||||
(git-run-hook "post-commit" nil)))
|
||||
(message "Commit aborted."))))
|
||||
(message "No files to commit.")))
|
||||
(message "Running git commit...")
|
||||
(when
|
||||
(and
|
||||
(git-read-tree head-tree index-file)
|
||||
(git-update-index nil files) ;update both the default index
|
||||
(git-update-index index-file files) ;and the temporary one
|
||||
(setq tree (git-write-tree index-file)))
|
||||
(if (or (not (string-equal tree head-tree))
|
||||
(yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
|
||||
(let ((commit (git-commit-tree buffer tree head)))
|
||||
(when commit
|
||||
(condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
|
||||
(condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
|
||||
(with-current-buffer buffer (erase-buffer))
|
||||
(git-update-status-files (git-get-filenames files))
|
||||
(git-call-process nil "rerere")
|
||||
(git-call-process nil "gc" "--auto")
|
||||
(message "Committed %s." commit)
|
||||
(git-run-hook "post-commit" nil)))
|
||||
(message "Commit aborted."))))
|
||||
(delete-file index-file))))))
|
||||
|
||||
|
||||
@@ -990,6 +1016,11 @@ Return the list of files that haven't been handled."
|
||||
(setq node (ewoc-prev git-status node)))
|
||||
(ewoc-goto-node git-status last)))
|
||||
|
||||
(defun git-insert-file (file)
|
||||
"Insert file(s) into the git-status buffer."
|
||||
(interactive "fInsert file: ")
|
||||
(git-update-status-files (list (file-relative-name file))))
|
||||
|
||||
(defun git-add-file ()
|
||||
"Add marked file(s) to the index cache."
|
||||
(interactive)
|
||||
@@ -998,7 +1029,7 @@ Return the list of files that haven't been handled."
|
||||
(unless files
|
||||
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
|
||||
(when (apply 'git-call-process-display-error "update-index" "--add" "--" files)
|
||||
(git-update-status-files files 'uptodate)
|
||||
(git-update-status-files files)
|
||||
(git-success-message "Added" files))))
|
||||
|
||||
(defun git-ignore-file ()
|
||||
@@ -1008,7 +1039,7 @@ Return the list of files that haven't been handled."
|
||||
(unless files
|
||||
(push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
|
||||
(dolist (f files) (git-append-to-ignore f))
|
||||
(git-update-status-files files 'ignored)
|
||||
(git-update-status-files files)
|
||||
(git-success-message "Ignored" files)))
|
||||
|
||||
(defun git-remove-file ()
|
||||
@@ -1026,7 +1057,7 @@ Return the list of files that haven't been handled."
|
||||
(delete-directory name)
|
||||
(delete-file name))))
|
||||
(when (apply 'git-call-process-display-error "update-index" "--remove" "--" files)
|
||||
(git-update-status-files files nil)
|
||||
(git-update-status-files files)
|
||||
(git-success-message "Removed" files)))
|
||||
(message "Aborting"))))
|
||||
|
||||
@@ -1054,7 +1085,7 @@ Return the list of files that haven't been handled."
|
||||
(apply 'git-call-process-display-error "update-index" "--force-remove" "--" added))
|
||||
(or (not modified)
|
||||
(apply 'git-call-process-display-error "checkout" "HEAD" modified)))))
|
||||
(git-update-status-files (append added modified) 'uptodate)
|
||||
(git-update-status-files (append added modified))
|
||||
(when ok
|
||||
(dolist (file modified)
|
||||
(let ((buffer (get-file-buffer file)))
|
||||
@@ -1067,7 +1098,7 @@ Return the list of files that haven't been handled."
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
|
||||
(when files
|
||||
(when (apply 'git-call-process-display-error "update-index" "--" files)
|
||||
(git-update-status-files files 'uptodate)
|
||||
(git-update-status-files files)
|
||||
(git-success-message "Resolved" files)))))
|
||||
|
||||
(defun git-remove-handled ()
|
||||
@@ -1225,11 +1256,10 @@ Return the list of files that haven't been handled."
|
||||
(goto-char (point-max))
|
||||
(insert sign-off "\n"))))
|
||||
|
||||
(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg)
|
||||
(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg)
|
||||
"Setup the log buffer for a commit."
|
||||
(unless git-status (error "Not in git-status buffer."))
|
||||
(let ((merge-heads (git-get-merge-heads))
|
||||
(dir default-directory)
|
||||
(let ((dir default-directory)
|
||||
(committer-name (git-get-committer-name))
|
||||
(committer-email (git-get-committer-email))
|
||||
(sign-off git-append-signed-off-by))
|
||||
@@ -1243,9 +1273,8 @@ Return the list of files that haven't been handled."
|
||||
(or author-email committer-email)
|
||||
(if date (format "Date: %s\n" date) "")
|
||||
(if merge-heads
|
||||
(format "Parent: %s\n%s\n"
|
||||
(git-rev-parse "HEAD")
|
||||
(mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n"))
|
||||
(format "Merge: %s\n"
|
||||
(mapconcat 'identity merge-heads " "))
|
||||
""))
|
||||
'face 'git-header-face)
|
||||
(propertize git-log-msg-separator 'face 'git-separator-face)
|
||||
@@ -1285,7 +1314,7 @@ Return the list of files that haven't been handled."
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward "^Date: \\(.*\\)$" nil t)
|
||||
(setq date (match-string 1)))))
|
||||
(git-setup-log-buffer buffer author-name author-email subject date))
|
||||
(git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date))
|
||||
(if (boundp 'log-edit-diff-function)
|
||||
(log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)
|
||||
(log-edit-diff-function . git-log-edit-diff)) buffer)
|
||||
@@ -1296,11 +1325,13 @@ Return the list of files that haven't been handled."
|
||||
|
||||
(defun git-setup-commit-buffer (commit)
|
||||
"Setup the commit buffer with the contents of COMMIT."
|
||||
(let (author-name author-email subject date msg)
|
||||
(let (parents author-name author-email subject date msg)
|
||||
(with-temp-buffer
|
||||
(let ((coding-system (git-get-logoutput-coding-system)))
|
||||
(git-call-process-env t nil "log" "-1" "--pretty=medium" commit)
|
||||
(git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit)
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward "^Merge: *\\(.*\\)$" nil t)
|
||||
(setq parents (cdr (split-string (match-string 1) " +"))))
|
||||
(when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
|
||||
(setq author-name (match-string 1))
|
||||
(setq author-email (match-string 2)))
|
||||
@@ -1312,14 +1343,14 @@ Return the list of files that haven't been handled."
|
||||
(setq subject (pop msg))
|
||||
(while (and msg (zerop (length (car msg))) (pop msg)))))
|
||||
(git-setup-log-buffer (get-buffer-create "*git-commit*")
|
||||
author-name author-email subject date
|
||||
parents author-name author-email subject date
|
||||
(mapconcat #'identity msg "\n"))))
|
||||
|
||||
(defun git-get-commit-files (commit)
|
||||
"Retrieve the list of files modified by COMMIT."
|
||||
(let (files)
|
||||
(with-temp-buffer
|
||||
(git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit)
|
||||
(git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
|
||||
(push (match-string 1) files)))
|
||||
@@ -1333,10 +1364,11 @@ amended version of it."
|
||||
(when (git-empty-db-p) (error "No commit to amend."))
|
||||
(let* ((commit (git-rev-parse "HEAD"))
|
||||
(files (git-get-commit-files commit)))
|
||||
(when (git-call-process-display-error "reset" "--soft" "HEAD^")
|
||||
(git-update-status-files (copy-sequence files) 'uptodate)
|
||||
(git-mark-files git-status files)
|
||||
(git-refresh-files)
|
||||
(when (if (git-rev-parse "HEAD^")
|
||||
(git-call-process-display-error "reset" "--soft" "HEAD^")
|
||||
(and (git-update-ref "ORIG_HEAD" commit)
|
||||
(git-update-ref "HEAD" nil commit)))
|
||||
(git-update-status-files files t)
|
||||
(git-setup-commit-buffer commit)
|
||||
(git-commit-file))))
|
||||
|
||||
@@ -1377,27 +1409,10 @@ amended version of it."
|
||||
(defun git-refresh-status ()
|
||||
"Refresh the git status buffer."
|
||||
(interactive)
|
||||
(let* ((status git-status)
|
||||
(pos (ewoc-locate status))
|
||||
(marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info)))))
|
||||
(cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
|
||||
(unless status (error "Not in git-status buffer."))
|
||||
(message "Refreshing git status...")
|
||||
(git-call-process-env nil nil "update-index" "--refresh")
|
||||
(git-clear-status status)
|
||||
(git-update-status-files nil)
|
||||
; restore file marks
|
||||
(when marked-files
|
||||
(git-status-filenames-map status
|
||||
(lambda (info)
|
||||
(setf (git-fileinfo->marked info) t)
|
||||
(setf (git-fileinfo->needs-refresh info) t))
|
||||
marked-files)
|
||||
(git-refresh-files))
|
||||
; move point to the current file name if any
|
||||
(message "Refreshing git status...done")
|
||||
(let ((node (and cur-name (git-find-status-file status cur-name))))
|
||||
(when node (ewoc-goto-node status node)))))
|
||||
(unless git-status (error "Not in git-status buffer."))
|
||||
(message "Refreshing git status...")
|
||||
(git-update-status-files)
|
||||
(message "Refreshing git status...done"))
|
||||
|
||||
(defun git-status-quit ()
|
||||
"Quit git-status mode."
|
||||
@@ -1434,6 +1449,7 @@ amended version of it."
|
||||
(define-key map "\r" 'git-find-file)
|
||||
(define-key map "g" 'git-refresh-status)
|
||||
(define-key map "i" 'git-ignore-file)
|
||||
(define-key map "I" 'git-insert-file)
|
||||
(define-key map "l" 'git-log-file)
|
||||
(define-key map "m" 'git-mark-file)
|
||||
(define-key map "M" 'git-mark-all)
|
||||
@@ -1490,6 +1506,7 @@ amended version of it."
|
||||
["Revert File" git-revert-file t]
|
||||
["Ignore File" git-ignore-file t]
|
||||
["Remove File" git-remove-file t]
|
||||
["Insert File" git-insert-file t]
|
||||
"--------"
|
||||
["Find File" git-find-file t]
|
||||
["View File" git-view-file t]
|
||||
@@ -1576,8 +1593,8 @@ Meant to be used in `after-save-hook'."
|
||||
(let ((filename (file-relative-name file dir)))
|
||||
; skip files located inside the .git directory
|
||||
(unless (string-match "^\\.git/" filename)
|
||||
(git-call-process-env nil nil "add" "--refresh" "--" filename)
|
||||
(git-update-status-files (list filename) 'uptodate)))))))
|
||||
(git-call-process nil "add" "--refresh" "--" filename)
|
||||
(git-update-status-files (list filename))))))))
|
||||
|
||||
(defun git-help ()
|
||||
"Display help for Git mode."
|
||||
|
||||
@@ -981,7 +981,7 @@ class P4Sync(Command):
|
||||
if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
|
||||
text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
|
||||
elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
|
||||
text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text)
|
||||
text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
|
||||
|
||||
contents[stat['depotFile']] = text
|
||||
|
||||
|
||||
111
daemon.c
111
daemon.c
@@ -1,7 +1,6 @@
|
||||
#include "cache.h"
|
||||
#include "pkt-line.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "interpolate.h"
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
@@ -54,26 +53,10 @@ static const char *user_path;
|
||||
static unsigned int timeout;
|
||||
static unsigned int init_timeout;
|
||||
|
||||
/*
|
||||
* Static table for now. Ugh.
|
||||
* Feel free to make dynamic as needed.
|
||||
*/
|
||||
#define INTERP_SLOT_HOST (0)
|
||||
#define INTERP_SLOT_CANON_HOST (1)
|
||||
#define INTERP_SLOT_IP (2)
|
||||
#define INTERP_SLOT_PORT (3)
|
||||
#define INTERP_SLOT_DIR (4)
|
||||
#define INTERP_SLOT_PERCENT (5)
|
||||
|
||||
static struct interp interp_table[] = {
|
||||
{ "%H", 0},
|
||||
{ "%CH", 0},
|
||||
{ "%IP", 0},
|
||||
{ "%P", 0},
|
||||
{ "%D", 0},
|
||||
{ "%%", 0},
|
||||
};
|
||||
|
||||
static char *hostname;
|
||||
static char *canon_hostname;
|
||||
static char *ip_address;
|
||||
static char *tcp_port;
|
||||
|
||||
static void logreport(int priority, const char *err, va_list params)
|
||||
{
|
||||
@@ -163,7 +146,7 @@ static int avoid_alias(char *p)
|
||||
}
|
||||
}
|
||||
|
||||
static char *path_ok(struct interp *itable)
|
||||
static char *path_ok(char *directory)
|
||||
{
|
||||
static char rpath[PATH_MAX];
|
||||
static char interp_path[PATH_MAX];
|
||||
@@ -171,7 +154,7 @@ static char *path_ok(struct interp *itable)
|
||||
char *path;
|
||||
char *dir;
|
||||
|
||||
dir = itable[INTERP_SLOT_DIR].value;
|
||||
dir = directory;
|
||||
|
||||
if (avoid_alias(dir)) {
|
||||
logerror("'%s': aliased", dir);
|
||||
@@ -201,14 +184,27 @@ static char *path_ok(struct interp *itable)
|
||||
}
|
||||
}
|
||||
else if (interpolated_path && saw_extended_args) {
|
||||
struct strbuf expanded_path = STRBUF_INIT;
|
||||
struct strbuf_expand_dict_entry dict[] = {
|
||||
{ "H", hostname },
|
||||
{ "CH", canon_hostname },
|
||||
{ "IP", ip_address },
|
||||
{ "P", tcp_port },
|
||||
{ "D", directory },
|
||||
{ "%", "%" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
if (*dir != '/') {
|
||||
/* Allow only absolute */
|
||||
logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
interpolate(interp_path, PATH_MAX, interpolated_path,
|
||||
interp_table, ARRAY_SIZE(interp_table));
|
||||
strbuf_expand(&expanded_path, interpolated_path,
|
||||
strbuf_expand_dict_cb, &dict);
|
||||
strlcpy(interp_path, expanded_path.buf, PATH_MAX);
|
||||
strbuf_release(&expanded_path);
|
||||
loginfo("Interpolated dir '%s'", interp_path);
|
||||
|
||||
dir = interp_path;
|
||||
@@ -233,7 +229,7 @@ static char *path_ok(struct interp *itable)
|
||||
* prefixing the base path
|
||||
*/
|
||||
if (base_path && base_path_relaxed && !retried_path) {
|
||||
dir = itable[INTERP_SLOT_DIR].value;
|
||||
dir = directory;
|
||||
retried_path = 1;
|
||||
continue;
|
||||
}
|
||||
@@ -299,14 +295,12 @@ static int git_daemon_config(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_service(struct interp *itable, struct daemon_service *service)
|
||||
static int run_service(char *dir, struct daemon_service *service)
|
||||
{
|
||||
const char *path;
|
||||
int enabled = service->enabled;
|
||||
|
||||
loginfo("Request %s for '%s'",
|
||||
service->name,
|
||||
itable[INTERP_SLOT_DIR].value);
|
||||
loginfo("Request %s for '%s'", service->name, dir);
|
||||
|
||||
if (!enabled && !service->overridable) {
|
||||
logerror("'%s': service not enabled.", service->name);
|
||||
@@ -314,7 +308,7 @@ static int run_service(struct interp *itable, struct daemon_service *service)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(path = path_ok(itable)))
|
||||
if (!(path = path_ok(dir)))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
@@ -413,13 +407,13 @@ static void make_service_overridable(const char *name, int ena)
|
||||
|
||||
/*
|
||||
* Separate the "extra args" information as supplied by the client connection.
|
||||
* Any resulting data is squirreled away in the given interpolation table.
|
||||
*/
|
||||
static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
|
||||
static void parse_extra_args(char *extra_args, int buflen)
|
||||
{
|
||||
char *val;
|
||||
int vallen;
|
||||
char *end = extra_args + buflen;
|
||||
char *hp;
|
||||
|
||||
while (extra_args < end && *extra_args) {
|
||||
saw_extended_args = 1;
|
||||
@@ -433,25 +427,22 @@ static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
|
||||
if (port) {
|
||||
*port = 0;
|
||||
port++;
|
||||
interp_set_entry(table, INTERP_SLOT_PORT, port);
|
||||
free(tcp_port);
|
||||
tcp_port = xstrdup(port);
|
||||
}
|
||||
interp_set_entry(table, INTERP_SLOT_HOST, host);
|
||||
free(hostname);
|
||||
hostname = xstrdup(host);
|
||||
}
|
||||
|
||||
/* On to the next one */
|
||||
extra_args = val + vallen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_in_extra_table_entries(struct interp *itable)
|
||||
{
|
||||
char *hp;
|
||||
|
||||
/*
|
||||
* Replace literal host with lowercase-ized hostname.
|
||||
*/
|
||||
hp = interp_table[INTERP_SLOT_HOST].value;
|
||||
hp = hostname;
|
||||
if (!hp)
|
||||
return;
|
||||
for ( ; *hp; hp++)
|
||||
@@ -470,17 +461,17 @@ static void fill_in_extra_table_entries(struct interp *itable)
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
|
||||
gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0);
|
||||
gai = getaddrinfo(hostname, 0, &hints, &ai0);
|
||||
if (!gai) {
|
||||
for (ai = ai0; ai; ai = ai->ai_next) {
|
||||
struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
|
||||
|
||||
inet_ntop(AF_INET, &sin_addr->sin_addr,
|
||||
addrbuf, sizeof(addrbuf));
|
||||
interp_set_entry(interp_table,
|
||||
INTERP_SLOT_CANON_HOST, ai->ai_canonname);
|
||||
interp_set_entry(interp_table,
|
||||
INTERP_SLOT_IP, addrbuf);
|
||||
free(canon_hostname);
|
||||
canon_hostname = xstrdup(ai->ai_canonname);
|
||||
free(ip_address);
|
||||
ip_address = xstrdup(addrbuf);
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(ai0);
|
||||
@@ -493,7 +484,7 @@ static void fill_in_extra_table_entries(struct interp *itable)
|
||||
char **ap;
|
||||
static char addrbuf[HOST_NAME_MAX + 1];
|
||||
|
||||
hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value);
|
||||
hent = gethostbyname(hostname);
|
||||
|
||||
ap = hent->h_addr_list;
|
||||
memset(&sa, 0, sizeof sa);
|
||||
@@ -504,8 +495,10 @@ static void fill_in_extra_table_entries(struct interp *itable)
|
||||
inet_ntop(hent->h_addrtype, &sa.sin_addr,
|
||||
addrbuf, sizeof(addrbuf));
|
||||
|
||||
interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name);
|
||||
interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf);
|
||||
free(canon_hostname);
|
||||
canon_hostname = xstrdup(hent->h_name);
|
||||
free(ip_address);
|
||||
ip_address = xstrdup(addrbuf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -557,16 +550,14 @@ static int execute(struct sockaddr *addr)
|
||||
pktlen--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the path interpolation table for this connection.
|
||||
*/
|
||||
interp_clear_table(interp_table, ARRAY_SIZE(interp_table));
|
||||
interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%");
|
||||
free(hostname);
|
||||
free(canon_hostname);
|
||||
free(ip_address);
|
||||
free(tcp_port);
|
||||
hostname = canon_hostname = ip_address = tcp_port = NULL;
|
||||
|
||||
if (len != pktlen) {
|
||||
parse_extra_args(interp_table, line + len + 1, pktlen - len - 1);
|
||||
fill_in_extra_table_entries(interp_table);
|
||||
}
|
||||
if (len != pktlen)
|
||||
parse_extra_args(line + len + 1, pktlen - len - 1);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
|
||||
struct daemon_service *s = &(daemon_service[i]);
|
||||
@@ -578,9 +569,7 @@ static int execute(struct sockaddr *addr)
|
||||
* Note: The directory here is probably context sensitive,
|
||||
* and might depend on the actual service being performed.
|
||||
*/
|
||||
interp_set_entry(interp_table,
|
||||
INTERP_SLOT_DIR, line + namelen + 5);
|
||||
return run_service(interp_table, s);
|
||||
return run_service(line + namelen + 5, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
|
||||
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
|
||||
enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
|
||||
|
||||
/* Parallel index stat data preload? */
|
||||
int core_preload_index = 0;
|
||||
|
||||
/* This is set by setup_git_dir_gently() and/or git_default_config() */
|
||||
char *git_work_tree_cfg;
|
||||
static char *work_tree;
|
||||
|
||||
@@ -14,7 +14,7 @@ sort |
|
||||
while read cmd
|
||||
do
|
||||
sed -n '
|
||||
/NAME/,/git-'"$cmd"'/H
|
||||
/^NAME/,/git-'"$cmd"'/H
|
||||
${
|
||||
x
|
||||
s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
|
||||
|
||||
@@ -191,6 +191,21 @@ check_expected_revs() {
|
||||
done
|
||||
}
|
||||
|
||||
bisect_skip() {
|
||||
all=''
|
||||
for arg in "$@"
|
||||
do
|
||||
case "$arg" in
|
||||
*..*)
|
||||
revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
|
||||
*)
|
||||
revs="'$arg'" ;;
|
||||
esac
|
||||
all="$all $revs"
|
||||
done
|
||||
bisect_state 'skip' $all
|
||||
}
|
||||
|
||||
bisect_state() {
|
||||
bisect_autostart
|
||||
state=$1
|
||||
@@ -630,8 +645,10 @@ case "$#" in
|
||||
git bisect -h ;;
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
bad|good|skip)
|
||||
bad|good)
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
skip)
|
||||
bisect_skip "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
|
||||
10
git-pull.sh
10
git-pull.sh
@@ -16,13 +16,17 @@ cd_to_toplevel
|
||||
test -z "$(git ls-files -u)" ||
|
||||
die "You are in the middle of a conflicted merge."
|
||||
|
||||
strategy_args= no_stat= no_commit= squash= no_ff= log_arg=
|
||||
strategy_args= no_stat= no_commit= squash= no_ff= log_arg= verbosity=
|
||||
curr_branch=$(git symbolic-ref -q HEAD)
|
||||
curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
|
||||
rebase=$(git config --bool branch.$curr_branch_short.rebase)
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
-q|--quiet)
|
||||
verbosity="$verbosity -q" ;;
|
||||
-v|--verbose)
|
||||
verbosity="$verbosity -v" ;;
|
||||
-n|--no-stat|--no-summary)
|
||||
no_stat=-n ;;
|
||||
--stat|--summary)
|
||||
@@ -121,7 +125,7 @@ test true = "$rebase" && {
|
||||
"refs/remotes/$origin/$reflist" 2>/dev/null)"
|
||||
}
|
||||
orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
|
||||
git fetch --update-head-ok "$@" || exit 1
|
||||
git fetch $verbosity --update-head-ok "$@" || exit 1
|
||||
|
||||
curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
|
||||
if test -n "$orig_head" && test "$curr_head" != "$orig_head"
|
||||
@@ -182,4 +186,4 @@ test true = "$rebase" &&
|
||||
exec git-rebase $strategy_args --onto $merge_head \
|
||||
${oldremoteref:-$merge_head}
|
||||
exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \
|
||||
"$merge_name" HEAD $merge_head
|
||||
"$merge_name" HEAD $merge_head $verbosity
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
# This file is licensed under the GPL v2, or a later version
|
||||
# at the discretion of Linus Torvalds.
|
||||
|
||||
USAGE='<commit> <url> [<head>]'
|
||||
LONG_USAGE='Summarizes the changes since <commit> to the standard output,
|
||||
and includes <url> in the message generated.'
|
||||
USAGE='<start> <url> [<end>]'
|
||||
LONG_USAGE='Summarizes the changes between two commits to the standard output,
|
||||
and includes the given URL in the generated summary.'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
|
||||
@@ -22,8 +22,12 @@ use Term::ReadLine;
|
||||
use Getopt::Long;
|
||||
use Data::Dumper;
|
||||
use Term::ANSIColor;
|
||||
use File::Temp qw/ tempdir /;
|
||||
use Error qw(:try);
|
||||
use Git;
|
||||
|
||||
Getopt::Long::Configure qw/ pass_through /;
|
||||
|
||||
package FakeTerm;
|
||||
sub new {
|
||||
my ($class, $reason) = @_;
|
||||
@@ -38,7 +42,7 @@ package main;
|
||||
|
||||
sub usage {
|
||||
print <<EOT;
|
||||
git send-email [options] <file | directory>...
|
||||
git send-email [options] <file | directory | rev-list options >
|
||||
|
||||
Composing:
|
||||
--from <str> * Email From:
|
||||
@@ -47,6 +51,7 @@ git send-email [options] <file | directory>...
|
||||
--bcc <str> * Email Bcc:
|
||||
--subject <str> * Email "Subject:"
|
||||
--in-reply-to <str> * Email "In-Reply-To:"
|
||||
--annotate * Review each patch that will be sent in an editor.
|
||||
--compose * Open an editor for introduction.
|
||||
|
||||
Sending:
|
||||
@@ -73,6 +78,8 @@ git send-email [options] <file | directory>...
|
||||
--quiet * Output one line of info per email.
|
||||
--dry-run * Don't actually send the emails.
|
||||
--[no-]validate * Perform patch sanity checks. Default on.
|
||||
--[no-]format-patch * understand any non optional arguments as
|
||||
`git format-patch` ones.
|
||||
|
||||
EOT
|
||||
exit(1);
|
||||
@@ -124,12 +131,10 @@ my $auth;
|
||||
sub unique_email_list(@);
|
||||
sub cleanup_compose_files();
|
||||
|
||||
# Constants (essentially)
|
||||
my $compose_filename = ".msg.$$";
|
||||
|
||||
# Variables we fill in automatically, or via prompting:
|
||||
my (@to,@cc,@initial_cc,@bcclist,@xh,
|
||||
$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
|
||||
$initial_reply_to,$initial_subject,@files,
|
||||
$author,$sender,$smtp_authpass,$annotate,$compose,$time);
|
||||
|
||||
my $envelope_sender;
|
||||
|
||||
@@ -149,6 +154,27 @@ if ($@) {
|
||||
|
||||
# Behavior modification variables
|
||||
my ($quiet, $dry_run) = (0, 0);
|
||||
my $format_patch;
|
||||
my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
|
||||
|
||||
# Handle interactive edition of files.
|
||||
my $multiedit;
|
||||
my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
|
||||
sub do_edit {
|
||||
if (defined($multiedit) && !$multiedit) {
|
||||
map {
|
||||
system('sh', '-c', $editor.' "$@"', $editor, $_);
|
||||
if (($? & 127) || ($? >> 8)) {
|
||||
die("the editor exited uncleanly, aborting everything");
|
||||
}
|
||||
} @_;
|
||||
} else {
|
||||
system('sh', '-c', $editor.' "$@"', $editor, @_);
|
||||
if (($? & 127) || ($? >> 8)) {
|
||||
die("the editor exited uncleanly, aborting everything");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Variables with corresponding config settings
|
||||
my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
|
||||
@@ -179,6 +205,7 @@ my %config_settings = (
|
||||
"aliasesfile" => \@alias_files,
|
||||
"suppresscc" => \@suppress_cc,
|
||||
"envelopesender" => \$envelope_sender,
|
||||
"multiedit" => \$multiedit,
|
||||
);
|
||||
|
||||
# Handle Uncouth Termination
|
||||
@@ -221,6 +248,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
|
||||
"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
|
||||
"smtp-encryption=s" => \$smtp_encryption,
|
||||
"identity=s" => \$identity,
|
||||
"annotate" => \$annotate,
|
||||
"compose" => \$compose,
|
||||
"quiet" => \$quiet,
|
||||
"cc-cmd=s" => \$cc_cmd,
|
||||
@@ -231,6 +259,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
|
||||
"envelope-sender=s" => \$envelope_sender,
|
||||
"thread!" => \$thread,
|
||||
"validate!" => \$validate,
|
||||
"format-patch!" => \$format_patch,
|
||||
);
|
||||
|
||||
unless ($rc) {
|
||||
@@ -345,10 +374,13 @@ my %parse_alias = (
|
||||
# spaces delimit multiple addresses
|
||||
$aliases{$1} = [ split(/\s+/, $2) ];
|
||||
}}},
|
||||
pine => sub { my $fh = shift; while (<$fh>) {
|
||||
if (/^(\S+)\t.*\t(.*)$/) {
|
||||
pine => sub { my $fh = shift; my $f='\t[^\t]*';
|
||||
for (my $x = ''; defined($x); $x = $_) {
|
||||
chomp $x;
|
||||
$x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);
|
||||
$x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
|
||||
$aliases{$1} = [ split(/\s*,\s*/, $2) ];
|
||||
}}},
|
||||
}},
|
||||
gnus => sub { my $fh = shift; while (<$fh>) {
|
||||
if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
|
||||
$aliases{$1} = [ $2 ];
|
||||
@@ -365,23 +397,52 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
|
||||
|
||||
($sender) = expand_aliases($sender) if defined $sender;
|
||||
|
||||
# returns 1 if the conflict must be solved using it as a format-patch argument
|
||||
sub check_file_rev_conflict($) {
|
||||
my $f = shift;
|
||||
try {
|
||||
$repo->command('rev-parse', '--verify', '--quiet', $f);
|
||||
if (defined($format_patch)) {
|
||||
print "foo\n";
|
||||
return $format_patch;
|
||||
}
|
||||
die(<<EOF);
|
||||
File '$f' exists but it could also be the range of commits
|
||||
to produce patches for. Please disambiguate by...
|
||||
|
||||
* Saying "./$f" if you mean a file; or
|
||||
* Giving --format-patch option if you mean a range.
|
||||
EOF
|
||||
} catch Git::Error::Command with {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
# Now that all the defaults are set, process the rest of the command line
|
||||
# arguments and collect up the files that need to be processed.
|
||||
for my $f (@ARGV) {
|
||||
if (-d $f) {
|
||||
my @rev_list_opts;
|
||||
while (my $f = pop @ARGV) {
|
||||
if ($f eq "--") {
|
||||
push @rev_list_opts, "--", @ARGV;
|
||||
@ARGV = ();
|
||||
} elsif (-d $f and !check_file_rev_conflict($f)) {
|
||||
opendir(DH,$f)
|
||||
or die "Failed to opendir $f: $!";
|
||||
|
||||
push @files, grep { -f $_ } map { +$f . "/" . $_ }
|
||||
sort readdir(DH);
|
||||
closedir(DH);
|
||||
} elsif (-f $f or -p $f) {
|
||||
} elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
|
||||
push @files, $f;
|
||||
} else {
|
||||
print STDERR "Skipping $f - not found.\n";
|
||||
push @rev_list_opts, $f;
|
||||
}
|
||||
}
|
||||
|
||||
if (@rev_list_opts) {
|
||||
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
|
||||
}
|
||||
|
||||
if ($validate) {
|
||||
foreach my $f (@files) {
|
||||
unless (-p $f) {
|
||||
@@ -400,6 +461,108 @@ if (@files) {
|
||||
usage();
|
||||
}
|
||||
|
||||
sub get_patch_subject($) {
|
||||
my $fn = shift;
|
||||
open (my $fh, '<', $fn);
|
||||
while (my $line = <$fh>) {
|
||||
next unless ($line =~ /^Subject: (.*)$/);
|
||||
close $fh;
|
||||
return "GIT: $1\n";
|
||||
}
|
||||
close $fh;
|
||||
die "No subject line in $fn ?";
|
||||
}
|
||||
|
||||
if ($compose) {
|
||||
# Note that this does not need to be secure, but we will make a small
|
||||
# effort to have it be unique
|
||||
open(C,">",$compose_filename)
|
||||
or die "Failed to open for writing $compose_filename: $!";
|
||||
|
||||
|
||||
my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
|
||||
my $tpl_subject = $initial_subject || '';
|
||||
my $tpl_reply_to = $initial_reply_to || '';
|
||||
|
||||
print C <<EOT;
|
||||
From $tpl_sender # This line is ignored.
|
||||
GIT: Lines beginning in "GIT: " will be removed.
|
||||
GIT: Consider including an overall diffstat or table of contents
|
||||
GIT: for the patch you are writing.
|
||||
GIT:
|
||||
GIT: Clear the body content if you don't wish to send a summary.
|
||||
From: $tpl_sender
|
||||
Subject: $tpl_subject
|
||||
In-Reply-To: $tpl_reply_to
|
||||
|
||||
EOT
|
||||
for my $f (@files) {
|
||||
print C get_patch_subject($f);
|
||||
}
|
||||
close(C);
|
||||
|
||||
my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
|
||||
|
||||
if ($annotate) {
|
||||
do_edit($compose_filename, @files);
|
||||
} else {
|
||||
do_edit($compose_filename);
|
||||
}
|
||||
|
||||
open(C2,">",$compose_filename . ".final")
|
||||
or die "Failed to open $compose_filename.final : " . $!;
|
||||
|
||||
open(C,"<",$compose_filename)
|
||||
or die "Failed to open $compose_filename : " . $!;
|
||||
|
||||
my $need_8bit_cte = file_has_nonascii($compose_filename);
|
||||
my $in_body = 0;
|
||||
my $summary_empty = 1;
|
||||
while(<C>) {
|
||||
next if m/^GIT: /;
|
||||
if ($in_body) {
|
||||
$summary_empty = 0 unless (/^\n$/);
|
||||
} elsif (/^\n$/) {
|
||||
$in_body = 1;
|
||||
if ($need_8bit_cte) {
|
||||
print C2 "MIME-Version: 1.0\n",
|
||||
"Content-Type: text/plain; ",
|
||||
"charset=utf-8\n",
|
||||
"Content-Transfer-Encoding: 8bit\n";
|
||||
}
|
||||
} elsif (/^MIME-Version:/i) {
|
||||
$need_8bit_cte = 0;
|
||||
} elsif (/^Subject:\s*(.+)\s*$/i) {
|
||||
$initial_subject = $1;
|
||||
my $subject = $initial_subject;
|
||||
$_ = "Subject: " .
|
||||
($subject =~ /[^[:ascii:]]/ ?
|
||||
quote_rfc2047($subject) :
|
||||
$subject) .
|
||||
"\n";
|
||||
} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
|
||||
$initial_reply_to = $1;
|
||||
next;
|
||||
} elsif (/^From:\s*(.+)\s*$/i) {
|
||||
$sender = $1;
|
||||
next;
|
||||
} elsif (/^(?:To|Cc|Bcc):/i) {
|
||||
print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
|
||||
next;
|
||||
}
|
||||
print C2 $_;
|
||||
}
|
||||
close(C);
|
||||
close(C2);
|
||||
|
||||
if ($summary_empty) {
|
||||
print "Summary email is empty, skipping it\n";
|
||||
$compose = -1;
|
||||
}
|
||||
} elsif ($annotate) {
|
||||
do_edit(@files);
|
||||
}
|
||||
|
||||
my $prompting = 0;
|
||||
if (!defined $sender) {
|
||||
$sender = $repoauthor || $repocommitter || '';
|
||||
@@ -444,17 +607,6 @@ sub expand_aliases {
|
||||
@initial_cc = expand_aliases(@initial_cc);
|
||||
@bcclist = expand_aliases(@bcclist);
|
||||
|
||||
if (!defined $initial_subject && $compose) {
|
||||
while (1) {
|
||||
$_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
|
||||
last if defined $_;
|
||||
print "\n";
|
||||
}
|
||||
|
||||
$initial_subject = $_;
|
||||
$prompting++;
|
||||
}
|
||||
|
||||
if ($thread && !defined $initial_reply_to && $prompting) {
|
||||
while (1) {
|
||||
$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
|
||||
@@ -481,59 +633,6 @@ if (!defined $smtp_server) {
|
||||
}
|
||||
|
||||
if ($compose) {
|
||||
# Note that this does not need to be secure, but we will make a small
|
||||
# effort to have it be unique
|
||||
open(C,">",$compose_filename)
|
||||
or die "Failed to open for writing $compose_filename: $!";
|
||||
print C "From $sender # This line is ignored.\n";
|
||||
printf C "Subject: %s\n\n", $initial_subject;
|
||||
printf C <<EOT;
|
||||
GIT: Please enter your email below.
|
||||
GIT: Lines beginning in "GIT: " will be removed.
|
||||
GIT: Consider including an overall diffstat or table of contents
|
||||
GIT: for the patch you are writing.
|
||||
|
||||
EOT
|
||||
close(C);
|
||||
|
||||
my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
|
||||
system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
|
||||
|
||||
open(C2,">",$compose_filename . ".final")
|
||||
or die "Failed to open $compose_filename.final : " . $!;
|
||||
|
||||
open(C,"<",$compose_filename)
|
||||
or die "Failed to open $compose_filename : " . $!;
|
||||
|
||||
my $need_8bit_cte = file_has_nonascii($compose_filename);
|
||||
my $in_body = 0;
|
||||
while(<C>) {
|
||||
next if m/^GIT: /;
|
||||
if (!$in_body && /^\n$/) {
|
||||
$in_body = 1;
|
||||
if ($need_8bit_cte) {
|
||||
print C2 "MIME-Version: 1.0\n",
|
||||
"Content-Type: text/plain; ",
|
||||
"charset=utf-8\n",
|
||||
"Content-Transfer-Encoding: 8bit\n";
|
||||
}
|
||||
}
|
||||
if (!$in_body && /^MIME-Version:/i) {
|
||||
$need_8bit_cte = 0;
|
||||
}
|
||||
if (!$in_body && /^Subject: ?(.*)/i) {
|
||||
my $subject = $1;
|
||||
$_ = "Subject: " .
|
||||
($subject =~ /[^[:ascii:]]/ ?
|
||||
quote_rfc2047($subject) :
|
||||
$subject) .
|
||||
"\n";
|
||||
}
|
||||
print C2 $_;
|
||||
}
|
||||
close(C);
|
||||
close(C2);
|
||||
|
||||
while (1) {
|
||||
$_ = $term->readline("Send this email? (y|n) ");
|
||||
last if defined $_;
|
||||
@@ -545,7 +644,9 @@ EOT
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@files = ($compose_filename . ".final", @files);
|
||||
if ($compose > 0) {
|
||||
@files = ($compose_filename . ".final", @files);
|
||||
}
|
||||
}
|
||||
|
||||
# Variables we set as part of the loop over files
|
||||
|
||||
103
interpolate.c
103
interpolate.c
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006 Jon Loeliger
|
||||
*/
|
||||
|
||||
#include "git-compat-util.h"
|
||||
#include "interpolate.h"
|
||||
|
||||
|
||||
void interp_set_entry(struct interp *table, int slot, const char *value)
|
||||
{
|
||||
char *oldval = table[slot].value;
|
||||
char *newval = NULL;
|
||||
|
||||
free(oldval);
|
||||
|
||||
if (value)
|
||||
newval = xstrdup(value);
|
||||
|
||||
table[slot].value = newval;
|
||||
}
|
||||
|
||||
|
||||
void interp_clear_table(struct interp *table, int ninterps)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ninterps; i++) {
|
||||
interp_set_entry(table, i, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert a NUL-terminated string in buffer orig
|
||||
* into the supplied buffer, result, whose length is reslen,
|
||||
* performing substitutions on %-named sub-strings from
|
||||
* the table, interps, with ninterps entries.
|
||||
*
|
||||
* Example interps:
|
||||
* {
|
||||
* { "%H", "example.org"},
|
||||
* { "%port", "123"},
|
||||
* { "%%", "%"},
|
||||
* }
|
||||
*
|
||||
* Returns the length of the substituted string (not including the final \0).
|
||||
* Like with snprintf, if the result is >= reslen, then it overflowed.
|
||||
*/
|
||||
|
||||
unsigned long interpolate(char *result, unsigned long reslen,
|
||||
const char *orig,
|
||||
const struct interp *interps, int ninterps)
|
||||
{
|
||||
const char *src = orig;
|
||||
char *dest = result;
|
||||
unsigned long newlen = 0;
|
||||
const char *name, *value;
|
||||
unsigned long namelen, valuelen;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
while ((c = *src)) {
|
||||
if (c == '%') {
|
||||
/* Try to match an interpolation string. */
|
||||
for (i = 0; i < ninterps; i++) {
|
||||
name = interps[i].name;
|
||||
namelen = strlen(name);
|
||||
if (strncmp(src, name, namelen) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for valid interpolation. */
|
||||
if (i < ninterps) {
|
||||
value = interps[i].value;
|
||||
if (!value) {
|
||||
src += namelen;
|
||||
continue;
|
||||
}
|
||||
|
||||
valuelen = strlen(value);
|
||||
if (newlen + valuelen < reslen) {
|
||||
/* Substitute. */
|
||||
memcpy(dest, value, valuelen);
|
||||
dest += valuelen;
|
||||
}
|
||||
newlen += valuelen;
|
||||
src += namelen;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Straight copy one non-interpolation character. */
|
||||
if (newlen + 1 < reslen)
|
||||
*dest++ = *src;
|
||||
src++;
|
||||
newlen++;
|
||||
}
|
||||
|
||||
/* XXX: the previous loop always keep room for the ending NUL,
|
||||
we just need to check if there was room for a NUL in the first place */
|
||||
if (reslen > 0)
|
||||
*dest = '\0';
|
||||
return newlen;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006 Jon Loeliger
|
||||
*/
|
||||
|
||||
#ifndef INTERPOLATE_H
|
||||
#define INTERPOLATE_H
|
||||
|
||||
/*
|
||||
* Convert a NUL-terminated string in buffer orig,
|
||||
* performing substitutions on %-named sub-strings from
|
||||
* the interpretation table.
|
||||
*/
|
||||
|
||||
struct interp {
|
||||
const char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
extern void interp_set_entry(struct interp *table, int slot, const char *value);
|
||||
extern void interp_clear_table(struct interp *table, int ninterps);
|
||||
|
||||
extern unsigned long interpolate(char *result, unsigned long reslen,
|
||||
const char *orig,
|
||||
const struct interp *interps, int ninterps);
|
||||
|
||||
#endif /* INTERPOLATE_H */
|
||||
@@ -1,6 +1,43 @@
|
||||
#include "cache.h"
|
||||
#include "levenshtein.h"
|
||||
|
||||
/*
|
||||
* This function implements the Damerau-Levenshtein algorithm to
|
||||
* calculate a distance between strings.
|
||||
*
|
||||
* Basically, it says how many letters need to be swapped, substituted,
|
||||
* deleted from, or added to string1, at least, to get string2.
|
||||
*
|
||||
* The idea is to build a distance matrix for the substrings of both
|
||||
* strings. To avoid a large space complexity, only the last three rows
|
||||
* are kept in memory (if swaps had the same or higher cost as one deletion
|
||||
* plus one insertion, only two rows would be needed).
|
||||
*
|
||||
* At any stage, "i + 1" denotes the length of the current substring of
|
||||
* string1 that the distance is calculated for.
|
||||
*
|
||||
* row2 holds the current row, row1 the previous row (i.e. for the substring
|
||||
* of string1 of length "i"), and row0 the row before that.
|
||||
*
|
||||
* In other words, at the start of the big loop, row2[j + 1] contains the
|
||||
* Damerau-Levenshtein distance between the substring of string1 of length
|
||||
* "i" and the substring of string2 of length "j + 1".
|
||||
*
|
||||
* All the big loop does is determine the partial minimum-cost paths.
|
||||
*
|
||||
* It does so by calculating the costs of the path ending in characters
|
||||
* i (in string1) and j (in string2), respectively, given that the last
|
||||
* operation is a substition, a swap, a deletion, or an insertion.
|
||||
*
|
||||
* This implementation allows the costs to be weighted:
|
||||
*
|
||||
* - w (as in "sWap")
|
||||
* - s (as in "Substitution")
|
||||
* - a (for insertion, AKA "Add")
|
||||
* - d (as in "Deletion")
|
||||
*
|
||||
* Note that this algorithm calculates a distance _iff_ d == a.
|
||||
*/
|
||||
int levenshtein(const char *string1, const char *string2,
|
||||
int w, int s, int a, int d)
|
||||
{
|
||||
@@ -25,7 +62,7 @@ int levenshtein(const char *string1, const char *string2,
|
||||
row2[j + 1] > row0[j - 1] + w)
|
||||
row2[j + 1] = row0[j - 1] + w;
|
||||
/* deletion */
|
||||
if (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d)
|
||||
if (row2[j + 1] > row1[j + 1] + d)
|
||||
row2[j + 1] = row1[j + 1] + d;
|
||||
/* insertion */
|
||||
if (row2[j + 1] > row2[j] + a)
|
||||
|
||||
21
ll-merge.c
21
ll-merge.c
@@ -8,7 +8,6 @@
|
||||
#include "attr.h"
|
||||
#include "xdiff-interface.h"
|
||||
#include "run-command.h"
|
||||
#include "interpolate.h"
|
||||
#include "ll-merge.h"
|
||||
|
||||
struct ll_merge_driver;
|
||||
@@ -169,11 +168,12 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
|
||||
int virtual_ancestor)
|
||||
{
|
||||
char temp[3][50];
|
||||
char cmdbuf[2048];
|
||||
struct interp table[] = {
|
||||
{ "%O" },
|
||||
{ "%A" },
|
||||
{ "%B" },
|
||||
struct strbuf cmd = STRBUF_INIT;
|
||||
struct strbuf_expand_dict_entry dict[] = {
|
||||
{ "O", temp[0] },
|
||||
{ "A", temp[1] },
|
||||
{ "B", temp[2] },
|
||||
{ NULL }
|
||||
};
|
||||
struct child_process child;
|
||||
const char *args[20];
|
||||
@@ -189,17 +189,13 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
|
||||
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);
|
||||
strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
|
||||
|
||||
memset(&child, 0, sizeof(child));
|
||||
child.argv = args;
|
||||
args[0] = "sh";
|
||||
args[1] = "-c";
|
||||
args[2] = cmdbuf;
|
||||
args[2] = cmd.buf;
|
||||
args[3] = NULL;
|
||||
|
||||
status = run_command(&child);
|
||||
@@ -224,6 +220,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
|
||||
bad:
|
||||
for (i = 0; i < 3; i++)
|
||||
unlink(temp[i]);
|
||||
strbuf_release(&cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "string-list.h"
|
||||
#include "xdiff-interface.h"
|
||||
#include "ll-merge.h"
|
||||
#include "interpolate.h"
|
||||
#include "attr.h"
|
||||
#include "merge-recursive.h"
|
||||
#include "dir.h"
|
||||
|
||||
@@ -484,6 +484,28 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
|
||||
int unset)
|
||||
{
|
||||
int *target = opt->value;
|
||||
|
||||
if (unset)
|
||||
/* --no-quiet, --no-verbose */
|
||||
*target = 0;
|
||||
else if (opt->short_name == 'v') {
|
||||
if (*target >= 0)
|
||||
(*target)++;
|
||||
else
|
||||
*target = 1;
|
||||
} else {
|
||||
if (*target <= 0)
|
||||
(*target)--;
|
||||
else
|
||||
*target = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This should really be OPTION_FILENAME type as a part of
|
||||
* parse_options that take prefix to do this while parsing.
|
||||
|
||||
@@ -150,9 +150,15 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx);
|
||||
/*----- some often used options -----*/
|
||||
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
|
||||
|
||||
#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
|
||||
#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")
|
||||
#define OPT__VERBOSITY(var) \
|
||||
{ OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
|
||||
PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
|
||||
{ OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
|
||||
PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
|
||||
#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run")
|
||||
#define OPT__ABBREV(var) \
|
||||
{ OPTION_CALLBACK, 0, "abbrev", (var), "n", \
|
||||
|
||||
91
preload-index.c
Normal file
91
preload-index.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Linus Torvalds
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* Mostly randomly chosen maximum thread counts: we
|
||||
* cap the parallelism to 20 threads, and we want
|
||||
* to have at least 500 lstat's per thread for it to
|
||||
* be worth starting a thread.
|
||||
*/
|
||||
#define MAX_PARALLEL (20)
|
||||
#define THREAD_COST (500)
|
||||
|
||||
struct thread_data {
|
||||
pthread_t pthread;
|
||||
struct index_state *index;
|
||||
const char **pathspec;
|
||||
int offset, nr;
|
||||
};
|
||||
|
||||
static void *preload_thread(void *_data)
|
||||
{
|
||||
int nr;
|
||||
struct thread_data *p = _data;
|
||||
struct index_state *index = p->index;
|
||||
struct cache_entry **cep = index->cache + p->offset;
|
||||
|
||||
nr = p->nr;
|
||||
if (nr + p->offset > index->cache_nr)
|
||||
nr = index->cache_nr - p->offset;
|
||||
|
||||
do {
|
||||
struct cache_entry *ce = *cep++;
|
||||
struct stat st;
|
||||
|
||||
if (ce_stage(ce))
|
||||
continue;
|
||||
if (ce_uptodate(ce))
|
||||
continue;
|
||||
if (!ce_path_match(ce, p->pathspec))
|
||||
continue;
|
||||
if (lstat(ce->name, &st))
|
||||
continue;
|
||||
if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
|
||||
continue;
|
||||
ce_mark_uptodate(ce);
|
||||
} while (--nr > 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void preload_index(struct index_state *index, const char **pathspec)
|
||||
{
|
||||
int threads, i, work, offset;
|
||||
struct thread_data data[MAX_PARALLEL];
|
||||
|
||||
if (!core_preload_index)
|
||||
return;
|
||||
|
||||
threads = index->cache_nr / THREAD_COST;
|
||||
if (threads < 2)
|
||||
return;
|
||||
if (threads > MAX_PARALLEL)
|
||||
threads = MAX_PARALLEL;
|
||||
offset = 0;
|
||||
work = (index->cache_nr + threads - 1) / threads;
|
||||
for (i = 0; i < threads; i++) {
|
||||
struct thread_data *p = data+i;
|
||||
p->index = index;
|
||||
p->pathspec = pathspec;
|
||||
p->offset = offset;
|
||||
p->nr = work;
|
||||
offset += work;
|
||||
if (pthread_create(&p->pthread, NULL, preload_thread, p))
|
||||
die("unable to create threaded lstat");
|
||||
}
|
||||
for (i = 0; i < threads; i++) {
|
||||
struct thread_data *p = data+i;
|
||||
if (pthread_join(p->pthread, NULL))
|
||||
die("unable to join threaded lstat");
|
||||
}
|
||||
}
|
||||
|
||||
int read_index_preload(struct index_state *index, const char **pathspec)
|
||||
{
|
||||
int retval = read_index(index);
|
||||
|
||||
preload_index(index, pathspec);
|
||||
return retval;
|
||||
}
|
||||
@@ -1749,6 +1749,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
||||
error("bad packed object CRC for %s",
|
||||
sha1_to_hex(sha1));
|
||||
mark_bad_packed_object(p, sha1);
|
||||
unuse_pack(&w_curs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -2288,7 +2289,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
|
||||
memcpy(buffer, filename, dirlen);
|
||||
strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
|
||||
fd = mkstemp(buffer);
|
||||
if (fd < 0 && dirlen) {
|
||||
if (fd < 0 && dirlen && errno == ENOENT) {
|
||||
/* Make sure the directory exists */
|
||||
memcpy(buffer, filename, dirlen);
|
||||
buffer[dirlen-1] = 0;
|
||||
@@ -2314,7 +2315,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
|
||||
filename = sha1_file_name(sha1);
|
||||
fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
|
||||
if (fd < 0) {
|
||||
if (errno == EPERM)
|
||||
if (errno == EACCES)
|
||||
return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
|
||||
else
|
||||
return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno));
|
||||
|
||||
16
strbuf.c
16
strbuf.c
@@ -237,6 +237,22 @@ void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
|
||||
}
|
||||
}
|
||||
|
||||
size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
|
||||
void *context)
|
||||
{
|
||||
struct strbuf_expand_dict_entry *e = context;
|
||||
size_t len;
|
||||
|
||||
for (; e->placeholder && (len = strlen(e->placeholder)); e++) {
|
||||
if (!strncmp(placeholder, e->placeholder, len)) {
|
||||
if (e->value)
|
||||
strbuf_addstr(sb, e->value);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
|
||||
{
|
||||
size_t res;
|
||||
|
||||
5
strbuf.h
5
strbuf.h
@@ -111,6 +111,11 @@ extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
|
||||
|
||||
typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
|
||||
extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
|
||||
struct strbuf_expand_dict_entry {
|
||||
const char *placeholder;
|
||||
const char *value;
|
||||
};
|
||||
extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
|
||||
|
||||
__attribute__((format(printf,2,3)))
|
||||
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
|
||||
|
||||
@@ -43,6 +43,13 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
|
||||
|
||||
GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff |
|
||||
grep "^diff --git a/file b/file"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'diff attribute' '
|
||||
|
||||
git config diff.parrot.command echo &&
|
||||
@@ -68,6 +75,13 @@ test_expect_success 'diff attribute should apply only to diff' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'diff attribute and --no-ext-diff' '
|
||||
|
||||
git diff --no-ext-diff |
|
||||
grep "^diff --git a/file b/file"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'diff attribute' '
|
||||
|
||||
git config --unset diff.parrot.command &&
|
||||
@@ -94,6 +108,13 @@ test_expect_success 'diff attribute should apply only to diff' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'diff attribute and --no-ext-diff' '
|
||||
|
||||
git diff --no-ext-diff |
|
||||
grep "^diff --git a/file b/file"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'no diff with -diff' '
|
||||
echo >.gitattributes "file -diff" &&
|
||||
git diff | grep Binary
|
||||
|
||||
@@ -21,7 +21,7 @@ EOF
|
||||
|
||||
cat >hexdump <<'EOF'
|
||||
#!/bin/sh
|
||||
perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' "$1"
|
||||
perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
|
||||
EOF
|
||||
chmod +x hexdump
|
||||
|
||||
|
||||
60
t/t5521-pull-options.sh
Executable file
60
t/t5521-pull-options.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='pull options'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
D=`pwd`
|
||||
|
||||
test_expect_success 'setup' '
|
||||
mkdir parent &&
|
||||
(cd parent && git init &&
|
||||
echo one >file && git add file &&
|
||||
git commit -m one)
|
||||
'
|
||||
|
||||
cd "$D"
|
||||
|
||||
test_expect_success 'git pull -q' '
|
||||
mkdir clonedq &&
|
||||
cd clonedq &&
|
||||
git pull -q "$D/parent" >out 2>err &&
|
||||
test ! -s out
|
||||
'
|
||||
|
||||
cd "$D"
|
||||
|
||||
test_expect_success 'git pull' '
|
||||
mkdir cloned &&
|
||||
cd cloned &&
|
||||
git pull "$D/parent" >out 2>err &&
|
||||
test -s out
|
||||
'
|
||||
cd "$D"
|
||||
|
||||
test_expect_success 'git pull -v' '
|
||||
mkdir clonedv &&
|
||||
cd clonedv &&
|
||||
git pull -v "$D/parent" >out 2>err &&
|
||||
test -s out
|
||||
'
|
||||
|
||||
cd "$D"
|
||||
|
||||
test_expect_success 'git pull -v -q' '
|
||||
mkdir clonedvq &&
|
||||
cd clonedvq &&
|
||||
git pull -v -q "$D/parent" >out 2>err &&
|
||||
test ! -s out
|
||||
'
|
||||
|
||||
cd "$D"
|
||||
|
||||
test_expect_success 'git pull -q -v' '
|
||||
mkdir clonedqv &&
|
||||
cd clonedqv &&
|
||||
git pull -q -v "$D/parent" >out 2>err &&
|
||||
test -s out
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -292,4 +292,12 @@ test_expect_success '--compose adds MIME for utf8 subject' '
|
||||
grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
|
||||
'
|
||||
|
||||
test_expect_success 'detects ambiguous reference/file conflict' '
|
||||
echo master > master &&
|
||||
git add master &&
|
||||
git commit -m"add master" &&
|
||||
test_must_fail git send-email --dry-run master 2>errors &&
|
||||
grep disambiguate errors
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -16,7 +16,7 @@ compare_git_head_with () {
|
||||
|
||||
compare_svn_head_with () {
|
||||
LC_ALL=en_US.UTF-8 svn log --limit 1 `git svn info --url` | \
|
||||
sed -e 1,3d -e "/^-\+\$/d" >current &&
|
||||
sed -e 1,3d -e "/^-\{1,\}\$/d" >current &&
|
||||
test_cmp current "$1"
|
||||
}
|
||||
|
||||
|
||||
@@ -231,4 +231,12 @@ test_expect_success 'fast-export -C -C | fast-import' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'fast-export | fast-import when master is tagged' '
|
||||
|
||||
git tag -m msg last &&
|
||||
git fast-export -C -C --signed-tags=strip --all > output &&
|
||||
test $(grep -c "^tag " output) = 3
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -254,16 +254,6 @@ static long ff_regexp(const char *line, long len,
|
||||
|
||||
line_buffer = xstrndup(line, len); /* make NUL terminated */
|
||||
|
||||
/* Exclude terminating newline (and cr) from matching */
|
||||
if (len > 0 && line[len-1] == '\n') {
|
||||
if (len > 1 && line[len-2] == '\r')
|
||||
len -= 2;
|
||||
else
|
||||
len--;
|
||||
}
|
||||
|
||||
line_buffer = xstrndup(line, len); /* make NUL terminated */
|
||||
|
||||
for (i = 0; i < regs->nr; i++) {
|
||||
struct ff_reg *reg = regs->array + i;
|
||||
if (!regexec(®->re, line_buffer, 2, pmatch, 0)) {
|
||||
@@ -338,4 +328,3 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
|
||||
}
|
||||
return git_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user