mirror of
https://github.com/git/git.git
synced 2026-04-02 04:50:12 +02:00
Merge commit 'v1.6.3-rc0' into 4msysgit-devel
Conflicts: t/t7501-commit.sh t/t9001-send-email.sh t/t9200-git-cvsexportcommit.sh t/t9500-gitweb-standalone-no-errors.sh
This commit is contained in:
@@ -103,6 +103,10 @@ ifdef DOCBOOK_SUPPRESS_SP
|
||||
XMLTO_EXTRA += -m manpage-suppress-sp.xsl
|
||||
endif
|
||||
|
||||
SHELL_PATH ?= $(SHELL)
|
||||
# Shell quote;
|
||||
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
|
||||
|
||||
#
|
||||
# Please note that there is a minor bug in asciidoc.
|
||||
# The version after 6.0.3 _will_ include the patch found here:
|
||||
@@ -178,7 +182,7 @@ install-pdf: pdf
|
||||
$(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
|
||||
|
||||
install-html: html
|
||||
sh ./install-webdoc.sh $(DESTDIR)$(htmldir)
|
||||
'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
|
||||
|
||||
../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
||||
$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
|
||||
@@ -240,7 +244,7 @@ user-manual.xml: user-manual.txt user-manual.conf
|
||||
|
||||
technical/api-index.txt: technical/api-index-skel.txt \
|
||||
technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
|
||||
$(QUIET_GEN)cd technical && sh ./api-index.sh
|
||||
$(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
|
||||
|
||||
$(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt
|
||||
$(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
|
||||
@@ -285,7 +289,7 @@ $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
|
||||
|
||||
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
|
||||
$(QUIET_GEN)$(RM) $@+ $@ && \
|
||||
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
|
||||
'$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
|
||||
@@ -299,14 +303,14 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
|
||||
mv $@+ $@
|
||||
|
||||
install-webdoc : html
|
||||
sh ./install-webdoc.sh $(WEBDOC_DEST)
|
||||
'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
|
||||
|
||||
quick-install: quick-install-man
|
||||
|
||||
quick-install-man:
|
||||
sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
|
||||
'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
|
||||
|
||||
quick-install-html:
|
||||
sh ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
|
||||
'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
|
||||
|
||||
.PHONY: .FORCE-GIT-VERSION-FILE
|
||||
|
||||
22
Documentation/RelNotes-1.6.2.3.txt
Normal file
22
Documentation/RelNotes-1.6.2.3.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
GIT v1.6.2.3 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.6.2.2
|
||||
--------------------
|
||||
|
||||
* Setting an octal mode value to core.sharedrepository configuration to
|
||||
restrict access to the repository to group members did not work as
|
||||
advertised.
|
||||
|
||||
* A fairly large and trivial memory leak while rev-list shows list of
|
||||
reachable objects has been identified and plugged.
|
||||
|
||||
* "git-commit --interactive" did not abort when underlying "git-add -i"
|
||||
signaled a failure.
|
||||
|
||||
* git-repack (invoked from git-gc) did not work as nicely as it should in
|
||||
a repository that borrows objects from neighbours via alternates
|
||||
mechanism especially when some packs are marked with the ".keep" flag
|
||||
to prevent them from being repacked.
|
||||
|
||||
Many small documentation updates are included as well.
|
||||
@@ -35,11 +35,16 @@ Updates since v1.6.2
|
||||
|
||||
(subsystems)
|
||||
|
||||
* various git-svn updates.
|
||||
|
||||
(performance)
|
||||
|
||||
* many uses of lstat(2) in the codepath for "git checkout" have been
|
||||
optimized out.
|
||||
|
||||
* pruning reflog entries that are unreachable from the tip of the ref
|
||||
during "git reflog prune" (hence "git gc") was very inefficient.
|
||||
|
||||
(usability, bells and whistles)
|
||||
|
||||
* rsync:/path/to/repo can be used to run git over rsync for local
|
||||
@@ -56,7 +61,7 @@ Updates since v1.6.2
|
||||
spelled as "--format=<style>". In addition, --format=%formatstring
|
||||
is a short-hand for --pretty=tformat:%formatstring.
|
||||
|
||||
* "--oneline" is a synonym for "--pretty=oneline --abbrev=commit".
|
||||
* "--oneline" is a synonym for "--pretty=oneline --abbrev-commit".
|
||||
|
||||
* If you realize that you botched the patch when you are editing hunks
|
||||
with the 'edit' action in git-add -i/-p, you can abort the editor to
|
||||
@@ -66,6 +71,10 @@ Updates since v1.6.2
|
||||
messages given by "git checkout" and "git status" used to count merge
|
||||
commits; now it doesn't.
|
||||
|
||||
* @{-1} is a new way to refer to the last branch you were on introduced in
|
||||
1.6.2, but the initial implementation did not teach this to a few
|
||||
commands. Now the syntax works with "branch -m @{-1} newname".
|
||||
|
||||
* git-archive learned --output=<file> option.
|
||||
|
||||
* git-bisect shows not just the number of remaining commits whose goodness
|
||||
@@ -73,9 +82,12 @@ Updates since v1.6.2
|
||||
|
||||
* You can give --date=<format> option to git-blame.
|
||||
|
||||
* git-branch -r shows HEAD symref that points at a remote branch in
|
||||
* "git-branch -r" shows HEAD symref that points at a remote branch in
|
||||
interest of each tracked remote repository.
|
||||
|
||||
* "git-branch -v -v" is a new way to get list of names for branches and the
|
||||
"upstream" branch for them.
|
||||
|
||||
* git-config learned -e option to open an editor to edit the config file
|
||||
directly.
|
||||
|
||||
@@ -83,15 +95,23 @@ Updates since v1.6.2
|
||||
|
||||
* git-fast-export choked when seeing a tag that does not point at commit.
|
||||
|
||||
* git-for-each-ref learned a new "upstream" token.
|
||||
|
||||
* git-format-patch can be told to use attachment with a new configuration,
|
||||
format.attach.
|
||||
|
||||
* git-format-patch can be told to produce deep or shallow message threads.
|
||||
|
||||
* git-format-patch can be told to always add sign-off with a configuration
|
||||
variable.
|
||||
|
||||
* git-format-patch learned format.headers configuration to add extra
|
||||
header fields to the output. This behaviour is similar to the existing
|
||||
--add-header=<header> option of the command.
|
||||
|
||||
* git-format-patch gives human readable names to the attached files, when
|
||||
told to send patches as attachments.
|
||||
|
||||
* git-grep learned to highlight the found substrings in color.
|
||||
|
||||
* git-imap-send learned to work around Thunderbird's inability to easily
|
||||
@@ -105,10 +125,8 @@ Updates since v1.6.2
|
||||
|
||||
* Output from git-remote command has been vastly improved.
|
||||
|
||||
* git-repack (invoked from git-gc) did not work as nicely as it should in
|
||||
a repository that borrows objects from neighbours via alternates
|
||||
mechanism especially when some packs are marked with the ".keep" flag
|
||||
to prevent them from being repacked.
|
||||
* "git remote update --prune $remote" updates from the named remote and
|
||||
then prunes stale tracking branches.
|
||||
|
||||
* git-send-email learned --confirm option to review the Cc: list before
|
||||
sending the messages out.
|
||||
@@ -141,11 +159,23 @@ v1.6.2.X series.
|
||||
* The initial checkout did not read the attributes from the .gitattribute
|
||||
file that is being checked out.
|
||||
|
||||
* "git-checkout <tree-ish> <submodule>" did not update the index entry at
|
||||
the named path; it now does.
|
||||
|
||||
* git-gc spent excessive amount of time to decide if an object appears
|
||||
in a locally existing pack (if needed, backport by merging 69e020a).
|
||||
|
||||
* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
|
||||
deciding to descend into a subdirectory but they did not match the
|
||||
individual paths correctly. This caused pathspecs "abc/d ab" to match
|
||||
"abc/0" ("abc/d" made them decide to descend into the directory "abc/",
|
||||
and then "ab" incorrectly matched "abc/0" when it shouldn't).
|
||||
|
||||
* "git-merge-recursive" was broken when a submodule entry was involved in
|
||||
a criss-cross merge situation.
|
||||
|
||||
---
|
||||
exec >/var/tmp/1
|
||||
O=v1.6.2.2-403-g8130949
|
||||
O=v1.6.2.3-497-g54a4749
|
||||
echo O=$(git describe master)
|
||||
git shortlog --no-merges $O..master ^maint
|
||||
|
||||
@@ -480,7 +480,7 @@ branch.<name>.remote::
|
||||
branch.<name>.merge::
|
||||
Defines, together with branch.<name>.remote, the upstream branch
|
||||
for the given branch. It tells 'git-fetch'/'git-pull' which
|
||||
branch to merge from.
|
||||
branch to merge and can also affect 'git-push' (see push.default).
|
||||
When in branch <name>, it tells 'git-fetch' the default
|
||||
refspec to be marked for merging in FETCH_HEAD. The value is
|
||||
handled like the remote part of a refspec, and must match a
|
||||
@@ -715,6 +715,13 @@ format.thread::
|
||||
A true boolean value is the same as `shallow`, and a false
|
||||
value disables threading.
|
||||
|
||||
format.signoff::
|
||||
A boolean value which lets you enable the `-s/--signoff` option of
|
||||
format-patch by default. *Note:* Adding the Signed-off-by: line to a
|
||||
patch should be a conscious act and means that you certify you have
|
||||
the rights to submit this work under the same open source license.
|
||||
Please see the 'SubmittingPatches' document for further discussion.
|
||||
|
||||
gc.aggressiveWindow::
|
||||
The window size parameter used in the delta compression
|
||||
algorithm used by 'git-gc --aggressive'. This defaults
|
||||
@@ -1208,7 +1215,7 @@ push.default::
|
||||
* `matching` push all matching branches.
|
||||
All branches having the same name in both ends are considered to be
|
||||
matching. This is the default.
|
||||
* `tracking` push the current branch to the branch it is tracking.
|
||||
* `tracking` push the current branch to its upstream branch.
|
||||
* `current` push the current branch to a branch of the same name.
|
||||
|
||||
rebase.stat::
|
||||
|
||||
@@ -16,6 +16,7 @@ body blockquote {
|
||||
html body {
|
||||
margin: 1em 5% 1em 5%;
|
||||
line-height: 1.2;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
body div {
|
||||
@@ -128,6 +129,15 @@ body pre {
|
||||
|
||||
tt.literal, code.literal {
|
||||
color: navy;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
code.literal:before { content: "'"; }
|
||||
code.literal:after { content: "'"; }
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
color: #064;
|
||||
}
|
||||
|
||||
div.literallayout p {
|
||||
@@ -137,7 +147,6 @@ div.literallayout p {
|
||||
|
||||
div.literallayout {
|
||||
font-family: monospace;
|
||||
# margin: 0.5em 10% 0.5em 1em;
|
||||
margin: 0em;
|
||||
color: navy;
|
||||
border: 1px solid silver;
|
||||
@@ -187,7 +196,8 @@ dt {
|
||||
}
|
||||
|
||||
dt span.term {
|
||||
font-style: italic;
|
||||
font-style: normal;
|
||||
color: navy;
|
||||
}
|
||||
|
||||
div.variablelist dd p {
|
||||
|
||||
@@ -100,7 +100,9 @@ OPTIONS
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Show sha1 and commit subject line for each head.
|
||||
Show sha1 and commit subject line for each head, along with
|
||||
relationship to upstream branch (if any). If given twice, print
|
||||
the name of the upstream branch, as well.
|
||||
|
||||
--abbrev=<length>::
|
||||
Alter the sha1's minimum display length in the output listing.
|
||||
|
||||
@@ -7,7 +7,9 @@ git-check-ref-format - Ensures that a reference name is well formed
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git check-ref-format' <refname>
|
||||
'git check-ref-format' [--branch] <branchname-shorthand>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -30,7 +32,11 @@ imposes the following rules on how references are named:
|
||||
caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
|
||||
or open bracket `[` anywhere.
|
||||
|
||||
. They cannot end with a slash `/`.
|
||||
. They cannot end with a slash `/` nor a dot `.`.
|
||||
|
||||
. They cannot end with the sequence `.lock`.
|
||||
|
||||
. They cannot contain a sequence `@{`.
|
||||
|
||||
These rules make it easy for shell script based tools to parse
|
||||
reference names, pathname expansion by the shell when a reference name is used
|
||||
@@ -49,6 +55,18 @@ reference name expressions (see linkgit:git-rev-parse[1]):
|
||||
It may also be used to select a specific object such as with
|
||||
'git-cat-file': "git cat-file blob v1.3.3:refs.c".
|
||||
|
||||
. at-open-brace `@{` is used as a notation to access a reflog entry.
|
||||
|
||||
With the `--branch` option, it expands a branch name shorthand and
|
||||
prints the name of the branch the shorthand refers to.
|
||||
|
||||
EXAMPLE
|
||||
-------
|
||||
|
||||
git check-ref-format --branch @{-1}::
|
||||
|
||||
Print the name of the previous branch.
|
||||
|
||||
|
||||
GIT
|
||||
---
|
||||
|
||||
@@ -126,9 +126,13 @@ the conflicted merge in the specified paths.
|
||||
<new_branch>::
|
||||
Name for the new branch.
|
||||
|
||||
<tree-ish>::
|
||||
Tree to checkout from (when paths are given). If not specified,
|
||||
the index will be used.
|
||||
|
||||
<branch>::
|
||||
Branch to checkout; may be any object ID that resolves to a
|
||||
commit. Defaults to HEAD.
|
||||
Branch to checkout (when no paths are given); may be any object
|
||||
ID that resolves to a commit. Defaults to HEAD.
|
||||
+
|
||||
When this parameter names a non-branch (but still a valid commit object),
|
||||
your HEAD becomes 'detached'.
|
||||
@@ -191,7 +195,7 @@ $ git checkout hello.c <3>
|
||||
+
|
||||
<1> switch branch
|
||||
<2> take a file out of another commit
|
||||
<3> restore hello.c from HEAD of current branch
|
||||
<3> restore hello.c from the index
|
||||
+
|
||||
If you have an unfortunate branch that is named `hello.c`, this
|
||||
step would be confused as an instruction to switch to that branch.
|
||||
|
||||
@@ -31,6 +31,9 @@ changes, which would normally have no effect. Nevertheless, this may be
|
||||
useful in the future for compensating for some git bugs or such,
|
||||
therefore such a usage is permitted.
|
||||
|
||||
*NOTE*: This command honors `.git/info/grafts`. If you have any grafts
|
||||
defined, running this command will make them permanent.
|
||||
|
||||
*WARNING*! The rewritten history will have different object names for all
|
||||
the objects and will not converge with the original branch. You will not
|
||||
be able to easily push and distribute the rewritten branch on top of the
|
||||
|
||||
@@ -85,6 +85,11 @@ objectsize::
|
||||
objectname::
|
||||
The object name (aka SHA-1).
|
||||
|
||||
upstream::
|
||||
The name of a local ref which can be considered ``upstream''
|
||||
from the displayed ref. Respects `:short` in the same way as
|
||||
`refname` above.
|
||||
|
||||
In addition to the above, for commit and tag objects, the header
|
||||
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
|
||||
be used to specify the value in the header field.
|
||||
|
||||
@@ -205,6 +205,7 @@ more than one.
|
||||
numbered = auto
|
||||
cc = <email>
|
||||
attach [ = mime-boundary-string ]
|
||||
signoff = true
|
||||
------------
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ problem by stashing the refs in a single file,
|
||||
traditional `$GIT_DIR/refs` hierarchy, it is looked up in this
|
||||
file and used if found.
|
||||
|
||||
Subsequent updates to branches always creates new file under
|
||||
Subsequent updates to branches always create new files under
|
||||
`$GIT_DIR/refs` hierarchy.
|
||||
|
||||
A recommended practice to deal with a repository with too many
|
||||
@@ -35,7 +35,7 @@ occasionally run `git pack-refs \--prune`. Tags are by
|
||||
definition stationary and are not expected to change. Branch
|
||||
heads will be packed with the initial `pack-refs --all`, but
|
||||
only the currently active branch heads will become unpacked,
|
||||
and next `pack-refs` (without `--all`) will leave them
|
||||
and the next `pack-refs` (without `--all`) will leave them
|
||||
unpacked.
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ SYNOPSIS
|
||||
'git remote set-head' <name> [-a | -d | <branch>]
|
||||
'git remote show' [-n] <name>
|
||||
'git remote prune' [-n | --dry-run] <name>
|
||||
'git remote update' [group]
|
||||
'git remote update' [-p | --prune] [group | remote]...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -125,6 +125,8 @@ the configuration parameter remotes.default will get used; if
|
||||
remotes.default is not defined, all remotes which do not have the
|
||||
configuration parameter remote.<name>.skipDefaultUpdate set to true will
|
||||
be updated. (See linkgit:git-config[1]).
|
||||
+
|
||||
With `--prune` option, prune all the remotes that are updated.
|
||||
|
||||
|
||||
DISCUSSION
|
||||
|
||||
@@ -39,13 +39,13 @@ OPTIONS
|
||||
Composing
|
||||
~~~~~~~~~
|
||||
|
||||
--bcc::
|
||||
--bcc=<address>::
|
||||
Specify a "Bcc:" value for each email. Default is the value of
|
||||
'sendemail.bcc'.
|
||||
+
|
||||
The --bcc option must be repeated for each user you want on the bcc list.
|
||||
|
||||
--cc::
|
||||
--cc=<address>::
|
||||
Specify a starting "Cc:" value for each email.
|
||||
Default is the value of 'sendemail.cc'.
|
||||
+
|
||||
@@ -68,24 +68,24 @@ and In-Reply-To headers will be used unless they are removed.
|
||||
+
|
||||
Missing From or In-Reply-To headers will be prompted for.
|
||||
|
||||
--from::
|
||||
--from=<address>::
|
||||
Specify the sender of the emails. This will default to
|
||||
the value GIT_COMMITTER_IDENT, as returned by "git var -l".
|
||||
The user will still be prompted to confirm this entry.
|
||||
|
||||
--in-reply-to::
|
||||
--in-reply-to=<identifier>::
|
||||
Specify the contents of the first In-Reply-To header.
|
||||
Subsequent emails will refer to the previous email
|
||||
instead of this if --chain-reply-to is set (the default)
|
||||
Only necessary if --compose is also set. If --compose
|
||||
is not set, this will be prompted for.
|
||||
|
||||
--subject::
|
||||
--subject=<string>::
|
||||
Specify the initial subject of the email thread.
|
||||
Only necessary if --compose is also set. If --compose
|
||||
is not set, this will be prompted for.
|
||||
|
||||
--to::
|
||||
--to=<address>::
|
||||
Specify the primary recipient of the emails generated. Generally, this
|
||||
will be the upstream maintainer of the project involved. Default is the
|
||||
value of the 'sendemail.to' configuration value; if that is unspecified,
|
||||
@@ -97,7 +97,7 @@ The --to option must be repeated for each user you want on the to list.
|
||||
Sending
|
||||
~~~~~~~
|
||||
|
||||
--envelope-sender::
|
||||
--envelope-sender=<address>::
|
||||
Specify the envelope sender used to send the emails.
|
||||
This is useful if your default address is not the address that is
|
||||
subscribed to a list. If you use the sendmail binary, you must have
|
||||
@@ -105,12 +105,12 @@ Sending
|
||||
the 'sendemail.envelopesender' configuration variable; if that is
|
||||
unspecified, choosing the envelope sender is left to your MTA.
|
||||
|
||||
--smtp-encryption::
|
||||
--smtp-encryption=<encryption>::
|
||||
Specify the encryption to use, either 'ssl' or 'tls'. Any other
|
||||
value reverts to plain SMTP. Default is the value of
|
||||
'sendemail.smtpencryption'.
|
||||
|
||||
--smtp-pass::
|
||||
--smtp-pass[=<password>]::
|
||||
Password for SMTP-AUTH. The argument is optional: If no
|
||||
argument is specified, then the empty string is used as
|
||||
the password. Default is the value of 'sendemail.smtppass',
|
||||
@@ -122,7 +122,7 @@ or on the command line. If a username has been specified (with
|
||||
specified (with '--smtp-pass' or 'sendemail.smtppass'), then the
|
||||
user is prompted for a password while the input is masked for privacy.
|
||||
|
||||
--smtp-server::
|
||||
--smtp-server=<host>::
|
||||
If set, specifies the outgoing SMTP server to use (e.g.
|
||||
`smtp.example.com` or a raw IP address). Alternatively it can
|
||||
specify a full pathname of a sendmail-like program instead;
|
||||
@@ -132,7 +132,7 @@ user is prompted for a password while the input is masked for privacy.
|
||||
`/usr/lib/sendmail` if such program is available, or
|
||||
`localhost` otherwise.
|
||||
|
||||
--smtp-server-port::
|
||||
--smtp-server-port=<port>::
|
||||
Specifies a port different from the default port (SMTP
|
||||
servers typically listen to smtp port 25 and ssmtp port
|
||||
465). This can be set with 'sendemail.smtpserverport'.
|
||||
@@ -140,7 +140,7 @@ user is prompted for a password while the input is masked for privacy.
|
||||
--smtp-ssl::
|
||||
Legacy alias for '--smtp-encryption ssl'.
|
||||
|
||||
--smtp-user::
|
||||
--smtp-user=<user>::
|
||||
Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser';
|
||||
if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
|
||||
then authentication is not attempted.
|
||||
@@ -149,13 +149,13 @@ user is prompted for a password while the input is masked for privacy.
|
||||
Automating
|
||||
~~~~~~~~~~
|
||||
|
||||
--cc-cmd::
|
||||
--cc-cmd=<command>::
|
||||
Specify a command to execute once per patch file which
|
||||
should generate patch file specific "Cc:" entries.
|
||||
Output of this command must be single email address per line.
|
||||
Default is the value of 'sendemail.cccmd' configuration value.
|
||||
|
||||
--[no-]chain-reply-to::
|
||||
--[no-]chain-reply-to=<identifier>::
|
||||
If this is set, each email will be sent as a reply to the previous
|
||||
email sent. If disabled with "--no-chain-reply-to", all emails after
|
||||
the first will be sent as replies to the first email sent. When using
|
||||
@@ -163,7 +163,7 @@ Automating
|
||||
entire patch series. Default is the value of the 'sendemail.chainreplyto'
|
||||
configuration value; if that is unspecified, default to --chain-reply-to.
|
||||
|
||||
--identity::
|
||||
--identity=<identity>::
|
||||
A configuration identity. When given, causes values in the
|
||||
'sendemail.<identity>' subsection to take precedence over
|
||||
values in the 'sendemail' section. The default identity is
|
||||
@@ -174,7 +174,7 @@ Automating
|
||||
cc list. Default is the value of 'sendemail.signedoffbycc' configuration
|
||||
value; if that is unspecified, default to --signed-off-by-cc.
|
||||
|
||||
--suppress-cc::
|
||||
--suppress-cc=<category>::
|
||||
Specify an additional category of recipients to suppress the
|
||||
auto-cc of:
|
||||
+
|
||||
@@ -211,7 +211,7 @@ specified, as well as 'body' if --no-signed-off-cc is specified.
|
||||
Administering
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
--confirm::
|
||||
--confirm=<mode>::
|
||||
Confirm just before sending:
|
||||
+
|
||||
--
|
||||
|
||||
@@ -85,6 +85,10 @@ COMMANDS
|
||||
specified, the prefix must include a trailing slash.
|
||||
Setting a prefix is useful if you wish to track multiple
|
||||
projects that share a common repository.
|
||||
--ignore-paths=<regex>;;
|
||||
When passed to 'init' or 'clone' this regular expression will
|
||||
be preserved as a config key. See 'fetch' for a description
|
||||
of '--ignore-paths'.
|
||||
|
||||
'fetch'::
|
||||
Fetch unfetched revisions from the Subversion remote we are
|
||||
@@ -97,6 +101,9 @@ COMMANDS
|
||||
makes 'git-log' (even without --date=local) show the same times
|
||||
that `svn log` would in the local timezone.
|
||||
|
||||
--parent;;
|
||||
Fetch only from the SVN parent of the current HEAD.
|
||||
|
||||
This doesn't interfere with interoperating with the Subversion
|
||||
repository you cloned from, but if you wish for your local Git
|
||||
repository to be able to interoperate with someone else's local Git
|
||||
@@ -104,17 +111,25 @@ repository, either don't use this option or you should both use it in
|
||||
the same local timezone.
|
||||
|
||||
--ignore-paths=<regex>;;
|
||||
This allows one to specify Perl regular expression that will
|
||||
This allows one to specify a Perl regular expression that will
|
||||
cause skipping of all matching paths from checkout from SVN.
|
||||
Examples:
|
||||
The '--ignore-paths' option should match for every 'fetch'
|
||||
(including automatic fetches due to 'clone', 'dcommit',
|
||||
'rebase', etc) on a given repository.
|
||||
|
||||
--ignore-paths="^doc" - skip "doc*" directory for every fetch.
|
||||
config key: svn-remote.<name>.ignore-paths
|
||||
|
||||
--ignore-paths="^[^/]+/(?:branches|tags)" - skip "branches"
|
||||
and "tags" of first level directories.
|
||||
If the ignore-paths config key is set and the command
|
||||
line option is also given, both regular expressions
|
||||
will be used.
|
||||
|
||||
Regular expression is not persistent, you should specify
|
||||
it every time when fetching.
|
||||
Examples:
|
||||
|
||||
--ignore-paths="^doc" - skip "doc*" directory for every
|
||||
fetch.
|
||||
|
||||
--ignore-paths="^[^/]+/(?:branches|tags)" - skip
|
||||
"branches" and "tags" of first level directories.
|
||||
|
||||
'clone'::
|
||||
Runs 'init' and 'fetch'. It will automatically create a
|
||||
|
||||
@@ -9,7 +9,7 @@ git - the stupid content tracker
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git' [--version] [--exec-path[=GIT_EXEC_PATH]]
|
||||
'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
|
||||
[-p|--paginate|--no-pager]
|
||||
[--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
|
||||
[--help] COMMAND [ARGS]
|
||||
@@ -43,9 +43,11 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.6.2.1/git.html[documentation for release 1.6.2.1]
|
||||
* link:v1.6.2.3/git.html[documentation for release 1.6.2.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.6.2.3.txt[1.6.2.3],
|
||||
link:RelNotes-1.6.2.2.txt[1.6.2.2],
|
||||
link:RelNotes-1.6.2.1.txt[1.6.2.1],
|
||||
link:RelNotes-1.6.2.txt[1.6.2].
|
||||
|
||||
@@ -178,6 +180,10 @@ help ...`.
|
||||
environment variable. If no path is given, 'git' will print
|
||||
the current setting and then exit.
|
||||
|
||||
--html-path::
|
||||
Print the path to wherever your git HTML documentation is installed
|
||||
and exit.
|
||||
|
||||
-p::
|
||||
--paginate::
|
||||
Pipe all output into 'less' (or if set, $PAGER).
|
||||
|
||||
@@ -60,9 +60,9 @@ same as in `.gitignore` files; see linkgit:gitignore[5].
|
||||
When deciding what attributes are assigned to a path, git
|
||||
consults `$GIT_DIR/info/attributes` file (which has the highest
|
||||
precedence), `.gitattributes` file in the same directory as the
|
||||
path in question, and its parent directories (the further the
|
||||
directory that contains `.gitattributes` is from the path in
|
||||
question, the lower its precedence).
|
||||
path in question, and its parent directories up to the toplevel of the
|
||||
work tree (the further the directory that contains `.gitattributes`
|
||||
is from the path in question, the lower its precedence).
|
||||
|
||||
If you wish to affect only a single repository (i.e., to assign
|
||||
attributes to files that are particular to one user's workflow), then
|
||||
|
||||
@@ -31,8 +31,8 @@ precedence, the last matching pattern decides the outcome):
|
||||
|
||||
* Patterns read from a `.gitignore` file in the same directory
|
||||
as the path, or in any parent directory, with patterns in the
|
||||
higher level files (up to the root) being overridden by those in
|
||||
lower level files down to the directory containing the file.
|
||||
higher level files (up to the toplevel of the work tree) being overridden
|
||||
by those in lower level files down to the directory containing the file.
|
||||
These patterns match relative to the location of the
|
||||
`.gitignore` file. A project normally includes such
|
||||
`.gitignore` files in its repository, containing patterns for
|
||||
|
||||
@@ -449,6 +449,12 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
An <<def_object,object>> which is not <<def_reachable,reachable>> from a
|
||||
<<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
|
||||
|
||||
[[def_upstream_branch]]upstream branch::
|
||||
The default <<def_branch,branch>> that is merged into the branch in
|
||||
question (or the branch in question is rebased onto). It is configured
|
||||
via branch.<name>.remote and branch.<name>.merge. If the upstream branch
|
||||
of 'A' is 'origin/B' sometimes we say "'A' is tracking 'origin/B'".
|
||||
|
||||
[[def_working_tree]]working tree::
|
||||
The tree of actual checked out files. The working tree is
|
||||
normally equal to the <<def_HEAD,HEAD>> plus any local changes
|
||||
|
||||
@@ -121,6 +121,7 @@ The placeholders are:
|
||||
- '%d': ref names, like the --decorate option of linkgit:git-log[1]
|
||||
- '%e': encoding
|
||||
- '%s': subject
|
||||
- '%f': sanitized subject line, suitable for a filename
|
||||
- '%b': body
|
||||
- '%Cred': switch color to red
|
||||
- '%Cgreen': switch color to green
|
||||
|
||||
@@ -140,38 +140,38 @@ limiting may be applied.
|
||||
--
|
||||
|
||||
-n 'number'::
|
||||
--max-count='number'::
|
||||
--max-count=<number>::
|
||||
|
||||
Limit the number of commits output.
|
||||
|
||||
--skip='number'::
|
||||
--skip=<number>::
|
||||
|
||||
Skip 'number' commits before starting to show the commit output.
|
||||
|
||||
--since='date'::
|
||||
--after='date'::
|
||||
--since=<date>::
|
||||
--after=<date>::
|
||||
|
||||
Show commits more recent than a specific date.
|
||||
|
||||
--until='date'::
|
||||
--before='date'::
|
||||
--until=<date>::
|
||||
--before=<date>::
|
||||
|
||||
Show commits older than a specific date.
|
||||
|
||||
ifdef::git-rev-list[]
|
||||
--max-age='timestamp'::
|
||||
--min-age='timestamp'::
|
||||
--max-age=<timestamp>::
|
||||
--min-age=<timestamp>::
|
||||
|
||||
Limit the commits output to specified time range.
|
||||
endif::git-rev-list[]
|
||||
|
||||
--author='pattern'::
|
||||
--committer='pattern'::
|
||||
--author=<pattern>::
|
||||
--committer=<pattern>::
|
||||
|
||||
Limit the commits output to ones with author/committer
|
||||
header lines that match the specified pattern (regular expression).
|
||||
|
||||
--grep='pattern'::
|
||||
--grep=<pattern>::
|
||||
|
||||
Limit the commits output to ones with log message that
|
||||
matches the specified pattern (regular expression).
|
||||
|
||||
@@ -188,7 +188,7 @@ As you can see, a commit shows who made the latest change, what they
|
||||
did, and why.
|
||||
|
||||
Every commit has a 40-hexdigit id, sometimes called the "object name" or the
|
||||
"SHA1 id", shown on the first line of the "git-show" output. You can usually
|
||||
"SHA-1 id", shown on the first line of the "git show" output. You can usually
|
||||
refer to a commit by a shorter name, such as a tag or a branch name, but this
|
||||
longer name can also be useful. Most importantly, it is a globally unique
|
||||
name for this commit: so if you tell somebody else the object name (for
|
||||
@@ -307,7 +307,7 @@ ref: refs/heads/master
|
||||
Examining an old version without creating a new branch
|
||||
------------------------------------------------------
|
||||
|
||||
The git-checkout command normally expects a branch head, but will also
|
||||
The `git checkout` command normally expects a branch head, but will also
|
||||
accept an arbitrary commit; for example, you can check out the commit
|
||||
referenced by a tag:
|
||||
|
||||
@@ -320,7 +320,7 @@ If you want to create a new branch from this checkout, you may do so
|
||||
HEAD is now at 427abfa... Linux v2.6.17
|
||||
------------------------------------------------
|
||||
|
||||
The HEAD then refers to the SHA1 of the commit instead of to a branch,
|
||||
The HEAD then refers to the SHA-1 of the commit instead of to a branch,
|
||||
and git branch shows that you are no longer on a branch:
|
||||
|
||||
------------------------------------------------
|
||||
@@ -400,7 +400,7 @@ references with the same shorthand name, see the "SPECIFYING
|
||||
REVISIONS" section of linkgit:git-rev-parse[1].
|
||||
|
||||
[[Updating-a-repository-With-git-fetch]]
|
||||
Updating a repository with git-fetch
|
||||
Updating a repository with git fetch
|
||||
------------------------------------
|
||||
|
||||
Eventually the developer cloned from will do additional work in her
|
||||
@@ -427,7 +427,7 @@ $ git fetch linux-nfs
|
||||
-------------------------------------------------
|
||||
|
||||
New remote-tracking branches will be stored under the shorthand name
|
||||
that you gave "git-remote add", in this case linux-nfs:
|
||||
that you gave "git remote add", in this case linux-nfs:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git branch -r
|
||||
@@ -516,7 +516,7 @@ $ git bisect reset
|
||||
|
||||
to return you to the branch you were on before.
|
||||
|
||||
Note that the version which git-bisect checks out for you at each
|
||||
Note that the version which `git bisect` checks out for you at each
|
||||
point is just a suggestion, and you're free to try a different
|
||||
version if you think it would be a good idea. For example,
|
||||
occasionally you may land on a commit that broke something unrelated;
|
||||
@@ -592,11 +592,11 @@ In addition to HEAD, there are several other special names for
|
||||
commits:
|
||||
|
||||
Merges (to be discussed later), as well as operations such as
|
||||
git-reset, which change the currently checked-out commit, generally
|
||||
`git reset`, which change the currently checked-out commit, generally
|
||||
set ORIG_HEAD to the value HEAD had before the current operation.
|
||||
|
||||
The git-fetch operation always stores the head of the last fetched
|
||||
branch in FETCH_HEAD. For example, if you run git fetch without
|
||||
The `git fetch` operation always stores the head of the last fetched
|
||||
branch in FETCH_HEAD. For example, if you run `git fetch` without
|
||||
specifying a local branch as the target of the operation
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -739,7 +739,7 @@ $ git log --pretty=oneline origin..mybranch | wc -l
|
||||
-------------------------------------------------
|
||||
|
||||
Alternatively, you may often see this sort of thing done with the
|
||||
lower-level command linkgit:git-rev-list[1], which just lists the SHA1's
|
||||
lower-level command linkgit:git-rev-list[1], which just lists the SHA-1's
|
||||
of all the given commits:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -1073,9 +1073,9 @@ $ git diff
|
||||
|
||||
shows the difference between the working tree and the index file.
|
||||
|
||||
Note that "git-add" always adds just the current contents of a file
|
||||
Note that "git add" always adds just the current contents of a file
|
||||
to the index; further changes to the same file will be ignored unless
|
||||
you run git-add on the file again.
|
||||
you run `git add` on the file again.
|
||||
|
||||
When you're ready, just run
|
||||
|
||||
@@ -1136,7 +1136,7 @@ Ignoring files
|
||||
A project will often generate files that you do 'not' want to track with git.
|
||||
This typically includes files generated by a build process or temporary
|
||||
backup files made by your editor. Of course, 'not' tracking files with git
|
||||
is just a matter of 'not' calling `git-add` on them. But it quickly becomes
|
||||
is just a matter of 'not' calling `git add` on them. But it quickly becomes
|
||||
annoying to have these untracked files lying around; e.g. they make
|
||||
`git add .` practically useless, and they keep showing up in the output of
|
||||
`git status`.
|
||||
@@ -1349,7 +1349,7 @@ $ git add file.txt
|
||||
-------------------------------------------------
|
||||
|
||||
the different stages of that file will be "collapsed", after which
|
||||
git-diff will (by default) no longer show diffs for that file.
|
||||
`git diff` will (by default) no longer show diffs for that file.
|
||||
|
||||
[[undoing-a-merge]]
|
||||
Undoing a merge
|
||||
@@ -1446,7 +1446,7 @@ Fixing a mistake by rewriting history
|
||||
|
||||
If the problematic commit is the most recent commit, and you have not
|
||||
yet made that commit public, then you may just
|
||||
<<undoing-a-merge,destroy it using git-reset>>.
|
||||
<<undoing-a-merge,destroy it using `git reset`>>.
|
||||
|
||||
Alternatively, you
|
||||
can edit the working directory and update the index to fix your
|
||||
@@ -1474,7 +1474,7 @@ Checking out an old version of a file
|
||||
|
||||
In the process of undoing a previous bad change, you may find it
|
||||
useful to check out an older version of a particular file using
|
||||
linkgit:git-checkout[1]. We've used git-checkout before to switch
|
||||
linkgit:git-checkout[1]. We've used `git checkout` before to switch
|
||||
branches, but it has quite different behavior if it is given a path
|
||||
name: the command
|
||||
|
||||
@@ -1542,7 +1542,7 @@ $ git gc
|
||||
-------------------------------------------------
|
||||
|
||||
to recompress the archive. This can be very time-consuming, so
|
||||
you may prefer to run git-gc when you are not doing other work.
|
||||
you may prefer to run `git gc` when you are not doing other work.
|
||||
|
||||
|
||||
[[ensuring-reliability]]
|
||||
@@ -1634,7 +1634,7 @@ In some situations the reflog may not be able to save you. For example,
|
||||
suppose you delete a branch, then realize you need the history it
|
||||
contained. The reflog is also deleted; however, if you have not yet
|
||||
pruned the repository, then you may still be able to find the lost
|
||||
commits in the dangling objects that git-fsck reports. See
|
||||
commits in the dangling objects that `git fsck` reports. See
|
||||
<<dangling-objects>> for the details.
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -1676,7 +1676,7 @@ Sharing development with others
|
||||
===============================
|
||||
|
||||
[[getting-updates-With-git-pull]]
|
||||
Getting updates with git-pull
|
||||
Getting updates with git pull
|
||||
-----------------------------
|
||||
|
||||
After you clone a repository and make a few changes of your own, you
|
||||
@@ -1722,7 +1722,7 @@ repository that you pulled from.
|
||||
<<fast-forwards,fast forward>>; instead, your branch will just be
|
||||
updated to point to the latest commit from the upstream branch.)
|
||||
|
||||
The git-pull command can also be given "." as the "remote" repository,
|
||||
The `git pull` command can also be given "." as the "remote" repository,
|
||||
in which case it just merges in a branch from the current repository; so
|
||||
the commands
|
||||
|
||||
@@ -1795,7 +1795,7 @@ Public git repositories
|
||||
Another way to submit changes to a project is to tell the maintainer
|
||||
of that project to pull the changes from your repository using
|
||||
linkgit:git-pull[1]. In the section "<<getting-updates-With-git-pull,
|
||||
Getting updates with git-pull>>" we described this as a way to get
|
||||
Getting updates with `git pull`>>" we described this as a way to get
|
||||
updates from the "main" repository, but it works just as well in the
|
||||
other direction.
|
||||
|
||||
@@ -1847,7 +1847,7 @@ Setting up a public repository
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Assume your personal repository is in the directory ~/proj. We
|
||||
first create a new clone of the repository and tell git-daemon that it
|
||||
first create a new clone of the repository and tell `git daemon` that it
|
||||
is meant to be public:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -1878,10 +1878,10 @@ repository>>", below.
|
||||
Otherwise, all you need to do is start linkgit:git-daemon[1]; it will
|
||||
listen on port 9418. By default, it will allow access to any directory
|
||||
that looks like a git directory and contains the magic file
|
||||
git-daemon-export-ok. Passing some directory paths as git-daemon
|
||||
git-daemon-export-ok. Passing some directory paths as `git daemon`
|
||||
arguments will further restrict the exports to those paths.
|
||||
|
||||
You can also run git-daemon as an inetd service; see the
|
||||
You can also run `git daemon` as an inetd service; see the
|
||||
linkgit:git-daemon[1] man page for details. (See especially the
|
||||
examples section.)
|
||||
|
||||
@@ -1942,7 +1942,7 @@ or just
|
||||
$ git push ssh://yourserver.com/~you/proj.git master
|
||||
-------------------------------------------------
|
||||
|
||||
As with git-fetch, git-push will complain if this does not result in a
|
||||
As with `git fetch`, `git push` will complain if this does not result in a
|
||||
<<fast-forwards,fast forward>>; see the following section for details on
|
||||
handling this case.
|
||||
|
||||
@@ -1952,7 +1952,7 @@ repository that has a checked-out working tree, but the working tree
|
||||
will not be updated by the push. This may lead to unexpected results if
|
||||
the branch you push to is the currently checked-out branch!
|
||||
|
||||
As with git-fetch, you may also set up configuration options to
|
||||
As with `git fetch`, you may also set up configuration options to
|
||||
save typing; so, for example, after
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -1988,13 +1988,13 @@ error: failed to push to 'ssh://yourserver.com/~you/proj.git'
|
||||
|
||||
This can happen, for example, if you:
|
||||
|
||||
- use `git-reset --hard` to remove already-published commits, or
|
||||
- use `git-commit --amend` to replace already-published commits
|
||||
- use `git reset --hard` to remove already-published commits, or
|
||||
- use `git commit --amend` to replace already-published commits
|
||||
(as in <<fixing-a-mistake-by-rewriting-history>>), or
|
||||
- use `git-rebase` to rebase any already-published commits (as
|
||||
- use `git rebase` to rebase any already-published commits (as
|
||||
in <<using-git-rebase>>).
|
||||
|
||||
You may force git-push to perform the update anyway by preceding the
|
||||
You may force `git push` to perform the update anyway by preceding the
|
||||
branch name with a plus sign:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -2036,7 +2036,7 @@ advantages over the central shared repository:
|
||||
|
||||
- Git's ability to quickly import and merge patches allows a
|
||||
single maintainer to process incoming changes even at very
|
||||
high rates. And when that becomes too much, git-pull provides
|
||||
high rates. And when that becomes too much, `git pull` provides
|
||||
an easy way for that maintainer to delegate this job to other
|
||||
maintainers while still allowing optional review of incoming
|
||||
changes.
|
||||
@@ -2404,7 +2404,7 @@ use them, and then explain some of the problems that can arise because
|
||||
you are rewriting history.
|
||||
|
||||
[[using-git-rebase]]
|
||||
Keeping a patch series up to date using git-rebase
|
||||
Keeping a patch series up to date using git rebase
|
||||
--------------------------------------------------
|
||||
|
||||
Suppose that you create a branch "mywork" on a remote-tracking branch
|
||||
@@ -2468,9 +2468,9 @@ patches to the new mywork. The result will look like:
|
||||
................................................
|
||||
|
||||
In the process, it may discover conflicts. In that case it will stop
|
||||
and allow you to fix the conflicts; after fixing conflicts, use "git-add"
|
||||
and allow you to fix the conflicts; after fixing conflicts, use `git add`
|
||||
to update the index with those contents, and then, instead of
|
||||
running git-commit, just run
|
||||
running `git commit`, just run
|
||||
|
||||
-------------------------------------------------
|
||||
$ git rebase --continue
|
||||
@@ -2508,7 +2508,7 @@ with
|
||||
$ git tag bad mywork~5
|
||||
-------------------------------------------------
|
||||
|
||||
(Either gitk or git-log may be useful for finding the commit.)
|
||||
(Either gitk or `git log` may be useful for finding the commit.)
|
||||
|
||||
Then check out that commit, edit it, and rebase the rest of the series
|
||||
on top of it (note that we could check out the commit on a temporary
|
||||
@@ -2549,12 +2549,12 @@ $ gitk origin..mywork &
|
||||
|
||||
and browse through the list of patches in the mywork branch using gitk,
|
||||
applying them (possibly in a different order) to mywork-new using
|
||||
cherry-pick, and possibly modifying them as you go using `commit --amend`.
|
||||
cherry-pick, and possibly modifying them as you go using `git commit --amend`.
|
||||
The linkgit:git-gui[1] command may also help as it allows you to
|
||||
individually select diff hunks for inclusion in the index (by
|
||||
right-clicking on the diff hunk and choosing "Stage Hunk for Commit").
|
||||
|
||||
Another technique is to use git-format-patch to create a series of
|
||||
Another technique is to use `git format-patch` to create a series of
|
||||
patches, then reset the state to before the patches:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -2662,7 +2662,7 @@ you know is that D is bad, that Z is good, and that
|
||||
linkgit:git-bisect[1] identifies C as the culprit, how will you
|
||||
figure out that the problem is due to this change in semantics?
|
||||
|
||||
When the result of a git-bisect is a non-merge commit, you should
|
||||
When the result of a `git bisect` is a non-merge commit, you should
|
||||
normally be able to discover the problem by examining just that commit.
|
||||
Developers can make this easy by breaking their changes into small
|
||||
self-contained commits. That won't help in the case above, however,
|
||||
@@ -2725,7 +2725,7 @@ master branch. In more detail:
|
||||
git fetch and fast-forwards
|
||||
---------------------------
|
||||
|
||||
In the previous example, when updating an existing branch, "git-fetch"
|
||||
In the previous example, when updating an existing branch, "git fetch"
|
||||
checks to make sure that the most recent commit on the remote
|
||||
branch is a descendant of the most recent commit on your copy of the
|
||||
branch before updating your copy of the branch to point at the new
|
||||
@@ -2751,7 +2751,7 @@ resulting in a situation like:
|
||||
o--o--o <-- new head of the branch
|
||||
................................................
|
||||
|
||||
In this case, "git-fetch" will fail, and print out a warning.
|
||||
In this case, "git fetch" will fail, and print out a warning.
|
||||
|
||||
In that case, you can still force git to update to the new head, as
|
||||
described in the following section. However, note that in the
|
||||
@@ -2760,7 +2760,7 @@ unless you've already created a reference of your own pointing to
|
||||
them.
|
||||
|
||||
[[forcing-fetch]]
|
||||
Forcing git-fetch to do non-fast-forward updates
|
||||
Forcing git fetch to do non-fast-forward updates
|
||||
------------------------------------------------
|
||||
|
||||
If git fetch fails because the new head of a branch is not a
|
||||
@@ -2865,8 +2865,8 @@ The Object Database
|
||||
We already saw in <<understanding-commits>> that all commits are stored
|
||||
under a 40-digit "object name". In fact, all the information needed to
|
||||
represent the history of a project is stored in objects with such names.
|
||||
In each case the name is calculated by taking the SHA1 hash of the
|
||||
contents of the object. The SHA1 hash is a cryptographic hash function.
|
||||
In each case the name is calculated by taking the SHA-1 hash of the
|
||||
contents of the object. The SHA-1 hash is a cryptographic hash function.
|
||||
What that means to us is that it is impossible to find two different
|
||||
objects with the same name. This has a number of advantages; among
|
||||
others:
|
||||
@@ -2877,10 +2877,10 @@ others:
|
||||
same content stored in two repositories will always be stored under
|
||||
the same name.
|
||||
- Git can detect errors when it reads an object, by checking that the
|
||||
object's name is still the SHA1 hash of its contents.
|
||||
object's name is still the SHA-1 hash of its contents.
|
||||
|
||||
(See <<object-details>> for the details of the object formatting and
|
||||
SHA1 calculation.)
|
||||
SHA-1 calculation.)
|
||||
|
||||
There are four different types of objects: "blob", "tree", "commit", and
|
||||
"tag".
|
||||
@@ -2926,9 +2926,9 @@ committer Junio C Hamano <gitster@pobox.com> 1187591163 -0700
|
||||
|
||||
As you can see, a commit is defined by:
|
||||
|
||||
- a tree: The SHA1 name of a tree object (as defined below), representing
|
||||
- a tree: The SHA-1 name of a tree object (as defined below), representing
|
||||
the contents of a directory at a certain point in time.
|
||||
- parent(s): The SHA1 name of some number of commits which represent the
|
||||
- parent(s): The SHA-1 name of some number of commits which represent the
|
||||
immediately previous step(s) in the history of the project. The
|
||||
example above has one parent; merge commits may have more than
|
||||
one. A commit with no parents is called a "root" commit, and
|
||||
@@ -2977,13 +2977,13 @@ $ git ls-tree fb3a8bdd0ce
|
||||
------------------------------------------------
|
||||
|
||||
As you can see, a tree object contains a list of entries, each with a
|
||||
mode, object type, SHA1 name, and name, sorted by name. It represents
|
||||
mode, object type, SHA-1 name, and name, sorted by name. It represents
|
||||
the contents of a single directory tree.
|
||||
|
||||
The object type may be a blob, representing the contents of a file, or
|
||||
another tree, representing the contents of a subdirectory. Since trees
|
||||
and blobs, like all other objects, are named by the SHA1 hash of their
|
||||
contents, two trees have the same SHA1 name if and only if their
|
||||
and blobs, like all other objects, are named by the SHA-1 hash of their
|
||||
contents, two trees have the same SHA-1 name if and only if their
|
||||
contents (including, recursively, the contents of all subdirectories)
|
||||
are identical. This allows git to quickly determine the differences
|
||||
between two related tree objects, since it can ignore any entries with
|
||||
@@ -3029,15 +3029,15 @@ currently checked out.
|
||||
Trust
|
||||
~~~~~
|
||||
|
||||
If you receive the SHA1 name of a blob from one source, and its contents
|
||||
If you receive the SHA-1 name of a blob from one source, and its contents
|
||||
from another (possibly untrusted) source, you can still trust that those
|
||||
contents are correct as long as the SHA1 name agrees. This is because
|
||||
the SHA1 is designed so that it is infeasible to find different contents
|
||||
contents are correct as long as the SHA-1 name agrees. This is because
|
||||
the SHA-1 is designed so that it is infeasible to find different contents
|
||||
that produce the same hash.
|
||||
|
||||
Similarly, you need only trust the SHA1 name of a top-level tree object
|
||||
Similarly, you need only trust the SHA-1 name of a top-level tree object
|
||||
to trust the contents of the entire directory that it refers to, and if
|
||||
you receive the SHA1 name of a commit from a trusted source, then you
|
||||
you receive the SHA-1 name of a commit from a trusted source, then you
|
||||
can easily verify the entire history of commits reachable through
|
||||
parents of that commit, and all of those contents of the trees referred
|
||||
to by those commits.
|
||||
@@ -3049,7 +3049,7 @@ that you trust that commit, and the immutability of the history of
|
||||
commits tells others that they can trust the whole history.
|
||||
|
||||
In other words, you can easily validate a whole archive by just
|
||||
sending out a single email that tells the people the name (SHA1 hash)
|
||||
sending out a single email that tells the people the name (SHA-1 hash)
|
||||
of the top commit, and digitally sign that email using something
|
||||
like GPG/PGP.
|
||||
|
||||
@@ -3090,7 +3090,7 @@ How git stores objects efficiently: pack files
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Newly created objects are initially created in a file named after the
|
||||
object's SHA1 hash (stored in .git/objects).
|
||||
object's SHA-1 hash (stored in .git/objects).
|
||||
|
||||
Unfortunately this system becomes inefficient once a project has a
|
||||
lot of objects. Try this on an old project:
|
||||
@@ -3131,7 +3131,7 @@ $ git prune
|
||||
|
||||
to remove any of the "loose" objects that are now contained in the
|
||||
pack. This will also remove any unreferenced objects (which may be
|
||||
created when, for example, you use "git-reset" to remove a commit).
|
||||
created when, for example, you use "git reset" to remove a commit).
|
||||
You can verify that the loose objects are gone by looking at the
|
||||
.git/objects directory or by running
|
||||
|
||||
@@ -3160,7 +3160,7 @@ branch still exists, as does everything it pointed to. The branch
|
||||
pointer itself just doesn't, since you replaced it with another one.
|
||||
|
||||
There are also other situations that cause dangling objects. For
|
||||
example, a "dangling blob" may arise because you did a "git-add" of a
|
||||
example, a "dangling blob" may arise because you did a "git add" of a
|
||||
file, but then, before you actually committed it and made it part of the
|
||||
bigger picture, you changed something else in that file and committed
|
||||
that *updated* thing--the old state that you added originally ends up
|
||||
@@ -3210,7 +3210,7 @@ Usually, dangling blobs and trees aren't very interesting. They're
|
||||
almost always the result of either being a half-way mergebase (the blob
|
||||
will often even have the conflict markers from a merge in it, if you
|
||||
have had conflicting merges that you fixed up by hand), or simply
|
||||
because you interrupted a "git-fetch" with ^C or something like that,
|
||||
because you interrupted a "git fetch" with ^C or something like that,
|
||||
leaving _some_ of the new objects in the object database, but just
|
||||
dangling and useless.
|
||||
|
||||
@@ -3225,9 +3225,9 @@ and they'll be gone. But you should only run "git prune" on a quiescent
|
||||
repository--it's kind of like doing a filesystem fsck recovery: you
|
||||
don't want to do that while the filesystem is mounted.
|
||||
|
||||
(The same is true of "git-fsck" itself, btw, but since
|
||||
git-fsck never actually *changes* the repository, it just reports
|
||||
on what it found, git-fsck itself is never "dangerous" to run.
|
||||
(The same is true of "git fsck" itself, btw, but since
|
||||
`git fsck` never actually *changes* the repository, it just reports
|
||||
on what it found, `git fsck` itself is never 'dangerous' to run.
|
||||
Running it while somebody is actually changing the repository can cause
|
||||
confusing and scary messages, but it won't actually do anything bad. In
|
||||
contrast, running "git prune" while somebody is actively changing the
|
||||
@@ -3297,7 +3297,7 @@ $ git hash-object -w somedirectory/myfile
|
||||
------------------------------------------------
|
||||
|
||||
which will create and store a blob object with the contents of
|
||||
somedirectory/myfile, and output the sha1 of that object. if you're
|
||||
somedirectory/myfile, and output the SHA-1 of that object. if you're
|
||||
extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in
|
||||
which case you've guessed right, and the corruption is fixed!
|
||||
|
||||
@@ -3359,7 +3359,7 @@ The index
|
||||
-----------
|
||||
|
||||
The index is a binary file (generally kept in .git/index) containing a
|
||||
sorted list of path names, each with permissions and the SHA1 of a blob
|
||||
sorted list of path names, each with permissions and the SHA-1 of a blob
|
||||
object; linkgit:git-ls-files[1] can show you the contents of the index:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -3489,14 +3489,14 @@ done
|
||||
|
||||
NOTE: Do not use local URLs here if you plan to publish your superproject!
|
||||
|
||||
See what files `git-submodule` created:
|
||||
See what files `git submodule` created:
|
||||
|
||||
-------------------------------------------------
|
||||
$ ls -a
|
||||
. .. .git .gitmodules a b c d
|
||||
-------------------------------------------------
|
||||
|
||||
The `git-submodule add <repo> <path>` command does a couple of things:
|
||||
The `git submodule add <repo> <path>` command does a couple of things:
|
||||
|
||||
- It clones the submodule from <repo> to the given <path> under the
|
||||
current directory and by default checks out the master branch.
|
||||
@@ -3542,7 +3542,7 @@ init` to add the submodule repository URLs to `.git/config`:
|
||||
$ git submodule init
|
||||
-------------------------------------------------
|
||||
|
||||
Now use `git-submodule update` to clone the repositories and check out the
|
||||
Now use `git submodule update` to clone the repositories and check out the
|
||||
commits specified in the superproject:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -3552,8 +3552,8 @@ $ ls -a
|
||||
. .. .git a.txt
|
||||
-------------------------------------------------
|
||||
|
||||
One major difference between `git-submodule update` and `git-submodule add` is
|
||||
that `git-submodule update` checks out a specific commit, rather than the tip
|
||||
One major difference between `git submodule update` and `git submodule add` is
|
||||
that `git submodule update` checks out a specific commit, rather than the tip
|
||||
of a branch. It's like checking out a tag: the head is detached, so you're not
|
||||
working on a branch.
|
||||
|
||||
@@ -3754,7 +3754,7 @@ unsaved state that you might want to restore later!) your current
|
||||
index. Normal operation is just
|
||||
|
||||
-------------------------------------------------
|
||||
$ git read-tree <sha1 of tree>
|
||||
$ git read-tree <SHA-1 of tree>
|
||||
-------------------------------------------------
|
||||
|
||||
and your index file will now be equivalent to the tree that you saved
|
||||
@@ -3769,7 +3769,7 @@ You update your working directory from the index by "checking out"
|
||||
files. This is not a very common operation, since normally you'd just
|
||||
keep your files updated, and rather than write to your working
|
||||
directory, you'd tell the index files about the changes in your
|
||||
working directory (i.e. `git-update-index`).
|
||||
working directory (i.e. `git update-index`).
|
||||
|
||||
However, if you decide to jump to a new version, or check out somebody
|
||||
else's version, or just restore a previous tree, you'd populate your
|
||||
@@ -3782,7 +3782,7 @@ $ git checkout-index filename
|
||||
|
||||
or, if you want to check out all of the index, use `-a`.
|
||||
|
||||
NOTE! git-checkout-index normally refuses to overwrite old files, so
|
||||
NOTE! `git checkout-index` normally refuses to overwrite old files, so
|
||||
if you have an old version of the tree already checked out, you will
|
||||
need to use the "-f" flag ('before' the "-a" flag or the filename) to
|
||||
'force' the checkout.
|
||||
@@ -3820,7 +3820,7 @@ $ git commit-tree <tree> -p <parent> [-p <parent2> ..]
|
||||
and then giving the reason for the commit on stdin (either through
|
||||
redirection from a pipe or file, or by just typing it at the tty).
|
||||
|
||||
git-commit-tree will return the name of the object that represents
|
||||
`git commit-tree` will return the name of the object that represents
|
||||
that commit, and you should save it away for later use. Normally,
|
||||
you'd commit a new `HEAD` state, and while git doesn't care where you
|
||||
save the note about that state, in practice we tend to just write the
|
||||
@@ -3889,7 +3889,7 @@ $ git cat-file blob|tree|commit|tag <objectname>
|
||||
|
||||
to show its contents. NOTE! Trees have binary content, and as a result
|
||||
there is a special helper for showing that content, called
|
||||
`git-ls-tree`, which turns the binary content into a more easily
|
||||
`git ls-tree`, which turns the binary content into a more easily
|
||||
readable form.
|
||||
|
||||
It's especially instructive to look at "commit" objects, since those
|
||||
@@ -3978,13 +3978,13 @@ $ git ls-files --unmerged
|
||||
------------------------------------------------
|
||||
|
||||
Each line of the `git ls-files --unmerged` output begins with
|
||||
the blob mode bits, blob SHA1, 'stage number', and the
|
||||
the blob mode bits, blob SHA-1, 'stage number', and the
|
||||
filename. The 'stage number' is git's way to say which tree it
|
||||
came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD`
|
||||
tree, and stage3 `$target` tree.
|
||||
|
||||
Earlier we said that trivial merges are done inside
|
||||
`git-read-tree -m`. For example, if the file did not change
|
||||
`git read-tree -m`. For example, if the file did not change
|
||||
from `$orig` to `HEAD` nor `$target`, or if the file changed
|
||||
from `$orig` to `HEAD` and `$orig` to `$target` the same way,
|
||||
obviously the final outcome is what is in `HEAD`. What the
|
||||
@@ -4011,20 +4011,20 @@ $ mv -f hello.c~2 hello.c
|
||||
$ git update-index hello.c
|
||||
-------------------------------------------------
|
||||
|
||||
When a path is in the "unmerged" state, running `git-update-index` for
|
||||
When a path is in the "unmerged" state, running `git update-index` for
|
||||
that path tells git to mark the path resolved.
|
||||
|
||||
The above is the description of a git merge at the lowest level,
|
||||
to help you understand what conceptually happens under the hood.
|
||||
In practice, nobody, not even git itself, runs `git-cat-file` three times
|
||||
for this. There is a `git-merge-index` program that extracts the
|
||||
In practice, nobody, not even git itself, runs `git cat-file` three times
|
||||
for this. There is a `git merge-index` program that extracts the
|
||||
stages to temporary files and calls a "merge" script on it:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git merge-index git-merge-one-file hello.c
|
||||
-------------------------------------------------
|
||||
|
||||
and that is what higher level `git-merge -s resolve` is implemented with.
|
||||
and that is what higher level `git merge -s resolve` is implemented with.
|
||||
|
||||
[[hacking-git]]
|
||||
Hacking git
|
||||
@@ -4045,12 +4045,12 @@ objects). There are currently four different object types: "blob",
|
||||
Regardless of object type, all objects share the following
|
||||
characteristics: they are all deflated with zlib, and have a header
|
||||
that not only specifies their type, but also provides size information
|
||||
about the data in the object. It's worth noting that the SHA1 hash
|
||||
about the data in the object. It's worth noting that the SHA-1 hash
|
||||
that is used to name the object is the hash of the original data
|
||||
plus this header, so `sha1sum` 'file' does not match the object name
|
||||
for 'file'.
|
||||
(Historical note: in the dawn of the age of git the hash
|
||||
was the sha1 of the 'compressed' object.)
|
||||
was the SHA-1 of the 'compressed' object.)
|
||||
|
||||
As a result, the general consistency of an object can always be tested
|
||||
independently of the contents or the type of the object: all objects can
|
||||
@@ -4061,7 +4061,7 @@ size> {plus} <byte\0> {plus} <binary object data>.
|
||||
|
||||
The structured objects can further have their structure and
|
||||
connectivity to other objects verified. This is generally done with
|
||||
the `git-fsck` program, which generates a full dependency graph
|
||||
the `git fsck` program, which generates a full dependency graph
|
||||
of all objects, and verifies their internal consistency (in addition
|
||||
to just verifying their superficial consistency through the hash).
|
||||
|
||||
@@ -4120,7 +4120,7 @@ functions like `get_sha1_basic()` or the likes.
|
||||
This is just to get you into the groove for the most libified part of Git:
|
||||
the revision walker.
|
||||
|
||||
Basically, the initial version of `git-log` was a shell script:
|
||||
Basically, the initial version of `git log` was a shell script:
|
||||
|
||||
----------------------------------------------------------------
|
||||
$ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
|
||||
@@ -4129,20 +4129,20 @@ $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
|
||||
|
||||
What does this mean?
|
||||
|
||||
`git-rev-list` is the original version of the revision walker, which
|
||||
`git rev-list` is the original version of the revision walker, which
|
||||
_always_ printed a list of revisions to stdout. It is still functional,
|
||||
and needs to, since most new Git programs start out as scripts using
|
||||
`git-rev-list`.
|
||||
`git rev-list`.
|
||||
|
||||
`git-rev-parse` is not as important any more; it was only used to filter out
|
||||
`git rev-parse` is not as important any more; it was only used to filter out
|
||||
options that were relevant for the different plumbing commands that were
|
||||
called by the script.
|
||||
|
||||
Most of what `git-rev-list` did is contained in `revision.c` and
|
||||
Most of what `git rev-list` did is contained in `revision.c` and
|
||||
`revision.h`. It wraps the options in a struct named `rev_info`, which
|
||||
controls how and what revisions are walked, and more.
|
||||
|
||||
The original job of `git-rev-parse` is now taken by the function
|
||||
The original job of `git rev-parse` is now taken by the function
|
||||
`setup_revisions()`, which parses the revisions and the common command line
|
||||
options for the revision walker. This information is stored in the struct
|
||||
`rev_info` for later consumption. You can do your own command line option
|
||||
@@ -4155,7 +4155,7 @@ just have a look at the first implementation of `cmd_log()`; call
|
||||
`git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you
|
||||
no longer need to call `setup_pager()` directly).
|
||||
|
||||
Nowadays, `git-log` is a builtin, which means that it is _contained_ in the
|
||||
Nowadays, `git log` is a builtin, which means that it is _contained_ in the
|
||||
command `git`. The source side of a builtin is
|
||||
|
||||
- a function called `cmd_<bla>`, typically defined in `builtin-<bla>.c`,
|
||||
@@ -4171,7 +4171,7 @@ since they share quite a bit of code. In that case, the commands which are
|
||||
_not_ named like the `.c` file in which they live have to be listed in
|
||||
`BUILT_INS` in the `Makefile`.
|
||||
|
||||
`git-log` looks more complicated in C than it does in the original script,
|
||||
`git log` looks more complicated in C than it does in the original script,
|
||||
but that allows for a much greater flexibility and performance.
|
||||
|
||||
Here again it is a good point to take a pause.
|
||||
@@ -4182,9 +4182,9 @@ the organization of Git (after you know the basic concepts).
|
||||
So, think about something which you are interested in, say, "how can I
|
||||
access a blob just knowing the object name of it?". The first step is to
|
||||
find a Git command with which you can do it. In this example, it is either
|
||||
`git-show` or `git-cat-file`.
|
||||
`git show` or `git cat-file`.
|
||||
|
||||
For the sake of clarity, let's stay with `git-cat-file`, because it
|
||||
For the sake of clarity, let's stay with `git cat-file`, because it
|
||||
|
||||
- is plumbing, and
|
||||
|
||||
@@ -4198,7 +4198,7 @@ it does.
|
||||
------------------------------------------------------------------
|
||||
git_config(git_default_config);
|
||||
if (argc != 3)
|
||||
usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
|
||||
usage("git cat-file [-t|-s|-e|-p|<type>] <sha1>");
|
||||
if (get_sha1(argv[2], sha1))
|
||||
die("Not a valid object name %s", argv[2]);
|
||||
------------------------------------------------------------------
|
||||
@@ -4243,10 +4243,10 @@ To find out how the result can be used, just read on in `cmd_cat_file()`:
|
||||
-----------------------------------
|
||||
|
||||
Sometimes, you do not know where to look for a feature. In many such cases,
|
||||
it helps to search through the output of `git log`, and then `git-show` the
|
||||
it helps to search through the output of `git log`, and then `git show` the
|
||||
corresponding commit.
|
||||
|
||||
Example: If you know that there was some test case for `git-bundle`, but
|
||||
Example: If you know that there was some test case for `git bundle`, but
|
||||
do not remember where it was (yes, you _could_ `git grep bundle t/`, but that
|
||||
does not illustrate the point!):
|
||||
|
||||
@@ -4530,7 +4530,7 @@ The basic requirements:
|
||||
- Whenever possible, section headings should clearly describe the task
|
||||
they explain how to do, in language that requires no more knowledge
|
||||
than necessary: for example, "importing patches into a project" rather
|
||||
than "the git-am command"
|
||||
than "the `git am` command"
|
||||
|
||||
Think about how to create a clear chapter dependency graph that will
|
||||
allow people to get to important topics without necessarily reading
|
||||
|
||||
31
Makefile
31
Makefile
@@ -145,6 +145,8 @@ all::
|
||||
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
|
||||
# MakeMaker (e.g. using ActiveState under Cygwin).
|
||||
#
|
||||
# Define NO_PERL if you do not want Perl scripts or libraries at all.
|
||||
#
|
||||
# Define NO_TCLTK if you do not want Tcl/Tk GUI.
|
||||
#
|
||||
# The TCL_PATH variable governs the location of the Tcl interpreter
|
||||
@@ -357,7 +359,10 @@ BUILT_INS += git-whatchanged$X
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
|
||||
|
||||
# what 'all' will build but not install in gitexecdir
|
||||
OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
|
||||
OTHER_PROGRAMS = git$X
|
||||
ifndef NO_PERL
|
||||
OTHER_PROGRAMS += gitweb/gitweb.cgi
|
||||
endif
|
||||
|
||||
# Set paths to tools early so that they can be used for version tests.
|
||||
ifndef SHELL_PATH
|
||||
@@ -436,6 +441,7 @@ LIB_OBJS += archive-tar.o
|
||||
LIB_OBJS += archive-zip.o
|
||||
LIB_OBJS += attr.o
|
||||
LIB_OBJS += base85.o
|
||||
LIB_OBJS += bisect.o
|
||||
LIB_OBJS += blob.o
|
||||
LIB_OBJS += branch.o
|
||||
LIB_OBJS += bundle.o
|
||||
@@ -536,6 +542,7 @@ BUILTIN_OBJS += builtin-add.o
|
||||
BUILTIN_OBJS += builtin-annotate.o
|
||||
BUILTIN_OBJS += builtin-apply.o
|
||||
BUILTIN_OBJS += builtin-archive.o
|
||||
BUILTIN_OBJS += builtin-bisect--helper.o
|
||||
BUILTIN_OBJS += builtin-blame.o
|
||||
BUILTIN_OBJS += builtin-branch.o
|
||||
BUILTIN_OBJS += builtin-bundle.o
|
||||
@@ -1119,6 +1126,10 @@ ifeq ($(TCLTK_PATH),)
|
||||
NO_TCLTK=NoThanks
|
||||
endif
|
||||
|
||||
ifeq ($(PERL_PATH),)
|
||||
NO_PERL=NoThanks
|
||||
endif
|
||||
|
||||
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
|
||||
QUIET_SUBDIR1 =
|
||||
|
||||
@@ -1193,7 +1204,9 @@ ifndef NO_TCLTK
|
||||
$(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
|
||||
$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
|
||||
endif
|
||||
ifndef NO_PERL
|
||||
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
|
||||
endif
|
||||
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
|
||||
|
||||
please_set_SHELL_PATH_to_a_more_modern_shell:
|
||||
@@ -1206,6 +1219,7 @@ strip: $(PROGRAMS) git$X
|
||||
|
||||
git.o: git.c common-cmds.h GIT-CFLAGS
|
||||
$(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
|
||||
'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
|
||||
$(ALL_CFLAGS) -c $(filter %.c,$^)
|
||||
|
||||
git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
|
||||
@@ -1240,6 +1254,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
ifndef NO_PERL
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
|
||||
|
||||
perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
|
||||
@@ -1299,6 +1314,15 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
|
||||
$@.sh > $@+ && \
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
else # NO_PERL
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
|
||||
$(QUIET_GEN)$(RM) $@ $@+ && \
|
||||
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
||||
-e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
|
||||
unimplemented.sh >$@+ && \
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
endif # NO_PERL
|
||||
|
||||
configure: configure.ac
|
||||
$(QUIET_GEN)$(RM) $@ $<+ && \
|
||||
@@ -1415,6 +1439,7 @@ GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
|
||||
@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
|
||||
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
|
||||
@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
|
||||
@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
|
||||
|
||||
### Detect Tck/Tk interpreter path changes
|
||||
ifndef NO_TCLTK
|
||||
@@ -1617,9 +1642,11 @@ clean:
|
||||
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
|
||||
$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
|
||||
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
|
||||
$(RM) gitweb/gitweb.cgi
|
||||
$(MAKE) -C Documentation/ clean
|
||||
ifndef NO_PERL
|
||||
$(RM) gitweb/gitweb.cgi
|
||||
$(MAKE) -C perl clean
|
||||
endif
|
||||
$(MAKE) -C templates/ clean
|
||||
$(MAKE) -C t/ clean
|
||||
ifndef NO_TCLTK
|
||||
|
||||
556
bisect.c
Normal file
556
bisect.c
Normal file
@@ -0,0 +1,556 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "refs.h"
|
||||
#include "list-objects.h"
|
||||
#include "quote.h"
|
||||
#include "sha1-lookup.h"
|
||||
#include "bisect.h"
|
||||
|
||||
static unsigned char (*skipped_sha1)[20];
|
||||
static int skipped_sha1_nr;
|
||||
static int skipped_sha1_alloc;
|
||||
|
||||
static const char **rev_argv;
|
||||
static int rev_argv_nr;
|
||||
static int rev_argv_alloc;
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
#define COUNTED (1u<<16)
|
||||
|
||||
/*
|
||||
* This is a truly stupid algorithm, but it's only
|
||||
* used for bisection, and we just don't care enough.
|
||||
*
|
||||
* We care just barely enough to avoid recursing for
|
||||
* non-merge entries.
|
||||
*/
|
||||
static int count_distance(struct commit_list *entry)
|
||||
{
|
||||
int nr = 0;
|
||||
|
||||
while (entry) {
|
||||
struct commit *commit = entry->item;
|
||||
struct commit_list *p;
|
||||
|
||||
if (commit->object.flags & (UNINTERESTING | COUNTED))
|
||||
break;
|
||||
if (!(commit->object.flags & TREESAME))
|
||||
nr++;
|
||||
commit->object.flags |= COUNTED;
|
||||
p = commit->parents;
|
||||
entry = p;
|
||||
if (p) {
|
||||
p = p->next;
|
||||
while (p) {
|
||||
nr += count_distance(p);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
static void clear_distance(struct commit_list *list)
|
||||
{
|
||||
while (list) {
|
||||
struct commit *commit = list->item;
|
||||
commit->object.flags &= ~COUNTED;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEBUG_BISECT 0
|
||||
|
||||
static inline int weight(struct commit_list *elem)
|
||||
{
|
||||
return *((int*)(elem->item->util));
|
||||
}
|
||||
|
||||
static inline void weight_set(struct commit_list *elem, int weight)
|
||||
{
|
||||
*((int*)(elem->item->util)) = weight;
|
||||
}
|
||||
|
||||
static int count_interesting_parents(struct commit *commit)
|
||||
{
|
||||
struct commit_list *p;
|
||||
int count;
|
||||
|
||||
for (count = 0, p = commit->parents; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline int halfway(struct commit_list *p, int nr)
|
||||
{
|
||||
/*
|
||||
* Don't short-cut something we are not going to return!
|
||||
*/
|
||||
if (p->item->object.flags & TREESAME)
|
||||
return 0;
|
||||
if (DEBUG_BISECT)
|
||||
return 0;
|
||||
/*
|
||||
* 2 and 3 are halfway of 5.
|
||||
* 3 is halfway of 6 but 2 and 4 are not.
|
||||
*/
|
||||
switch (2 * weight(p) - nr) {
|
||||
case -1: case 0: case 1:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if !DEBUG_BISECT
|
||||
#define show_list(a,b,c,d) do { ; } while (0)
|
||||
#else
|
||||
static void show_list(const char *debug, int counted, int nr,
|
||||
struct commit_list *list)
|
||||
{
|
||||
struct commit_list *p;
|
||||
|
||||
fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
|
||||
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *pp;
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *buf = read_sha1_file(commit->object.sha1, &type, &size);
|
||||
char *ep, *sp;
|
||||
|
||||
fprintf(stderr, "%c%c%c ",
|
||||
(flags & TREESAME) ? ' ' : 'T',
|
||||
(flags & UNINTERESTING) ? 'U' : ' ',
|
||||
(flags & COUNTED) ? 'C' : ' ');
|
||||
if (commit->util)
|
||||
fprintf(stderr, "%3d", weight(p));
|
||||
else
|
||||
fprintf(stderr, "---");
|
||||
fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
|
||||
for (pp = commit->parents; pp; pp = pp->next)
|
||||
fprintf(stderr, " %.*s", 8,
|
||||
sha1_to_hex(pp->item->object.sha1));
|
||||
|
||||
sp = strstr(buf, "\n\n");
|
||||
if (sp) {
|
||||
sp += 2;
|
||||
for (ep = sp; *ep && *ep != '\n'; ep++)
|
||||
;
|
||||
fprintf(stderr, " %.*s", (int)(ep - sp), sp);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_BISECT */
|
||||
|
||||
static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p, *best;
|
||||
int best_distance = -1;
|
||||
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > best_distance) {
|
||||
best = p;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
struct commit_dist {
|
||||
struct commit *commit;
|
||||
int distance;
|
||||
};
|
||||
|
||||
static int compare_commit_dist(const void *a_, const void *b_)
|
||||
{
|
||||
struct commit_dist *a, *b;
|
||||
|
||||
a = (struct commit_dist *)a_;
|
||||
b = (struct commit_dist *)b_;
|
||||
if (a->distance != b->distance)
|
||||
return b->distance - a->distance; /* desc sort */
|
||||
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
|
||||
}
|
||||
|
||||
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_dist *array = xcalloc(nr, sizeof(*array));
|
||||
int cnt, i;
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
array[cnt].commit = p->item;
|
||||
array[cnt].distance = distance;
|
||||
cnt++;
|
||||
}
|
||||
qsort(array, cnt, sizeof(*array), compare_commit_dist);
|
||||
for (p = list, i = 0; i < cnt; i++) {
|
||||
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
|
||||
struct object *obj = &(array[i].commit->object);
|
||||
|
||||
sprintf(r->name, "dist=%d", array[i].distance);
|
||||
r->next = add_decoration(&name_decoration, obj, r);
|
||||
p->item = array[i].commit;
|
||||
p = p->next;
|
||||
}
|
||||
if (p)
|
||||
p->next = NULL;
|
||||
free(array);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
* reach any tree-changing commits (e.g. just above uninteresting one
|
||||
* but traversal is with pathspec).
|
||||
*
|
||||
* weight = -1 means it has one parent and its distance is yet to
|
||||
* be computed.
|
||||
*
|
||||
* weight = -2 means it has more than one parent and its distance is
|
||||
* unknown. After running count_distance() first, they will get zero
|
||||
* or positive distance.
|
||||
*/
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
{
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
|
||||
counted = 0;
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
|
||||
p->item->util = &weights[n++];
|
||||
switch (count_interesting_parents(commit)) {
|
||||
case 0:
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, 1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
/*
|
||||
* otherwise, it is known not to reach any
|
||||
* tree-changing commit and gets weight 0.
|
||||
*/
|
||||
break;
|
||||
case 1:
|
||||
weight_set(p, -1);
|
||||
break;
|
||||
default:
|
||||
weight_set(p, -2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 initialize", counted, nr, list);
|
||||
|
||||
/*
|
||||
* If you have only one parent in the resulting set
|
||||
* then you can reach one commit more than that parent
|
||||
* can reach. So we do not have to run the expensive
|
||||
* count_distance() for single strand of pearls.
|
||||
*
|
||||
* However, if you have more than one parents, you cannot
|
||||
* just add their distance and one for yourself, since
|
||||
* they usually reach the same ancestor and you would
|
||||
* end up counting them twice that way.
|
||||
*
|
||||
* So we will first count distance of merges the usual
|
||||
* way, and then fill the blanks using cheaper algorithm.
|
||||
*/
|
||||
for (p = list; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (weight(p) != -2)
|
||||
continue;
|
||||
weight_set(p, count_distance(p));
|
||||
clear_distance(list);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
counted++;
|
||||
}
|
||||
|
||||
show_list("bisection 2 count_distance", counted, nr, list);
|
||||
|
||||
while (counted < nr) {
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *q;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (0 <= weight(p))
|
||||
continue;
|
||||
for (q = p->item->parents; q; q = q->next) {
|
||||
if (q->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (0 <= weight(q))
|
||||
break;
|
||||
}
|
||||
if (!q)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* weight for p is unknown but q is known.
|
||||
* add one for p itself if p is to be counted,
|
||||
* otherwise inherit it from q directly.
|
||||
*/
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, weight(q)+1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
else
|
||||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
if (!find_all)
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all,
|
||||
int find_all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!(flags & TREESAME))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
if (best) {
|
||||
if (!find_all)
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
free(weights);
|
||||
return best;
|
||||
}
|
||||
|
||||
static int register_ref(const char *refname, const unsigned char *sha1,
|
||||
int flags, void *cb_data)
|
||||
{
|
||||
if (!strcmp(refname, "bad")) {
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1));
|
||||
} else if (!prefixcmp(refname, "good-")) {
|
||||
const char *hex = sha1_to_hex(sha1);
|
||||
char *good = xmalloc(strlen(hex) + 2);
|
||||
*good = '^';
|
||||
memcpy(good + 1, hex, strlen(hex) + 1);
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = good;
|
||||
} else if (!prefixcmp(refname, "skip-")) {
|
||||
ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1,
|
||||
skipped_sha1_alloc);
|
||||
hashcpy(skipped_sha1[skipped_sha1_nr++], sha1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_bisect_refs(void)
|
||||
{
|
||||
return for_each_ref_in("refs/bisect/", register_ref, NULL);
|
||||
}
|
||||
|
||||
void read_bisect_paths(void)
|
||||
{
|
||||
struct strbuf str = STRBUF_INIT;
|
||||
const char *filename = git_path("BISECT_NAMES");
|
||||
FILE *fp = fopen(filename, "r");
|
||||
|
||||
if (!fp)
|
||||
die("Could not open file '%s': %s", filename, strerror(errno));
|
||||
|
||||
while (strbuf_getline(&str, fp, '\n') != EOF) {
|
||||
char *quoted;
|
||||
int res;
|
||||
|
||||
strbuf_trim(&str);
|
||||
quoted = strbuf_detach(&str, NULL);
|
||||
res = sq_dequote_to_argv(quoted, &rev_argv,
|
||||
&rev_argv_nr, &rev_argv_alloc);
|
||||
if (res)
|
||||
die("Badly quoted content in file '%s': %s",
|
||||
filename, quoted);
|
||||
}
|
||||
|
||||
strbuf_release(&str);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static int skipcmp(const void *a, const void *b)
|
||||
{
|
||||
return hashcmp(a, b);
|
||||
}
|
||||
|
||||
static void prepare_skipped(void)
|
||||
{
|
||||
qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp);
|
||||
}
|
||||
|
||||
static const unsigned char *skipped_sha1_access(size_t index, void *table)
|
||||
{
|
||||
unsigned char (*skipped)[20] = table;
|
||||
return skipped[index];
|
||||
}
|
||||
|
||||
static int lookup_skipped(unsigned char *sha1)
|
||||
{
|
||||
return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
|
||||
skipped_sha1_access);
|
||||
}
|
||||
|
||||
struct commit_list *filter_skipped(struct commit_list *list,
|
||||
struct commit_list **tried,
|
||||
int show_all)
|
||||
{
|
||||
struct commit_list *filtered = NULL, **f = &filtered;
|
||||
|
||||
*tried = NULL;
|
||||
|
||||
if (!skipped_sha1_nr)
|
||||
return list;
|
||||
|
||||
prepare_skipped();
|
||||
|
||||
while (list) {
|
||||
struct commit_list *next = list->next;
|
||||
list->next = NULL;
|
||||
if (0 <= lookup_skipped(list->item->object.sha1)) {
|
||||
/* Move current to tried list */
|
||||
*tried = list;
|
||||
tried = &list->next;
|
||||
} else {
|
||||
if (!show_all)
|
||||
return list;
|
||||
/* Move current to filtered list */
|
||||
*f = list;
|
||||
f = &list->next;
|
||||
}
|
||||
list = next;
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
static void bisect_rev_setup(struct rev_info *revs, const char *prefix)
|
||||
{
|
||||
init_revisions(revs, prefix);
|
||||
revs->abbrev = 0;
|
||||
revs->commit_format = CMIT_FMT_UNSPECIFIED;
|
||||
|
||||
/* argv[0] will be ignored by setup_revisions */
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup");
|
||||
|
||||
if (read_bisect_refs())
|
||||
die("reading bisect refs failed");
|
||||
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = xstrdup("--");
|
||||
|
||||
read_bisect_paths();
|
||||
|
||||
ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
|
||||
rev_argv[rev_argv_nr++] = NULL;
|
||||
|
||||
setup_revisions(rev_argv_nr, rev_argv, revs, NULL);
|
||||
|
||||
revs->limited = 1;
|
||||
}
|
||||
|
||||
int bisect_next_vars(const char *prefix)
|
||||
{
|
||||
struct rev_info revs;
|
||||
struct rev_list_info info;
|
||||
int reaches = 0, all = 0;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.revs = &revs;
|
||||
info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED;
|
||||
|
||||
bisect_rev_setup(&revs, prefix);
|
||||
|
||||
if (prepare_revision_walk(&revs))
|
||||
die("revision walk setup failed");
|
||||
if (revs.tree_objects)
|
||||
mark_edges_uninteresting(revs.commits, &revs, NULL);
|
||||
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all,
|
||||
!!skipped_sha1_nr);
|
||||
|
||||
return show_bisect_vars(&info, reaches, all);
|
||||
}
|
||||
29
bisect.h
Normal file
29
bisect.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef BISECT_H
|
||||
#define BISECT_H
|
||||
|
||||
extern struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all,
|
||||
int find_all);
|
||||
|
||||
extern struct commit_list *filter_skipped(struct commit_list *list,
|
||||
struct commit_list **tried,
|
||||
int show_all);
|
||||
|
||||
/* bisect_show_flags flags in struct rev_list_info */
|
||||
#define BISECT_SHOW_ALL (1<<0)
|
||||
#define BISECT_SHOW_TRIED (1<<1)
|
||||
#define BISECT_SHOW_STRINGED (1<<2)
|
||||
|
||||
struct rev_list_info {
|
||||
struct rev_info *revs;
|
||||
int bisect_show_flags;
|
||||
int show_timestamp;
|
||||
int hdr_termination;
|
||||
const char *header_prefix;
|
||||
};
|
||||
|
||||
extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
|
||||
|
||||
extern int bisect_next_vars(const char *prefix);
|
||||
|
||||
#endif
|
||||
10
branch.c
10
branch.c
@@ -134,16 +134,8 @@ void create_branch(const char *head,
|
||||
char *real_ref, msg[PATH_MAX + 20];
|
||||
struct strbuf ref = STRBUF_INIT;
|
||||
int forcing = 0;
|
||||
int len;
|
||||
|
||||
len = strlen(name);
|
||||
if (interpret_nth_last_branch(name, &ref) != len) {
|
||||
strbuf_reset(&ref);
|
||||
strbuf_add(&ref, name, len);
|
||||
}
|
||||
strbuf_splice(&ref, 0, 0, "refs/heads/", 11);
|
||||
|
||||
if (check_ref_format(ref.buf))
|
||||
if (strbuf_check_branch_ref(&ref, name))
|
||||
die("'%s' is not a valid branch name.", name);
|
||||
|
||||
if (resolve_ref(ref.buf, sha1, 1, NULL)) {
|
||||
|
||||
27
builtin-bisect--helper.c
Normal file
27
builtin-bisect--helper.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "parse-options.h"
|
||||
#include "bisect.h"
|
||||
|
||||
static const char * const git_bisect_helper_usage[] = {
|
||||
"git bisect--helper --next-vars",
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int next_vars = 0;
|
||||
struct option options[] = {
|
||||
OPT_BOOLEAN(0, "next-vars", &next_vars,
|
||||
"output next bisect step variables"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0);
|
||||
|
||||
if (!next_vars)
|
||||
usage_with_options(git_bisect_helper_usage, options);
|
||||
|
||||
/* next-vars */
|
||||
return bisect_next_vars(prefix);
|
||||
}
|
||||
@@ -121,11 +121,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||
die("Couldn't look up commit object for HEAD");
|
||||
}
|
||||
for (i = 0; i < argc; i++, strbuf_release(&bname)) {
|
||||
int len = strlen(argv[i]);
|
||||
|
||||
if (interpret_nth_last_branch(argv[i], &bname) != len)
|
||||
strbuf_add(&bname, argv[i], len);
|
||||
|
||||
strbuf_branchname(&bname, argv[i]);
|
||||
if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
|
||||
error("Cannot delete the branch '%s' "
|
||||
"which you are currently on.", bname.buf);
|
||||
@@ -305,19 +301,30 @@ static int ref_cmp(const void *r1, const void *r2)
|
||||
return strcmp(c1->name, c2->name);
|
||||
}
|
||||
|
||||
static void fill_tracking_info(struct strbuf *stat, const char *branch_name)
|
||||
static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
|
||||
int show_upstream_ref)
|
||||
{
|
||||
int ours, theirs;
|
||||
struct branch *branch = branch_get(branch_name);
|
||||
|
||||
if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
|
||||
if (!stat_tracking_info(branch, &ours, &theirs)) {
|
||||
if (branch && branch->merge && branch->merge[0]->dst &&
|
||||
show_upstream_ref)
|
||||
strbuf_addf(stat, "[%s] ",
|
||||
shorten_unambiguous_ref(branch->merge[0]->dst));
|
||||
return;
|
||||
}
|
||||
|
||||
strbuf_addch(stat, '[');
|
||||
if (show_upstream_ref)
|
||||
strbuf_addf(stat, "%s: ",
|
||||
shorten_unambiguous_ref(branch->merge[0]->dst));
|
||||
if (!ours)
|
||||
strbuf_addf(stat, "[behind %d] ", theirs);
|
||||
strbuf_addf(stat, "behind %d] ", theirs);
|
||||
else if (!theirs)
|
||||
strbuf_addf(stat, "[ahead %d] ", ours);
|
||||
strbuf_addf(stat, "ahead %d] ", ours);
|
||||
else
|
||||
strbuf_addf(stat, "[ahead %d, behind %d] ", ours, theirs);
|
||||
strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
|
||||
}
|
||||
|
||||
static int matches_merge_filter(struct commit *commit)
|
||||
@@ -383,7 +390,7 @@ 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, verbose > 1);
|
||||
|
||||
strbuf_addf(&out, " %s %s%s",
|
||||
find_unique_abbrev(item->commit->object.sha1, abbrev),
|
||||
@@ -468,22 +475,27 @@ static void rename_branch(const char *oldname, const char *newname, int force)
|
||||
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
|
||||
unsigned char sha1[20];
|
||||
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
|
||||
int recovery = 0;
|
||||
|
||||
if (!oldname)
|
||||
die("cannot rename the current branch while not on any.");
|
||||
|
||||
strbuf_addf(&oldref, "refs/heads/%s", oldname);
|
||||
if (strbuf_check_branch_ref(&oldref, oldname)) {
|
||||
/*
|
||||
* Bad name --- this could be an attempt to rename a
|
||||
* ref that we used to allow to be created by accident.
|
||||
*/
|
||||
if (resolve_ref(oldref.buf, sha1, 1, NULL))
|
||||
recovery = 1;
|
||||
else
|
||||
die("Invalid branch name: '%s'", oldname);
|
||||
}
|
||||
|
||||
if (check_ref_format(oldref.buf))
|
||||
die("Invalid branch name: %s", oldref.buf);
|
||||
|
||||
strbuf_addf(&newref, "refs/heads/%s", newname);
|
||||
|
||||
if (check_ref_format(newref.buf))
|
||||
die("Invalid branch name: %s", newref.buf);
|
||||
if (strbuf_check_branch_ref(&newref, newname))
|
||||
die("Invalid branch name: '%s'", newname);
|
||||
|
||||
if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
|
||||
die("A branch named '%s' already exists.", newname);
|
||||
die("A branch named '%s' already exists.", newref.buf + 11);
|
||||
|
||||
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
|
||||
oldref.buf, newref.buf);
|
||||
@@ -492,6 +504,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
|
||||
die("Branch rename failed");
|
||||
strbuf_release(&logmsg);
|
||||
|
||||
if (recovery)
|
||||
warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
|
||||
|
||||
/* no need to pass logmsg here as HEAD didn't really move */
|
||||
if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
|
||||
die("Branch renamed to %s, but HEAD is not updated!", newname);
|
||||
|
||||
@@ -5,9 +5,18 @@
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "builtin.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
if (argc == 3 && !strcmp(argv[1], "--branch")) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
if (strbuf_check_branch_ref(&sb, argv[2]))
|
||||
die("'%s' is not a valid branch name", argv[2]);
|
||||
printf("%s\n", sb.buf + 11);
|
||||
exit(0);
|
||||
}
|
||||
if (argc != 2)
|
||||
usage("git check-ref-format refname");
|
||||
return !!check_ref_format(argv[1]);
|
||||
|
||||
@@ -53,9 +53,6 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
|
||||
int len;
|
||||
struct cache_entry *ce;
|
||||
|
||||
if (S_ISGITLINK(mode))
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(mode))
|
||||
return READ_TREE_RECURSIVE;
|
||||
|
||||
@@ -353,16 +350,11 @@ struct branch_info {
|
||||
static void setup_branch_path(struct branch_info *branch)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret;
|
||||
|
||||
if ((ret = interpret_nth_last_branch(branch->name, &buf))
|
||||
&& ret == strlen(branch->name)) {
|
||||
strbuf_branchname(&buf, branch->name);
|
||||
if (strcmp(buf.buf, branch->name))
|
||||
branch->name = xstrdup(buf.buf);
|
||||
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
|
||||
} else {
|
||||
strbuf_addstr(&buf, "refs/heads/");
|
||||
strbuf_addstr(&buf, branch->name);
|
||||
}
|
||||
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
|
||||
branch->path = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
@@ -503,10 +495,10 @@ static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
create_symref("HEAD", new->path, msg.buf);
|
||||
if (!opts->quiet) {
|
||||
if (old->path && !strcmp(new->path, old->path))
|
||||
fprintf(stderr, "Already on \"%s\"\n",
|
||||
fprintf(stderr, "Already on '%s'\n",
|
||||
new->name);
|
||||
else
|
||||
fprintf(stderr, "Switched to%s branch \"%s\"\n",
|
||||
fprintf(stderr, "Switched to%s branch '%s'\n",
|
||||
opts->new_branch ? " a new" : "",
|
||||
new->name);
|
||||
}
|
||||
@@ -515,7 +507,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
REF_NODEREF, DIE_ON_ERR);
|
||||
if (!opts->quiet) {
|
||||
if (old->path)
|
||||
fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b <new_branch_name>\n", new->name);
|
||||
fprintf(stderr, "Note: moving to '%s' which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b <new_branch_name>\n", new->name);
|
||||
describe_detached_head("HEAD is now at", new->commit);
|
||||
}
|
||||
}
|
||||
@@ -738,12 +730,11 @@ no_reference:
|
||||
|
||||
if (opts.new_branch) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addstr(&buf, "refs/heads/");
|
||||
strbuf_addstr(&buf, opts.new_branch);
|
||||
if (strbuf_check_branch_ref(&buf, opts.new_branch))
|
||||
die("git checkout: we do not like '%s' as a branch name.",
|
||||
opts.new_branch);
|
||||
if (!get_sha1(buf.buf, rev))
|
||||
die("git checkout: branch %s already exists", opts.new_branch);
|
||||
if (check_ref_format(buf.buf))
|
||||
die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
|
||||
@@ -224,7 +224,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
|
||||
const char **pathspec = NULL;
|
||||
|
||||
if (interactive) {
|
||||
interactive_add(argc, argv, prefix);
|
||||
if (interactive_add(argc, argv, prefix) != 0)
|
||||
die("interactive add failed");
|
||||
if (read_cache_preload(NULL) < 0)
|
||||
die("index file corrupt");
|
||||
commit_style = COMMIT_AS_IS;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "blob.h"
|
||||
#include "quote.h"
|
||||
#include "parse-options.h"
|
||||
#include "remote.h"
|
||||
|
||||
/* Quoting styles */
|
||||
#define QUOTE_NONE 0
|
||||
@@ -66,6 +67,7 @@ static struct {
|
||||
{ "subject" },
|
||||
{ "body" },
|
||||
{ "contents" },
|
||||
{ "upstream" },
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -543,109 +545,6 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* generate a format suitable for scanf from a ref_rev_parse_rules
|
||||
* rule, that is replace the "%.*s" spec with a "%s" spec
|
||||
*/
|
||||
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
|
||||
{
|
||||
char *spec;
|
||||
|
||||
spec = strstr(rule, "%.*s");
|
||||
if (!spec || strstr(spec + 4, "%.*s"))
|
||||
die("invalid rule in ref_rev_parse_rules: %s", rule);
|
||||
|
||||
/* copy all until spec */
|
||||
strncpy(scanf_fmt, rule, spec - rule);
|
||||
scanf_fmt[spec - rule] = '\0';
|
||||
/* copy new spec */
|
||||
strcat(scanf_fmt, "%s");
|
||||
/* copy remaining rule */
|
||||
strcat(scanf_fmt, spec + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shorten the refname to an non-ambiguous form
|
||||
*/
|
||||
static char *get_short_ref(struct refinfo *ref)
|
||||
{
|
||||
int i;
|
||||
static char **scanf_fmts;
|
||||
static int nr_rules;
|
||||
char *short_name;
|
||||
|
||||
/* pre generate scanf formats from ref_rev_parse_rules[] */
|
||||
if (!nr_rules) {
|
||||
size_t total_len = 0;
|
||||
|
||||
/* the rule list is NULL terminated, count them first */
|
||||
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
|
||||
/* no +1 because strlen("%s") < strlen("%.*s") */
|
||||
total_len += strlen(ref_rev_parse_rules[nr_rules]);
|
||||
|
||||
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
|
||||
|
||||
total_len = 0;
|
||||
for (i = 0; i < nr_rules; i++) {
|
||||
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
|
||||
+ total_len;
|
||||
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
|
||||
total_len += strlen(ref_rev_parse_rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* bail out if there are no rules */
|
||||
if (!nr_rules)
|
||||
return ref->refname;
|
||||
|
||||
/* buffer for scanf result, at most ref->refname must fit */
|
||||
short_name = xstrdup(ref->refname);
|
||||
|
||||
/* skip first rule, it will always match */
|
||||
for (i = nr_rules - 1; i > 0 ; --i) {
|
||||
int j;
|
||||
int short_name_len;
|
||||
|
||||
if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
|
||||
continue;
|
||||
|
||||
short_name_len = strlen(short_name);
|
||||
|
||||
/*
|
||||
* check if the short name resolves to a valid ref,
|
||||
* but use only rules prior to the matched one
|
||||
*/
|
||||
for (j = 0; j < i; j++) {
|
||||
const char *rule = ref_rev_parse_rules[j];
|
||||
unsigned char short_objectname[20];
|
||||
char refname[PATH_MAX];
|
||||
|
||||
/*
|
||||
* the short name is ambiguous, if it resolves
|
||||
* (with this previous rule) to a valid ref
|
||||
* read_ref() returns 0 on success
|
||||
*/
|
||||
mksnpath(refname, sizeof(refname),
|
||||
rule, short_name_len, short_name);
|
||||
if (!read_ref(refname, short_objectname))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* short name is non-ambiguous if all previous rules
|
||||
* haven't resolved to a valid ref
|
||||
*/
|
||||
if (j == i)
|
||||
return short_name;
|
||||
}
|
||||
|
||||
free(short_name);
|
||||
return ref->refname;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse the object referred by ref, and grab needed value.
|
||||
*/
|
||||
@@ -672,32 +571,49 @@ static void populate_value(struct refinfo *ref)
|
||||
const char *name = used_atom[i];
|
||||
struct atom_value *v = &ref->value[i];
|
||||
int deref = 0;
|
||||
const char *refname;
|
||||
const char *formatp;
|
||||
|
||||
if (*name == '*') {
|
||||
deref = 1;
|
||||
name++;
|
||||
}
|
||||
if (!prefixcmp(name, "refname")) {
|
||||
const char *formatp = strchr(name, ':');
|
||||
const char *refname = ref->refname;
|
||||
|
||||
/* look for "short" refname format */
|
||||
if (formatp) {
|
||||
formatp++;
|
||||
if (!strcmp(formatp, "short"))
|
||||
refname = get_short_ref(ref);
|
||||
else
|
||||
die("unknown refname format %s",
|
||||
formatp);
|
||||
}
|
||||
if (!prefixcmp(name, "refname"))
|
||||
refname = ref->refname;
|
||||
else if(!prefixcmp(name, "upstream")) {
|
||||
struct branch *branch;
|
||||
/* only local branches may have an upstream */
|
||||
if (prefixcmp(ref->refname, "refs/heads/"))
|
||||
continue;
|
||||
branch = branch_get(ref->refname + 11);
|
||||
|
||||
if (!deref)
|
||||
v->s = refname;
|
||||
else {
|
||||
int len = strlen(refname);
|
||||
char *s = xmalloc(len + 4);
|
||||
sprintf(s, "%s^{}", refname);
|
||||
v->s = s;
|
||||
}
|
||||
if (!branch || !branch->merge || !branch->merge[0] ||
|
||||
!branch->merge[0]->dst)
|
||||
continue;
|
||||
refname = branch->merge[0]->dst;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
formatp = strchr(name, ':');
|
||||
/* look for "short" refname format */
|
||||
if (formatp) {
|
||||
formatp++;
|
||||
if (!strcmp(formatp, "short"))
|
||||
refname = shorten_unambiguous_ref(refname);
|
||||
else
|
||||
die("unknown %.*s format %s",
|
||||
(int)(formatp - name), name, formatp);
|
||||
}
|
||||
|
||||
if (!deref)
|
||||
v->s = refname;
|
||||
else {
|
||||
int len = strlen(refname);
|
||||
char *s = xmalloc(len + 4);
|
||||
sprintf(s, "%s^{}", refname);
|
||||
v->s = s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -194,6 +194,8 @@ static int create_default_files(const char *template_path)
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
is_bare_repository_cfg = init_is_bare_repository;
|
||||
|
||||
/* reading existing config may have overwrote it */
|
||||
if (init_shared_repository != -1)
|
||||
shared_repository = init_shared_repository;
|
||||
|
||||
@@ -312,12 +314,15 @@ int init_db(const char *template_dir, unsigned int flags)
|
||||
* and compatibility values for PERM_GROUP and
|
||||
* PERM_EVERYBODY.
|
||||
*/
|
||||
if (shared_repository == PERM_GROUP)
|
||||
if (shared_repository < 0)
|
||||
/* force to the mode value */
|
||||
sprintf(buf, "0%o", -shared_repository);
|
||||
else if (shared_repository == PERM_GROUP)
|
||||
sprintf(buf, "%d", OLD_PERM_GROUP);
|
||||
else if (shared_repository == PERM_EVERYBODY)
|
||||
sprintf(buf, "%d", OLD_PERM_EVERYBODY);
|
||||
else
|
||||
sprintf(buf, "0%o", shared_repository);
|
||||
die("oops");
|
||||
git_config_set("core.sharedrepository", buf);
|
||||
git_config_set("receive.denyNonFastforwards", "true");
|
||||
}
|
||||
@@ -397,6 +402,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
|
||||
usage(init_db_usage);
|
||||
}
|
||||
|
||||
if (init_shared_repository != -1)
|
||||
shared_repository = init_shared_repository;
|
||||
|
||||
/*
|
||||
* GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
|
||||
* without --bare. Catch the error early.
|
||||
|
||||
154
builtin-log.c
154
builtin-log.c
@@ -417,13 +417,6 @@ int cmd_log(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
/* format-patch */
|
||||
#define FORMAT_PATCH_NAME_MAX 64
|
||||
|
||||
static int istitlechar(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
static const char *fmt_patch_suffix = ".patch";
|
||||
static int numbered = 0;
|
||||
@@ -465,6 +458,7 @@ static void add_header(const char *value)
|
||||
#define THREAD_SHALLOW 1
|
||||
#define THREAD_DEEP 2
|
||||
static int thread = 0;
|
||||
static int do_signoff = 0;
|
||||
|
||||
static int git_format_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
@@ -514,96 +508,41 @@ static int git_format_config(const char *var, const char *value, void *cb)
|
||||
thread = git_config_bool(var, value) && THREAD_SHALLOW;
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "format.signoff")) {
|
||||
do_signoff = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_log_config(var, value, cb);
|
||||
}
|
||||
|
||||
|
||||
static const char *get_oneline_for_filename(struct commit *commit,
|
||||
int keep_subject)
|
||||
{
|
||||
static char filename[PATH_MAX];
|
||||
char *sol;
|
||||
int len = 0;
|
||||
int suffix_len = strlen(fmt_patch_suffix) + 1;
|
||||
|
||||
sol = strstr(commit->buffer, "\n\n");
|
||||
if (!sol)
|
||||
filename[0] = '\0';
|
||||
else {
|
||||
int j, space = 0;
|
||||
|
||||
sol += 2;
|
||||
/* strip [PATCH] or [PATCH blabla] */
|
||||
if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
|
||||
char *eos = strchr(sol + 6, ']');
|
||||
if (eos) {
|
||||
while (isspace(*eos))
|
||||
eos++;
|
||||
sol = eos;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0;
|
||||
j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
|
||||
len < sizeof(filename) - suffix_len &&
|
||||
sol[j] && sol[j] != '\n';
|
||||
j++) {
|
||||
if (istitlechar(sol[j])) {
|
||||
if (space) {
|
||||
filename[len++] = '-';
|
||||
space = 0;
|
||||
}
|
||||
filename[len++] = sol[j];
|
||||
if (sol[j] == '.')
|
||||
while (sol[j + 1] == '.')
|
||||
j++;
|
||||
} else
|
||||
space = 1;
|
||||
}
|
||||
while (filename[len - 1] == '.'
|
||||
|| filename[len - 1] == '-')
|
||||
len--;
|
||||
filename[len] = '\0';
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
static FILE *realstdout = NULL;
|
||||
static const char *output_directory = NULL;
|
||||
static int outdir_offset;
|
||||
|
||||
static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev)
|
||||
static int reopen_stdout(struct commit *commit, struct rev_info *rev)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
int len = 0;
|
||||
struct strbuf filename = STRBUF_INIT;
|
||||
int suffix_len = strlen(fmt_patch_suffix) + 1;
|
||||
|
||||
if (output_directory) {
|
||||
len = snprintf(filename, sizeof(filename), "%s",
|
||||
output_directory);
|
||||
if (len >=
|
||||
sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
|
||||
strbuf_addstr(&filename, output_directory);
|
||||
if (filename.len >=
|
||||
PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
|
||||
return error("name of output directory is too long");
|
||||
if (filename[len - 1] != '/')
|
||||
filename[len++] = '/';
|
||||
if (filename.buf[filename.len - 1] != '/')
|
||||
strbuf_addch(&filename, '/');
|
||||
}
|
||||
|
||||
if (!oneline)
|
||||
len += sprintf(filename + len, "%d", nr);
|
||||
else {
|
||||
len += sprintf(filename + len, "%04d-", nr);
|
||||
len += snprintf(filename + len, sizeof(filename) - len - 1
|
||||
- suffix_len, "%s", oneline);
|
||||
strcpy(filename + len, fmt_patch_suffix);
|
||||
}
|
||||
get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
|
||||
|
||||
if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
|
||||
fprintf(realstdout, "%s\n", filename + outdir_offset);
|
||||
fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
|
||||
|
||||
if (freopen(filename, "w", stdout) == NULL)
|
||||
return error("Cannot open patch file %s",filename);
|
||||
if (freopen(filename.buf, "w", stdout) == NULL)
|
||||
return error("Cannot open patch file %s", filename.buf);
|
||||
|
||||
strbuf_release(&filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -673,7 +612,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
|
||||
int nr, struct commit **list, struct commit *head)
|
||||
{
|
||||
const char *committer;
|
||||
char *head_sha1;
|
||||
const char *subject_start = NULL;
|
||||
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
|
||||
const char *msg;
|
||||
@@ -684,21 +622,41 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
|
||||
const char *encoding = "utf-8";
|
||||
struct diff_options opts;
|
||||
int need_8bit_cte = 0;
|
||||
struct commit *commit = NULL;
|
||||
|
||||
if (rev->commit_format != CMIT_FMT_EMAIL)
|
||||
die("Cover letter needs email format");
|
||||
|
||||
if (!use_stdout && reopen_stdout(numbered_files ?
|
||||
NULL : "cover-letter", 0, rev))
|
||||
committer = git_committer_info(0);
|
||||
|
||||
if (!numbered_files) {
|
||||
/*
|
||||
* We fake a commit for the cover letter so we get the filename
|
||||
* desired.
|
||||
*/
|
||||
commit = xcalloc(1, sizeof(*commit));
|
||||
commit->buffer = xmalloc(400);
|
||||
snprintf(commit->buffer, 400,
|
||||
"tree 0000000000000000000000000000000000000000\n"
|
||||
"parent %s\n"
|
||||
"author %s\n"
|
||||
"committer %s\n\n"
|
||||
"cover letter\n",
|
||||
sha1_to_hex(head->object.sha1), committer, committer);
|
||||
}
|
||||
|
||||
if (!use_stdout && reopen_stdout(commit, rev))
|
||||
return;
|
||||
|
||||
head_sha1 = sha1_to_hex(head->object.sha1);
|
||||
if (commit) {
|
||||
|
||||
log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
|
||||
free(commit->buffer);
|
||||
free(commit);
|
||||
}
|
||||
|
||||
log_write_email_headers(rev, head, &subject_start, &extra_headers,
|
||||
&need_8bit_cte);
|
||||
|
||||
committer = git_committer_info(0);
|
||||
|
||||
msg = body;
|
||||
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
|
||||
encoding);
|
||||
@@ -865,13 +823,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
else if (!strcmp(argv[i], "--signoff") ||
|
||||
!strcmp(argv[i], "-s")) {
|
||||
const char *committer;
|
||||
const char *endpos;
|
||||
committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
|
||||
endpos = strchr(committer, '>');
|
||||
if (!endpos)
|
||||
die("bogus committer info %s", committer);
|
||||
add_signoff = xmemdupz(committer, endpos - committer + 1);
|
||||
do_signoff = 1;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--attach")) {
|
||||
rev.mime_boundary = git_version_string;
|
||||
@@ -925,6 +877,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
argc = j;
|
||||
|
||||
if (do_signoff) {
|
||||
const char *committer;
|
||||
const char *endpos;
|
||||
committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
|
||||
endpos = strchr(committer, '>');
|
||||
if (!endpos)
|
||||
die("bogus committer info %s", committer);
|
||||
add_signoff = xmemdupz(committer, endpos - committer + 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < extra_hdr_nr; i++) {
|
||||
strbuf_addstr(&buf, extra_hdr[i]);
|
||||
strbuf_addch(&buf, '\n');
|
||||
@@ -1058,6 +1020,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
const char *msgid = clean_message_id(in_reply_to);
|
||||
string_list_append(msgid, rev.ref_message_ids);
|
||||
}
|
||||
rev.numbered_files = numbered_files;
|
||||
rev.patch_suffix = fmt_patch_suffix;
|
||||
if (cover_letter) {
|
||||
if (thread)
|
||||
gen_message_id(&rev, "cover");
|
||||
@@ -1106,9 +1070,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
if (!use_stdout && reopen_stdout(numbered_files ? NULL :
|
||||
get_oneline_for_filename(commit, keep_subject),
|
||||
rev.nr, &rev))
|
||||
|
||||
if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
|
||||
&rev))
|
||||
die("Failed to create output files");
|
||||
shown = log_tree_commit(&rev, commit);
|
||||
free(commit->buffer);
|
||||
|
||||
@@ -360,9 +360,8 @@ static void merge_name(const char *remote, struct strbuf *msg)
|
||||
const char *ptr;
|
||||
int len, early;
|
||||
|
||||
len = strlen(remote);
|
||||
if (interpret_nth_last_branch(remote, &bname) == len)
|
||||
remote = bname.buf;
|
||||
strbuf_branchname(&bname, remote);
|
||||
remote = bname.buf;
|
||||
|
||||
memset(branch_head, 0, sizeof(branch_head));
|
||||
remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
|
||||
|
||||
@@ -1612,7 +1612,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
|
||||
return;
|
||||
}
|
||||
if (progress > pack_to_stdout)
|
||||
fprintf(stderr, "Delta compression using %d threads.\n",
|
||||
fprintf(stderr, "Delta compression using up to %d threads.\n",
|
||||
delta_search_threads);
|
||||
|
||||
/* Partition the work amongst work threads. */
|
||||
@@ -1901,17 +1901,19 @@ static void read_object_list_from_stdin(void)
|
||||
|
||||
#define OBJECT_ADDED (1u<<20)
|
||||
|
||||
static void show_commit(struct commit *commit)
|
||||
static void show_commit(struct commit *commit, void *data)
|
||||
{
|
||||
add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
|
||||
commit->object.flags |= OBJECT_ADDED;
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
static void show_object(struct object_array_entry *p, void *data)
|
||||
{
|
||||
add_preferred_base_object(p->name);
|
||||
add_object_entry(p->item->sha1, p->item->type, p->name, 0);
|
||||
p->item->flags |= OBJECT_ADDED;
|
||||
free((char *)p->name);
|
||||
p->name = NULL;
|
||||
}
|
||||
|
||||
static void show_edge(struct commit *commit)
|
||||
@@ -2071,7 +2073,7 @@ static void get_object_list(int ac, const char **av)
|
||||
if (prepare_revision_walk(&revs))
|
||||
die("revision walk setup failed");
|
||||
mark_edges_uninteresting(revs.commits, &revs, show_edge);
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
traverse_commit_list(&revs, show_commit, show_object, NULL);
|
||||
|
||||
if (keep_unreachable)
|
||||
add_objects_in_unpacked_packs(&revs);
|
||||
|
||||
@@ -52,6 +52,7 @@ struct collect_reflog_cb {
|
||||
|
||||
#define INCOMPLETE (1u<<10)
|
||||
#define STUDYING (1u<<11)
|
||||
#define REACHABLE (1u<<12)
|
||||
|
||||
static int tree_is_complete(const unsigned char *sha1)
|
||||
{
|
||||
@@ -209,6 +210,70 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
|
||||
{
|
||||
/*
|
||||
* We may or may not have the commit yet - if not, look it
|
||||
* up using the supplied sha1.
|
||||
*/
|
||||
if (!commit) {
|
||||
if (is_null_sha1(sha1))
|
||||
return 0;
|
||||
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
|
||||
/* Not a commit -- keep it */
|
||||
if (!commit)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reachable from the current ref? Don't prune. */
|
||||
if (commit->object.flags & REACHABLE)
|
||||
return 0;
|
||||
if (in_merge_bases(commit, &cb->ref_commit, 1))
|
||||
return 0;
|
||||
|
||||
/* We can't reach it - prune it. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mark_reachable(struct commit *commit, unsigned long expire_limit)
|
||||
{
|
||||
/*
|
||||
* We need to compute if commit on either side of an reflog
|
||||
* entry is reachable from the tip of the ref for all entries.
|
||||
* Mark commits that are reachable from the tip down to the
|
||||
* time threashold first; we know a commit marked thusly is
|
||||
* reachable from the tip without running in_merge_bases()
|
||||
* at all.
|
||||
*/
|
||||
struct commit_list *pending = NULL;
|
||||
|
||||
commit_list_insert(commit, &pending);
|
||||
while (pending) {
|
||||
struct commit_list *entry = pending;
|
||||
struct commit_list *parent;
|
||||
pending = entry->next;
|
||||
commit = entry->item;
|
||||
free(entry);
|
||||
if (commit->object.flags & REACHABLE)
|
||||
continue;
|
||||
if (parse_commit(commit))
|
||||
continue;
|
||||
commit->object.flags |= REACHABLE;
|
||||
if (commit->date < expire_limit)
|
||||
continue;
|
||||
parent = commit->parents;
|
||||
while (parent) {
|
||||
commit = parent->item;
|
||||
parent = parent->next;
|
||||
if (commit->object.flags & REACHABLE)
|
||||
continue;
|
||||
commit_list_insert(commit, &pending);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
const char *email, unsigned long timestamp, int tz,
|
||||
const char *message, void *cb_data)
|
||||
@@ -230,12 +295,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
if (timestamp < cb->cmd->expire_unreachable) {
|
||||
if (!cb->ref_commit)
|
||||
goto prune;
|
||||
if (!old && !is_null_sha1(osha1))
|
||||
old = lookup_commit_reference_gently(osha1, 1);
|
||||
if (!new && !is_null_sha1(nsha1))
|
||||
new = lookup_commit_reference_gently(nsha1, 1);
|
||||
if ((old && !in_merge_bases(old, &cb->ref_commit, 1)) ||
|
||||
(new && !in_merge_bases(new, &cb->ref_commit, 1)))
|
||||
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
|
||||
goto prune;
|
||||
}
|
||||
|
||||
@@ -288,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
|
||||
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
|
||||
cb.ref = ref;
|
||||
cb.cmd = cmd;
|
||||
if (cb.ref_commit)
|
||||
mark_reachable(cb.ref_commit, cmd->expire_total);
|
||||
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
|
||||
if (cb.ref_commit)
|
||||
clear_commit_marks(cb.ref_commit, REACHABLE);
|
||||
finish:
|
||||
if (cb.newlog) {
|
||||
if (fclose(cb.newlog)) {
|
||||
|
||||
@@ -15,7 +15,7 @@ static const char * const builtin_remote_usage[] = {
|
||||
"git remote set-head <name> [-a | -d | <branch>]",
|
||||
"git remote show [-n] <name>",
|
||||
"git remote prune [-n | --dry-run] <name>",
|
||||
"git remote [-v | --verbose] update [group]",
|
||||
"git remote [-v | --verbose] update [-p | --prune] [group]",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ static const char * const builtin_remote_usage[] = {
|
||||
static int verbose;
|
||||
|
||||
static int show_all(void);
|
||||
static int prune_remote(const char *remote, int dry_run);
|
||||
|
||||
static inline int postfixcmp(const char *string, const char *postfix)
|
||||
{
|
||||
@@ -1128,46 +1129,49 @@ static int prune(int argc, const char **argv)
|
||||
OPT__DRY_RUN(&dry_run),
|
||||
OPT_END()
|
||||
};
|
||||
struct ref_states states;
|
||||
const char *dangling_msg;
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
|
||||
if (argc < 1)
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
dangling_msg = (dry_run
|
||||
? " %s will become dangling!\n"
|
||||
: " %s has become dangling!\n");
|
||||
for (; argc; argc--, argv++)
|
||||
result |= prune_remote(*argv, dry_run);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int prune_remote(const char *remote, int dry_run)
|
||||
{
|
||||
int result = 0, i;
|
||||
struct ref_states states;
|
||||
const char *dangling_msg = dry_run
|
||||
? " %s will become dangling!\n"
|
||||
: " %s has become dangling!\n";
|
||||
|
||||
memset(&states, 0, sizeof(states));
|
||||
for (; argc; argc--, argv++) {
|
||||
int i;
|
||||
get_remote_ref_states(remote, &states, GET_REF_STATES);
|
||||
|
||||
get_remote_ref_states(*argv, &states, GET_REF_STATES);
|
||||
|
||||
if (states.stale.nr) {
|
||||
printf("Pruning %s\n", *argv);
|
||||
printf("URL: %s\n",
|
||||
states.remote->url_nr
|
||||
? states.remote->url[0]
|
||||
: "(no URL)");
|
||||
}
|
||||
|
||||
for (i = 0; i < states.stale.nr; i++) {
|
||||
const char *refname = states.stale.items[i].util;
|
||||
|
||||
if (!dry_run)
|
||||
result |= delete_ref(refname, NULL, 0);
|
||||
|
||||
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
|
||||
abbrev_ref(refname, "refs/remotes/"));
|
||||
warn_dangling_symref(dangling_msg, refname);
|
||||
}
|
||||
|
||||
free_remote_ref_states(&states);
|
||||
if (states.stale.nr) {
|
||||
printf("Pruning %s\n", remote);
|
||||
printf("URL: %s\n",
|
||||
states.remote->url_nr
|
||||
? states.remote->url[0]
|
||||
: "(no URL)");
|
||||
}
|
||||
|
||||
for (i = 0; i < states.stale.nr; i++) {
|
||||
const char *refname = states.stale.items[i].util;
|
||||
|
||||
if (!dry_run)
|
||||
result |= delete_ref(refname, NULL, 0);
|
||||
|
||||
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
|
||||
abbrev_ref(refname, "refs/remotes/"));
|
||||
warn_dangling_symref(dangling_msg, refname);
|
||||
}
|
||||
|
||||
free_remote_ref_states(&states);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1184,16 +1188,18 @@ struct remote_group {
|
||||
struct string_list *list;
|
||||
} remote_group;
|
||||
|
||||
static int get_remote_group(const char *key, const char *value, void *cb)
|
||||
static int get_remote_group(const char *key, const char *value, void *num_hits)
|
||||
{
|
||||
if (!prefixcmp(key, "remotes.") &&
|
||||
!strcmp(key + 8, remote_group.name)) {
|
||||
/* split list by white space */
|
||||
int space = strcspn(value, " \t\n");
|
||||
while (*value) {
|
||||
if (space > 1)
|
||||
if (space > 1) {
|
||||
string_list_append(xstrndup(value, space),
|
||||
remote_group.list);
|
||||
++*((int *)num_hits);
|
||||
}
|
||||
value += space + (value[space] != '\0');
|
||||
space = strcspn(value, " \t\n");
|
||||
}
|
||||
@@ -1204,10 +1210,18 @@ static int get_remote_group(const char *key, const char *value, void *cb)
|
||||
|
||||
static int update(int argc, const char **argv)
|
||||
{
|
||||
int i, result = 0;
|
||||
int i, result = 0, prune = 0;
|
||||
struct string_list list = { NULL, 0, 0, 0 };
|
||||
static const char *default_argv[] = { NULL, "default", NULL };
|
||||
struct option options[] = {
|
||||
OPT_GROUP("update specific options"),
|
||||
OPT_BOOLEAN('p', "prune", &prune,
|
||||
"prune remotes after fecthing"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage,
|
||||
PARSE_OPT_KEEP_ARGV0);
|
||||
if (argc < 2) {
|
||||
argc = 2;
|
||||
argv = default_argv;
|
||||
@@ -1215,15 +1229,28 @@ static int update(int argc, const char **argv)
|
||||
|
||||
remote_group.list = &list;
|
||||
for (i = 1; i < argc; i++) {
|
||||
int groups_found = 0;
|
||||
remote_group.name = argv[i];
|
||||
result = git_config(get_remote_group, NULL);
|
||||
result = git_config(get_remote_group, &groups_found);
|
||||
if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
|
||||
struct remote *remote;
|
||||
if (!remote_is_configured(argv[i]))
|
||||
die("No such remote or remote group: %s",
|
||||
argv[i]);
|
||||
remote = remote_get(argv[i]);
|
||||
string_list_append(remote->name, remote_group.list);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default"))
|
||||
result = for_each_remote(get_one_remote_for_update, &list);
|
||||
|
||||
for (i = 0; i < list.nr; i++)
|
||||
result |= fetch_remote(list.items[i].string);
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
int err = fetch_remote(list.items[i].string);
|
||||
result |= err;
|
||||
if (!err && prune)
|
||||
result |= prune_remote(list.items[i].string, 0);
|
||||
}
|
||||
|
||||
/* all names were strdup()ed or strndup()ed */
|
||||
list.strdup_strings = 1;
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "tag.h"
|
||||
#include "commit.h"
|
||||
#include "tree.h"
|
||||
#include "blob.h"
|
||||
#include "tree-walk.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "list-objects.h"
|
||||
#include "builtin.h"
|
||||
#include "log-tree.h"
|
||||
#include "graph.h"
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
#define COUNTED (1u<<16)
|
||||
#include "bisect.h"
|
||||
|
||||
static const char rev_list_usage[] =
|
||||
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
|
||||
@@ -50,73 +42,69 @@ static const char rev_list_usage[] =
|
||||
" --bisect-all"
|
||||
;
|
||||
|
||||
static struct rev_info revs;
|
||||
|
||||
static int bisect_list;
|
||||
static int show_timestamp;
|
||||
static int hdr_termination;
|
||||
static const char *header_prefix;
|
||||
|
||||
static void finish_commit(struct commit *commit);
|
||||
static void show_commit(struct commit *commit)
|
||||
static void finish_commit(struct commit *commit, void *data);
|
||||
static void show_commit(struct commit *commit, void *data)
|
||||
{
|
||||
graph_show_commit(revs.graph);
|
||||
struct rev_list_info *info = data;
|
||||
struct rev_info *revs = info->revs;
|
||||
|
||||
if (show_timestamp)
|
||||
graph_show_commit(revs->graph);
|
||||
|
||||
if (info->show_timestamp)
|
||||
printf("%lu ", commit->date);
|
||||
if (header_prefix)
|
||||
fputs(header_prefix, stdout);
|
||||
if (info->header_prefix)
|
||||
fputs(info->header_prefix, stdout);
|
||||
|
||||
if (!revs.graph) {
|
||||
if (!revs->graph) {
|
||||
if (commit->object.flags & BOUNDARY)
|
||||
putchar('-');
|
||||
else if (commit->object.flags & UNINTERESTING)
|
||||
putchar('^');
|
||||
else if (revs.left_right) {
|
||||
else if (revs->left_right) {
|
||||
if (commit->object.flags & SYMMETRIC_LEFT)
|
||||
putchar('<');
|
||||
else
|
||||
putchar('>');
|
||||
}
|
||||
}
|
||||
if (revs.abbrev_commit && revs.abbrev)
|
||||
fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
|
||||
if (revs->abbrev_commit && revs->abbrev)
|
||||
fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
|
||||
stdout);
|
||||
else
|
||||
fputs(sha1_to_hex(commit->object.sha1), stdout);
|
||||
if (revs.print_parents) {
|
||||
if (revs->print_parents) {
|
||||
struct commit_list *parents = commit->parents;
|
||||
while (parents) {
|
||||
printf(" %s", sha1_to_hex(parents->item->object.sha1));
|
||||
parents = parents->next;
|
||||
}
|
||||
}
|
||||
if (revs.children.name) {
|
||||
if (revs->children.name) {
|
||||
struct commit_list *children;
|
||||
|
||||
children = lookup_decoration(&revs.children, &commit->object);
|
||||
children = lookup_decoration(&revs->children, &commit->object);
|
||||
while (children) {
|
||||
printf(" %s", sha1_to_hex(children->item->object.sha1));
|
||||
children = children->next;
|
||||
}
|
||||
}
|
||||
show_decorations(&revs, commit);
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
show_decorations(revs, commit);
|
||||
if (revs->commit_format == CMIT_FMT_ONELINE)
|
||||
putchar(' ');
|
||||
else
|
||||
putchar('\n');
|
||||
|
||||
if (revs.verbose_header && commit->buffer) {
|
||||
if (revs->verbose_header && commit->buffer) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
pretty_print_commit(revs.commit_format, commit,
|
||||
&buf, revs.abbrev, NULL, NULL,
|
||||
revs.date_mode, 0);
|
||||
if (revs.graph) {
|
||||
pretty_print_commit(revs->commit_format, commit,
|
||||
&buf, revs->abbrev, NULL, NULL,
|
||||
revs->date_mode, 0);
|
||||
if (revs->graph) {
|
||||
if (buf.len) {
|
||||
if (revs.commit_format != CMIT_FMT_ONELINE)
|
||||
graph_show_oneline(revs.graph);
|
||||
if (revs->commit_format != CMIT_FMT_ONELINE)
|
||||
graph_show_oneline(revs->graph);
|
||||
|
||||
graph_show_commit_msg(revs.graph, &buf);
|
||||
graph_show_commit_msg(revs->graph, &buf);
|
||||
|
||||
/*
|
||||
* Add a newline after the commit message.
|
||||
@@ -134,7 +122,7 @@ static void show_commit(struct commit *commit)
|
||||
* format doesn't explicitly end in a newline.)
|
||||
*/
|
||||
if (buf.len && buf.buf[buf.len - 1] == '\n')
|
||||
graph_show_padding(revs.graph);
|
||||
graph_show_padding(revs->graph);
|
||||
putchar('\n');
|
||||
} else {
|
||||
/*
|
||||
@@ -142,23 +130,23 @@ static void show_commit(struct commit *commit)
|
||||
* the rest of the graph output for this
|
||||
* commit.
|
||||
*/
|
||||
if (graph_show_remainder(revs.graph))
|
||||
if (graph_show_remainder(revs->graph))
|
||||
putchar('\n');
|
||||
}
|
||||
} else {
|
||||
if (buf.len)
|
||||
printf("%s%c", buf.buf, hdr_termination);
|
||||
printf("%s%c", buf.buf, info->hdr_termination);
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
} else {
|
||||
if (graph_show_remainder(revs.graph))
|
||||
if (graph_show_remainder(revs->graph))
|
||||
putchar('\n');
|
||||
}
|
||||
maybe_flush_or_die(stdout, "stdout");
|
||||
finish_commit(commit);
|
||||
finish_commit(commit, data);
|
||||
}
|
||||
|
||||
static void finish_commit(struct commit *commit)
|
||||
static void finish_commit(struct commit *commit, void *data)
|
||||
{
|
||||
if (commit->parents) {
|
||||
free_commit_list(commit->parents);
|
||||
@@ -168,20 +156,20 @@ static void finish_commit(struct commit *commit)
|
||||
commit->buffer = NULL;
|
||||
}
|
||||
|
||||
static void finish_object(struct object_array_entry *p)
|
||||
static void finish_object(struct object_array_entry *p, void *data)
|
||||
{
|
||||
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
|
||||
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
static void show_object(struct object_array_entry *p, void *data)
|
||||
{
|
||||
/* An object with name "foo\n0000000..." can be used to
|
||||
* confuse downstream "git pack-objects" very badly.
|
||||
*/
|
||||
const char *ep = strchr(p->name, '\n');
|
||||
|
||||
finish_object(p);
|
||||
finish_object(p, data);
|
||||
if (ep) {
|
||||
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
|
||||
(int) (ep - p->name),
|
||||
@@ -196,384 +184,6 @@ static void show_edge(struct commit *commit)
|
||||
printf("-%s\n", sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a truly stupid algorithm, but it's only
|
||||
* used for bisection, and we just don't care enough.
|
||||
*
|
||||
* We care just barely enough to avoid recursing for
|
||||
* non-merge entries.
|
||||
*/
|
||||
static int count_distance(struct commit_list *entry)
|
||||
{
|
||||
int nr = 0;
|
||||
|
||||
while (entry) {
|
||||
struct commit *commit = entry->item;
|
||||
struct commit_list *p;
|
||||
|
||||
if (commit->object.flags & (UNINTERESTING | COUNTED))
|
||||
break;
|
||||
if (!(commit->object.flags & TREESAME))
|
||||
nr++;
|
||||
commit->object.flags |= COUNTED;
|
||||
p = commit->parents;
|
||||
entry = p;
|
||||
if (p) {
|
||||
p = p->next;
|
||||
while (p) {
|
||||
nr += count_distance(p);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
static void clear_distance(struct commit_list *list)
|
||||
{
|
||||
while (list) {
|
||||
struct commit *commit = list->item;
|
||||
commit->object.flags &= ~COUNTED;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEBUG_BISECT 0
|
||||
|
||||
static inline int weight(struct commit_list *elem)
|
||||
{
|
||||
return *((int*)(elem->item->util));
|
||||
}
|
||||
|
||||
static inline void weight_set(struct commit_list *elem, int weight)
|
||||
{
|
||||
*((int*)(elem->item->util)) = weight;
|
||||
}
|
||||
|
||||
static int count_interesting_parents(struct commit *commit)
|
||||
{
|
||||
struct commit_list *p;
|
||||
int count;
|
||||
|
||||
for (count = 0, p = commit->parents; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline int halfway(struct commit_list *p, int nr)
|
||||
{
|
||||
/*
|
||||
* Don't short-cut something we are not going to return!
|
||||
*/
|
||||
if (p->item->object.flags & TREESAME)
|
||||
return 0;
|
||||
if (DEBUG_BISECT)
|
||||
return 0;
|
||||
/*
|
||||
* 2 and 3 are halfway of 5.
|
||||
* 3 is halfway of 6 but 2 and 4 are not.
|
||||
*/
|
||||
switch (2 * weight(p) - nr) {
|
||||
case -1: case 0: case 1:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if !DEBUG_BISECT
|
||||
#define show_list(a,b,c,d) do { ; } while (0)
|
||||
#else
|
||||
static void show_list(const char *debug, int counted, int nr,
|
||||
struct commit_list *list)
|
||||
{
|
||||
struct commit_list *p;
|
||||
|
||||
fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
|
||||
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *pp;
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *buf = read_sha1_file(commit->object.sha1, &type, &size);
|
||||
char *ep, *sp;
|
||||
|
||||
fprintf(stderr, "%c%c%c ",
|
||||
(flags & TREESAME) ? ' ' : 'T',
|
||||
(flags & UNINTERESTING) ? 'U' : ' ',
|
||||
(flags & COUNTED) ? 'C' : ' ');
|
||||
if (commit->util)
|
||||
fprintf(stderr, "%3d", weight(p));
|
||||
else
|
||||
fprintf(stderr, "---");
|
||||
fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
|
||||
for (pp = commit->parents; pp; pp = pp->next)
|
||||
fprintf(stderr, " %.*s", 8,
|
||||
sha1_to_hex(pp->item->object.sha1));
|
||||
|
||||
sp = strstr(buf, "\n\n");
|
||||
if (sp) {
|
||||
sp += 2;
|
||||
for (ep = sp; *ep && *ep != '\n'; ep++)
|
||||
;
|
||||
fprintf(stderr, " %.*s", (int)(ep - sp), sp);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_BISECT */
|
||||
|
||||
static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p, *best;
|
||||
int best_distance = -1;
|
||||
|
||||
best = list;
|
||||
for (p = list; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
if (distance > best_distance) {
|
||||
best = p;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
struct commit_dist {
|
||||
struct commit *commit;
|
||||
int distance;
|
||||
};
|
||||
|
||||
static int compare_commit_dist(const void *a_, const void *b_)
|
||||
{
|
||||
struct commit_dist *a, *b;
|
||||
|
||||
a = (struct commit_dist *)a_;
|
||||
b = (struct commit_dist *)b_;
|
||||
if (a->distance != b->distance)
|
||||
return b->distance - a->distance; /* desc sort */
|
||||
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
|
||||
}
|
||||
|
||||
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_dist *array = xcalloc(nr, sizeof(*array));
|
||||
int cnt, i;
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (flags & TREESAME)
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
array[cnt].commit = p->item;
|
||||
array[cnt].distance = distance;
|
||||
cnt++;
|
||||
}
|
||||
qsort(array, cnt, sizeof(*array), compare_commit_dist);
|
||||
for (p = list, i = 0; i < cnt; i++) {
|
||||
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
|
||||
struct object *obj = &(array[i].commit->object);
|
||||
|
||||
sprintf(r->name, "dist=%d", array[i].distance);
|
||||
r->next = add_decoration(&name_decoration, obj, r);
|
||||
p->item = array[i].commit;
|
||||
p = p->next;
|
||||
}
|
||||
if (p)
|
||||
p->next = NULL;
|
||||
free(array);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
* reach any tree-changing commits (e.g. just above uninteresting one
|
||||
* but traversal is with pathspec).
|
||||
*
|
||||
* weight = -1 means it has one parent and its distance is yet to
|
||||
* be computed.
|
||||
*
|
||||
* weight = -2 means it has more than one parent and its distance is
|
||||
* unknown. After running count_distance() first, they will get zero
|
||||
* or positive distance.
|
||||
*/
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
{
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
|
||||
counted = 0;
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
struct commit *commit = p->item;
|
||||
unsigned flags = commit->object.flags;
|
||||
|
||||
p->item->util = &weights[n++];
|
||||
switch (count_interesting_parents(commit)) {
|
||||
case 0:
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, 1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
/*
|
||||
* otherwise, it is known not to reach any
|
||||
* tree-changing commit and gets weight 0.
|
||||
*/
|
||||
break;
|
||||
case 1:
|
||||
weight_set(p, -1);
|
||||
break;
|
||||
default:
|
||||
weight_set(p, -2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 initialize", counted, nr, list);
|
||||
|
||||
/*
|
||||
* If you have only one parent in the resulting set
|
||||
* then you can reach one commit more than that parent
|
||||
* can reach. So we do not have to run the expensive
|
||||
* count_distance() for single strand of pearls.
|
||||
*
|
||||
* However, if you have more than one parents, you cannot
|
||||
* just add their distance and one for yourself, since
|
||||
* they usually reach the same ancestor and you would
|
||||
* end up counting them twice that way.
|
||||
*
|
||||
* So we will first count distance of merges the usual
|
||||
* way, and then fill the blanks using cheaper algorithm.
|
||||
*/
|
||||
for (p = list; p; p = p->next) {
|
||||
if (p->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (weight(p) != -2)
|
||||
continue;
|
||||
weight_set(p, count_distance(p));
|
||||
clear_distance(list);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
counted++;
|
||||
}
|
||||
|
||||
show_list("bisection 2 count_distance", counted, nr, list);
|
||||
|
||||
while (counted < nr) {
|
||||
for (p = list; p; p = p->next) {
|
||||
struct commit_list *q;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (0 <= weight(p))
|
||||
continue;
|
||||
for (q = p->item->parents; q; q = q->next) {
|
||||
if (q->item->object.flags & UNINTERESTING)
|
||||
continue;
|
||||
if (0 <= weight(q))
|
||||
break;
|
||||
}
|
||||
if (!q)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* weight for p is unknown but q is known.
|
||||
* add one for p itself if p is to be counted,
|
||||
* otherwise inherit it from q directly.
|
||||
*/
|
||||
if (!(flags & TREESAME)) {
|
||||
weight_set(p, weight(q)+1);
|
||||
counted++;
|
||||
show_list("bisection 2 count one",
|
||||
counted, nr, list);
|
||||
}
|
||||
else
|
||||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
if (!find_all)
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all,
|
||||
int find_all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
int *weights;
|
||||
|
||||
show_list("bisection 2 entry", 0, 0, list);
|
||||
|
||||
/*
|
||||
* Count the number of total and tree-changing items on the
|
||||
* list, while reversing the list.
|
||||
*/
|
||||
for (nr = on_list = 0, last = NULL, p = list;
|
||||
p;
|
||||
p = next) {
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
next = p->next;
|
||||
if (flags & UNINTERESTING)
|
||||
continue;
|
||||
p->next = last;
|
||||
last = p;
|
||||
if (!(flags & TREESAME))
|
||||
nr++;
|
||||
on_list++;
|
||||
}
|
||||
list = last;
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
if (best) {
|
||||
if (!find_all)
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
free(weights);
|
||||
return best;
|
||||
}
|
||||
|
||||
static inline int log2i(int n)
|
||||
{
|
||||
int log2 = 0;
|
||||
@@ -613,11 +223,83 @@ static int estimate_bisect_steps(int all)
|
||||
return (e < 3 * x) ? n : n - 1;
|
||||
}
|
||||
|
||||
static void show_tried_revs(struct commit_list *tried, int stringed)
|
||||
{
|
||||
printf("bisect_tried='");
|
||||
for (;tried; tried = tried->next) {
|
||||
char *format = tried->next ? "%s|" : "%s";
|
||||
printf(format, sha1_to_hex(tried->item->object.sha1));
|
||||
}
|
||||
printf(stringed ? "' &&\n" : "'\n");
|
||||
}
|
||||
|
||||
int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
|
||||
{
|
||||
int cnt, flags = info->bisect_show_flags;
|
||||
char hex[41] = "", *format;
|
||||
struct commit_list *tried;
|
||||
struct rev_info *revs = info->revs;
|
||||
|
||||
if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
|
||||
return 1;
|
||||
|
||||
revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL);
|
||||
|
||||
/*
|
||||
* revs->commits can reach "reaches" commits among
|
||||
* "all" commits. If it is good, then there are
|
||||
* (all-reaches) commits left to be bisected.
|
||||
* On the other hand, if it is bad, then the set
|
||||
* to bisect is "reaches".
|
||||
* A bisect set of size N has (N-1) commits further
|
||||
* to test, as we already know one bad one.
|
||||
*/
|
||||
cnt = all - reaches;
|
||||
if (cnt < reaches)
|
||||
cnt = reaches;
|
||||
|
||||
if (revs->commits)
|
||||
strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
|
||||
|
||||
if (flags & BISECT_SHOW_ALL) {
|
||||
traverse_commit_list(revs, show_commit, show_object, info);
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
if (flags & BISECT_SHOW_TRIED)
|
||||
show_tried_revs(tried, flags & BISECT_SHOW_STRINGED);
|
||||
format = (flags & BISECT_SHOW_STRINGED) ?
|
||||
"bisect_rev=%s &&\n"
|
||||
"bisect_nr=%d &&\n"
|
||||
"bisect_good=%d &&\n"
|
||||
"bisect_bad=%d &&\n"
|
||||
"bisect_all=%d &&\n"
|
||||
"bisect_steps=%d\n"
|
||||
:
|
||||
"bisect_rev=%s\n"
|
||||
"bisect_nr=%d\n"
|
||||
"bisect_good=%d\n"
|
||||
"bisect_bad=%d\n"
|
||||
"bisect_all=%d\n"
|
||||
"bisect_steps=%d\n";
|
||||
printf(format,
|
||||
hex,
|
||||
cnt - 1,
|
||||
all - reaches - 1,
|
||||
reaches - 1,
|
||||
all,
|
||||
estimate_bisect_steps(all));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct commit_list *list;
|
||||
struct rev_info revs;
|
||||
struct rev_list_info info;
|
||||
int i;
|
||||
int read_from_stdin = 0;
|
||||
int bisect_list = 0;
|
||||
int bisect_show_vars = 0;
|
||||
int bisect_find_all = 0;
|
||||
int quiet = 0;
|
||||
@@ -628,6 +310,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
revs.commit_format = CMIT_FMT_UNSPECIFIED;
|
||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.revs = &revs;
|
||||
|
||||
quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
|
||||
for (i = 1 ; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
@@ -637,7 +322,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--timestamp")) {
|
||||
show_timestamp = 1;
|
||||
info.show_timestamp = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect")) {
|
||||
@@ -647,6 +332,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
if (!strcmp(arg, "--bisect-all")) {
|
||||
bisect_list = 1;
|
||||
bisect_find_all = 1;
|
||||
info.bisect_show_flags = BISECT_SHOW_ALL;
|
||||
revs.show_decorations = 1;
|
||||
continue;
|
||||
}
|
||||
@@ -666,19 +352,17 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
|
||||
/* The command line has a --pretty */
|
||||
hdr_termination = '\n';
|
||||
info.hdr_termination = '\n';
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
header_prefix = "";
|
||||
info.header_prefix = "";
|
||||
else
|
||||
header_prefix = "commit ";
|
||||
info.header_prefix = "commit ";
|
||||
}
|
||||
else if (revs.verbose_header)
|
||||
/* Only --header was specified */
|
||||
revs.commit_format = CMIT_FMT_RAW;
|
||||
|
||||
list = revs.commits;
|
||||
|
||||
if ((!list &&
|
||||
if ((!revs.commits &&
|
||||
(!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
|
||||
!revs.pending.nr)) ||
|
||||
revs.diff)
|
||||
@@ -699,49 +383,15 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all,
|
||||
bisect_find_all);
|
||||
if (bisect_show_vars) {
|
||||
int cnt;
|
||||
char hex[41];
|
||||
if (!revs.commits)
|
||||
return 1;
|
||||
/*
|
||||
* revs.commits can reach "reaches" commits among
|
||||
* "all" commits. If it is good, then there are
|
||||
* (all-reaches) commits left to be bisected.
|
||||
* On the other hand, if it is bad, then the set
|
||||
* to bisect is "reaches".
|
||||
* A bisect set of size N has (N-1) commits further
|
||||
* to test, as we already know one bad one.
|
||||
*/
|
||||
cnt = all - reaches;
|
||||
if (cnt < reaches)
|
||||
cnt = reaches;
|
||||
strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
|
||||
|
||||
if (bisect_find_all) {
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
printf("bisect_rev=%s\n"
|
||||
"bisect_nr=%d\n"
|
||||
"bisect_good=%d\n"
|
||||
"bisect_bad=%d\n"
|
||||
"bisect_all=%d\n"
|
||||
"bisect_steps=%d\n",
|
||||
hex,
|
||||
cnt - 1,
|
||||
all - reaches - 1,
|
||||
reaches - 1,
|
||||
all,
|
||||
estimate_bisect_steps(all));
|
||||
return 0;
|
||||
}
|
||||
if (bisect_show_vars)
|
||||
return show_bisect_vars(&info, reaches, all);
|
||||
}
|
||||
|
||||
traverse_commit_list(&revs,
|
||||
quiet ? finish_commit : show_commit,
|
||||
quiet ? finish_object : show_object);
|
||||
quiet ? finish_commit : show_commit,
|
||||
quiet ? finish_object : show_object,
|
||||
&info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_apply(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_archive(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_blame(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_branch(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_bundle(int argc, const char **argv, const char *prefix);
|
||||
|
||||
5
cache.h
5
cache.h
@@ -623,7 +623,8 @@ enum sharedrepo {
|
||||
PERM_EVERYBODY = 0664,
|
||||
};
|
||||
int git_config_perm(const char *var, const char *value);
|
||||
int adjust_shared_perm(const char *path);
|
||||
int set_shared_perm(const char *path, int mode);
|
||||
#define adjust_shared_perm(path) set_shared_perm((path), 0)
|
||||
int safe_create_leading_directories(char *path);
|
||||
int safe_create_leading_directories_const(const char *path);
|
||||
char *enter_repo(char *path, int strict);
|
||||
@@ -679,7 +680,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
|
||||
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
|
||||
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
|
||||
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
|
||||
extern int interpret_nth_last_branch(const char *str, struct strbuf *);
|
||||
extern int interpret_branch_name(const char *str, struct strbuf *);
|
||||
|
||||
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
|
||||
extern const char *ref_rev_parse_rules[];
|
||||
|
||||
@@ -646,7 +646,8 @@ _git_am ()
|
||||
;;
|
||||
--*)
|
||||
__gitcomp "
|
||||
--signoff --utf8 --binary --3way --interactive
|
||||
--3way --committer-date-is-author-date --ignore-date
|
||||
--interactive --keep --no-utf8 --signoff --utf8
|
||||
--whitespace=
|
||||
"
|
||||
return
|
||||
@@ -1870,6 +1871,7 @@ _git ()
|
||||
--bare
|
||||
--version
|
||||
--exec-path
|
||||
--html-path
|
||||
--work-tree=
|
||||
--help
|
||||
"
|
||||
|
||||
17
diff.c
17
diff.c
@@ -62,6 +62,15 @@ static int parse_diff_color_slot(const char *var, int ofs)
|
||||
die("bad config variable '%s'", var);
|
||||
}
|
||||
|
||||
static int git_config_rename(const char *var, const char *value)
|
||||
{
|
||||
if (!value)
|
||||
return DIFF_DETECT_RENAME;
|
||||
if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy"))
|
||||
return DIFF_DETECT_COPY;
|
||||
return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are to give UI layer defaults.
|
||||
* The core-level commands such as git-diff-files should
|
||||
@@ -75,13 +84,7 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "diff.renames")) {
|
||||
if (!value)
|
||||
diff_detect_rename_default = DIFF_DETECT_RENAME;
|
||||
else if (!strcasecmp(value, "copies") ||
|
||||
!strcasecmp(value, "copy"))
|
||||
diff_detect_rename_default = DIFF_DETECT_COPY;
|
||||
else if (git_config_bool(var,value))
|
||||
diff_detect_rename_default = DIFF_DETECT_RENAME;
|
||||
diff_detect_rename_default = git_config_rename(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "diff.autorefreshindex")) {
|
||||
|
||||
@@ -903,9 +903,6 @@ static char *keep_pack(char *curr_index_name)
|
||||
static const char *keep_msg = "fast-import";
|
||||
int keep_fd;
|
||||
|
||||
chmod(pack_data->pack_name, 0444);
|
||||
chmod(curr_index_name, 0444);
|
||||
|
||||
keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
|
||||
if (keep_fd < 0)
|
||||
die("cannot create keep file");
|
||||
|
||||
100
git-bisect.sh
100
git-bisect.sh
@@ -77,7 +77,7 @@ bisect_start() {
|
||||
then
|
||||
# Reset to the rev from where we started.
|
||||
start_head=$(cat "$GIT_DIR/BISECT_START")
|
||||
git checkout "$start_head" || exit
|
||||
git checkout "$start_head" -- || exit
|
||||
else
|
||||
# Get rev from where we start.
|
||||
case "$head" in
|
||||
@@ -279,87 +279,14 @@ bisect_auto_next() {
|
||||
bisect_next_check && bisect_next || :
|
||||
}
|
||||
|
||||
filter_skipped() {
|
||||
_eval="$1"
|
||||
_skip="$2"
|
||||
|
||||
if [ -z "$_skip" ]; then
|
||||
eval "$_eval" | {
|
||||
while read line
|
||||
do
|
||||
echo "$line &&"
|
||||
done
|
||||
echo ':'
|
||||
}
|
||||
return
|
||||
fi
|
||||
|
||||
# Let's parse the output of:
|
||||
# "git rev-list --bisect-vars --bisect-all ..."
|
||||
eval "$_eval" | {
|
||||
VARS= FOUND= TRIED=
|
||||
while read hash line
|
||||
do
|
||||
case "$VARS,$FOUND,$TRIED,$hash" in
|
||||
1,*,*,*)
|
||||
# "bisect_foo=bar" read from rev-list output.
|
||||
echo "$hash &&"
|
||||
;;
|
||||
,*,*,---*)
|
||||
# Separator
|
||||
;;
|
||||
,,,bisect_rev*)
|
||||
# We had nothing to search.
|
||||
echo "bisect_rev= &&"
|
||||
VARS=1
|
||||
;;
|
||||
,,*,bisect_rev*)
|
||||
# We did not find a good bisect rev.
|
||||
# This should happen only if the "bad"
|
||||
# commit is also a "skip" commit.
|
||||
echo "bisect_rev='$TRIED' &&"
|
||||
VARS=1
|
||||
;;
|
||||
,,*,*)
|
||||
# We are searching.
|
||||
TRIED="${TRIED:+$TRIED|}$hash"
|
||||
case "$_skip" in
|
||||
*$hash*) ;;
|
||||
*)
|
||||
echo "bisect_rev=$hash &&"
|
||||
echo "bisect_tried='$TRIED' &&"
|
||||
FOUND=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
,1,*,bisect_rev*)
|
||||
# We have already found a rev to be tested.
|
||||
VARS=1
|
||||
;;
|
||||
,1,*,*)
|
||||
;;
|
||||
*)
|
||||
# Unexpected input
|
||||
echo "die 'filter_skipped error'"
|
||||
die "filter_skipped error " \
|
||||
"VARS: '$VARS' " \
|
||||
"FOUND: '$FOUND' " \
|
||||
"TRIED: '$TRIED' " \
|
||||
"hash: '$hash' " \
|
||||
"line: '$line'"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo ':'
|
||||
}
|
||||
}
|
||||
|
||||
exit_if_skipped_commits () {
|
||||
_tried=$1
|
||||
if expr "$_tried" : ".*[|].*" > /dev/null ; then
|
||||
_bad=$2
|
||||
if test -n "$_tried" ; then
|
||||
echo "There are only 'skip'ped commit left to test."
|
||||
echo "The first bad commit could be any of:"
|
||||
echo "$_tried" | tr '[|]' '[\012]'
|
||||
test -n "$_bad" && echo "$_bad"
|
||||
echo "We cannot bisect more!"
|
||||
exit 2
|
||||
fi
|
||||
@@ -370,7 +297,7 @@ bisect_checkout() {
|
||||
_msg="$2"
|
||||
echo "Bisecting: $_msg"
|
||||
mark_expected_rev "$_rev"
|
||||
git checkout -q "$_rev" || exit
|
||||
git checkout -q "$_rev" -- || exit
|
||||
git show-branch "$_rev"
|
||||
}
|
||||
|
||||
@@ -490,28 +417,23 @@ bisect_next() {
|
||||
test "$?" -eq "1" && return
|
||||
|
||||
# Get bisection information
|
||||
BISECT_OPT=''
|
||||
test -n "$skip" && BISECT_OPT='--bisect-all'
|
||||
eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
|
||||
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
|
||||
eval=$(filter_skipped "$eval" "$skip") &&
|
||||
eval=$(eval "git bisect--helper --next-vars") &&
|
||||
eval "$eval" || exit
|
||||
|
||||
if [ -z "$bisect_rev" ]; then
|
||||
# We should exit here only if the "bad"
|
||||
# commit is also a "skip" commit (see above).
|
||||
exit_if_skipped_commits "$bisect_tried"
|
||||
echo "$bad was both good and bad"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$bisect_rev" = "$bad" ]; then
|
||||
exit_if_skipped_commits "$bisect_tried"
|
||||
exit_if_skipped_commits "$bisect_tried" "$bad"
|
||||
echo "$bisect_rev is first bad commit"
|
||||
git diff-tree --pretty $bisect_rev
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# We should exit here only if the "bad"
|
||||
# commit is also a "skip" commit (see above).
|
||||
exit_if_skipped_commits "$bisect_rev"
|
||||
|
||||
bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)"
|
||||
}
|
||||
|
||||
@@ -549,7 +471,7 @@ bisect_reset() {
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
git checkout "$branch" && bisect_clean_state
|
||||
git checkout "$branch" -- && bisect_clean_state
|
||||
}
|
||||
|
||||
bisect_clean_state() {
|
||||
|
||||
42
git-pull.sh
42
git-pull.sh
@@ -90,23 +90,31 @@ error_on_no_merge_candidates () {
|
||||
|
||||
curr_branch=${curr_branch#refs/heads/}
|
||||
|
||||
echo "You asked me to pull without telling me which branch you"
|
||||
echo "want to merge with, and 'branch.${curr_branch}.merge' in"
|
||||
echo "your configuration file does not tell me either. Please"
|
||||
echo "name which branch you want to merge on the command line and"
|
||||
echo "try again (e.g. 'git pull <repository> <refspec>')."
|
||||
echo "See git-pull(1) for details on the refspec."
|
||||
echo
|
||||
echo "If you often merge with the same branch, you may want to"
|
||||
echo "configure the following variables in your configuration"
|
||||
echo "file:"
|
||||
echo
|
||||
echo " branch.${curr_branch}.remote = <nickname>"
|
||||
echo " branch.${curr_branch}.merge = <remote-ref>"
|
||||
echo " remote.<nickname>.url = <url>"
|
||||
echo " remote.<nickname>.fetch = <refspec>"
|
||||
echo
|
||||
echo "See git-config(1) for details."
|
||||
if [ -z "$curr_branch" ]; then
|
||||
echo "You are not currently on a branch, so I cannot use any"
|
||||
echo "'branch.<branchname>.merge' in your configuration file."
|
||||
echo "Please specify which branch you want to merge on the command"
|
||||
echo "line and try again (e.g. 'git pull <repository> <refspec>')."
|
||||
echo "See git-pull(1) for details."
|
||||
else
|
||||
echo "You asked me to pull without telling me which branch you"
|
||||
echo "want to merge with, and 'branch.${curr_branch}.merge' in"
|
||||
echo "your configuration file does not tell me either. Please"
|
||||
echo "specify which branch you want to merge on the command line and"
|
||||
echo "try again (e.g. 'git pull <repository> <refspec>')."
|
||||
echo "See git-pull(1) for details."
|
||||
echo
|
||||
echo "If you often merge with the same branch, you may want to"
|
||||
echo "configure the following variables in your configuration"
|
||||
echo "file:"
|
||||
echo
|
||||
echo " branch.${curr_branch}.remote = <nickname>"
|
||||
echo " branch.${curr_branch}.merge = <remote-ref>"
|
||||
echo " remote.<nickname>.url = <url>"
|
||||
echo " remote.<nickname>.fetch = <refspec>"
|
||||
echo
|
||||
echo "See git-config(1) for details."
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
@@ -181,5 +181,5 @@ fi
|
||||
|
||||
case "$no_update_info" in
|
||||
t) : ;;
|
||||
*) git-update-server-info ;;
|
||||
*) git update-server-info ;;
|
||||
esac
|
||||
|
||||
@@ -608,7 +608,7 @@ EOT
|
||||
|
||||
sub ask {
|
||||
my ($prompt, %arg) = @_;
|
||||
my $valid_re = $arg{valid_re} || ""; # "" matches anything
|
||||
my $valid_re = $arg{valid_re};
|
||||
my $default = $arg{default};
|
||||
my $resp;
|
||||
my $i = 0;
|
||||
@@ -624,7 +624,7 @@ sub ask {
|
||||
if ($resp eq '' and defined $default) {
|
||||
return $default;
|
||||
}
|
||||
if ($resp =~ /$valid_re/) {
|
||||
if (!defined $valid_re or $resp =~ /$valid_re/) {
|
||||
return $resp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Copyright (c) 2007 Lars Hjemli
|
||||
|
||||
USAGE="[--quiet] [--cached] \
|
||||
[add <repo> [-b branch] <path>]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit <n>] [<commit>]] \
|
||||
[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit <n>] [<commit>]] \
|
||||
[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
|
||||
102
git-svn.perl
102
git-svn.perl
@@ -47,7 +47,8 @@ BEGIN {
|
||||
# import functions from Git into our packages, en masse
|
||||
no strict 'refs';
|
||||
foreach (qw/command command_oneline command_noisy command_output_pipe
|
||||
command_input_pipe command_close_pipe/) {
|
||||
command_input_pipe command_close_pipe
|
||||
command_bidi_pipe command_close_bidi_pipe/) {
|
||||
for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
|
||||
Git::SVN::Migration Git::SVN::Log Git::SVN),
|
||||
__PACKAGE__) {
|
||||
@@ -63,7 +64,7 @@ $sha1_short = qr/[a-f\d]{4,40}/;
|
||||
my ($_stdin, $_help, $_edit,
|
||||
$_message, $_file,
|
||||
$_template, $_shared,
|
||||
$_version, $_fetch_all, $_no_rebase,
|
||||
$_version, $_fetch_all, $_no_rebase, $_fetch_parent,
|
||||
$_merge, $_strategy, $_dry_run, $_local,
|
||||
$_prefix, $_no_checkout, $_url, $_verbose,
|
||||
$_git_format, $_commit_url, $_tag);
|
||||
@@ -112,6 +113,7 @@ my %cmd = (
|
||||
fetch => [ \&cmd_fetch, "Download new revisions from SVN",
|
||||
{ 'revision|r=s' => \$_revision,
|
||||
'fetch-all|all' => \$_fetch_all,
|
||||
'parent|p' => \$_fetch_parent,
|
||||
%fc_opts } ],
|
||||
clone => [ \&cmd_clone, "Initialize and fetch revisions",
|
||||
{ 'revision|r=s' => \$_revision,
|
||||
@@ -326,6 +328,7 @@ sub do_git_init_db {
|
||||
command_noisy(@init_db);
|
||||
$_repository = Git->repository(Repository => ".git");
|
||||
}
|
||||
command_noisy('config', 'core.autocrlf', 'false');
|
||||
my $set;
|
||||
my $pfx = "svn-remote.$Git::SVN::default_repo_id";
|
||||
foreach my $i (keys %icv) {
|
||||
@@ -334,6 +337,9 @@ sub do_git_init_db {
|
||||
command_noisy('config', "$pfx.$i", $icv{$i});
|
||||
$set = $i;
|
||||
}
|
||||
my $ignore_regex = \$SVN::Git::Fetcher::_ignore_regex;
|
||||
command_noisy('config', "$pfx.ignore-paths", $$ignore_regex)
|
||||
if defined $$ignore_regex;
|
||||
}
|
||||
|
||||
sub init_subdir {
|
||||
@@ -381,12 +387,21 @@ sub cmd_fetch {
|
||||
}
|
||||
my ($remote) = @_;
|
||||
if (@_ > 1) {
|
||||
die "Usage: $0 fetch [--all] [svn-remote]\n";
|
||||
die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n";
|
||||
}
|
||||
$remote ||= $Git::SVN::default_repo_id;
|
||||
if ($_fetch_all) {
|
||||
if ($_fetch_parent) {
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
|
||||
unless ($gs) {
|
||||
die "Unable to determine upstream SVN information from ",
|
||||
"working tree history\n";
|
||||
}
|
||||
# just fetch, don't checkout.
|
||||
$_no_checkout = 'true';
|
||||
$_fetch_all ? $gs->fetch_all : $gs->fetch;
|
||||
} elsif ($_fetch_all) {
|
||||
cmd_multi_fetch();
|
||||
} else {
|
||||
$remote ||= $Git::SVN::default_repo_id;
|
||||
Git::SVN::fetch_all($remote, Git::SVN::read_all_remotes());
|
||||
}
|
||||
}
|
||||
@@ -1254,6 +1269,40 @@ sub cmt_metadata {
|
||||
command(qw/cat-file commit/, shift)))[-1]);
|
||||
}
|
||||
|
||||
sub cmt_sha2rev_batch {
|
||||
my %s2r;
|
||||
my ($pid, $in, $out, $ctx) = command_bidi_pipe(qw/cat-file --batch/);
|
||||
my $list = shift;
|
||||
|
||||
foreach my $sha (@{$list}) {
|
||||
my $first = 1;
|
||||
my $size = 0;
|
||||
print $out $sha, "\n";
|
||||
|
||||
while (my $line = <$in>) {
|
||||
if ($first && $line =~ /^[[:xdigit:]]{40}\smissing$/) {
|
||||
last;
|
||||
} elsif ($first &&
|
||||
$line =~ /^[[:xdigit:]]{40}\scommit\s(\d+)$/) {
|
||||
$first = 0;
|
||||
$size = $1;
|
||||
next;
|
||||
} elsif ($line =~ /^(git-svn-id: )/) {
|
||||
my (undef, $rev, undef) =
|
||||
extract_metadata($line);
|
||||
$s2r{$sha} = $rev;
|
||||
}
|
||||
|
||||
$size -= length($line);
|
||||
last if ($size == 0);
|
||||
}
|
||||
}
|
||||
|
||||
command_close_bidi_pipe($pid, $in, $out, $ctx);
|
||||
|
||||
return \%s2r;
|
||||
}
|
||||
|
||||
sub working_head_info {
|
||||
my ($head, $refs) = @_;
|
||||
my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
|
||||
@@ -3286,6 +3335,8 @@ sub new {
|
||||
$self->{empty_symlinks} =
|
||||
_mark_empty_symlinks($git_svn, $switch_path);
|
||||
}
|
||||
$self->{ignore_regex} = eval { command_oneline('config', '--get',
|
||||
"svn-remote.$git_svn->{repo_id}.ignore-paths") };
|
||||
$self->{empty} = {};
|
||||
$self->{dir_prop} = {};
|
||||
$self->{file_prop} = {};
|
||||
@@ -3350,8 +3401,10 @@ sub in_dot_git {
|
||||
|
||||
# return value: 0 -- don't ignore, 1 -- ignore
|
||||
sub is_path_ignored {
|
||||
my ($path) = @_;
|
||||
my ($self, $path) = @_;
|
||||
return 1 if in_dot_git($path);
|
||||
return 1 if defined($self->{ignore_regex}) &&
|
||||
$path =~ m!$self->{ignore_regex}!;
|
||||
return 0 unless defined($_ignore_regex);
|
||||
return 1 if $path =~ m!$_ignore_regex!o;
|
||||
return 0;
|
||||
@@ -3382,7 +3435,7 @@ sub git_path {
|
||||
|
||||
sub delete_entry {
|
||||
my ($self, $path, $rev, $pb) = @_;
|
||||
return undef if is_path_ignored($path);
|
||||
return undef if $self->is_path_ignored($path);
|
||||
|
||||
my $gpath = $self->git_path($path);
|
||||
return undef if ($gpath eq '');
|
||||
@@ -3415,7 +3468,7 @@ sub open_file {
|
||||
my ($self, $path, $pb, $rev) = @_;
|
||||
my ($mode, $blob);
|
||||
|
||||
goto out if is_path_ignored($path);
|
||||
goto out if $self->is_path_ignored($path);
|
||||
|
||||
my $gpath = $self->git_path($path);
|
||||
($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
|
||||
@@ -3435,7 +3488,7 @@ sub add_file {
|
||||
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
|
||||
my $mode;
|
||||
|
||||
if (!is_path_ignored($path)) {
|
||||
if (!$self->is_path_ignored($path)) {
|
||||
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
|
||||
delete $self->{empty}->{$dir};
|
||||
$mode = '100644';
|
||||
@@ -3446,7 +3499,7 @@ sub add_file {
|
||||
|
||||
sub add_directory {
|
||||
my ($self, $path, $cp_path, $cp_rev) = @_;
|
||||
goto out if is_path_ignored($path);
|
||||
goto out if $self->is_path_ignored($path);
|
||||
my $gpath = $self->git_path($path);
|
||||
if ($gpath eq '') {
|
||||
my ($ls, $ctx) = command_output_pipe(qw/ls-tree
|
||||
@@ -3470,7 +3523,7 @@ out:
|
||||
|
||||
sub change_dir_prop {
|
||||
my ($self, $db, $prop, $value) = @_;
|
||||
return undef if is_path_ignored($db->{path});
|
||||
return undef if $self->is_path_ignored($db->{path});
|
||||
$self->{dir_prop}->{$db->{path}} ||= {};
|
||||
$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
|
||||
undef;
|
||||
@@ -3478,7 +3531,7 @@ sub change_dir_prop {
|
||||
|
||||
sub absent_directory {
|
||||
my ($self, $path, $pb) = @_;
|
||||
return undef if is_path_ignored($path);
|
||||
return undef if $self->is_path_ignored($path);
|
||||
$self->{absent_dir}->{$pb->{path}} ||= [];
|
||||
push @{$self->{absent_dir}->{$pb->{path}}}, $path;
|
||||
undef;
|
||||
@@ -3486,7 +3539,7 @@ sub absent_directory {
|
||||
|
||||
sub absent_file {
|
||||
my ($self, $path, $pb) = @_;
|
||||
return undef if is_path_ignored($path);
|
||||
return undef if $self->is_path_ignored($path);
|
||||
$self->{absent_file}->{$pb->{path}} ||= [];
|
||||
push @{$self->{absent_file}->{$pb->{path}}}, $path;
|
||||
undef;
|
||||
@@ -3494,7 +3547,7 @@ sub absent_file {
|
||||
|
||||
sub change_file_prop {
|
||||
my ($self, $fb, $prop, $value) = @_;
|
||||
return undef if is_path_ignored($fb->{path});
|
||||
return undef if $self->is_path_ignored($fb->{path});
|
||||
if ($prop eq 'svn:executable') {
|
||||
if ($fb->{mode_b} != 120000) {
|
||||
$fb->{mode_b} = defined $value ? 100755 : 100644;
|
||||
@@ -3510,7 +3563,7 @@ sub change_file_prop {
|
||||
|
||||
sub apply_textdelta {
|
||||
my ($self, $fb, $exp) = @_;
|
||||
return undef if is_path_ignored($fb->{path});
|
||||
return undef if $self->is_path_ignored($fb->{path});
|
||||
my $fh = $::_repository->temp_acquire('svn_delta');
|
||||
# $fh gets auto-closed() by SVN::TxDelta::apply(),
|
||||
# (but $base does not,) so dup() it for reading in close_file
|
||||
@@ -3557,7 +3610,7 @@ sub apply_textdelta {
|
||||
|
||||
sub close_file {
|
||||
my ($self, $fb, $exp) = @_;
|
||||
return undef if is_path_ignored($fb->{path});
|
||||
return undef if $self->is_path_ignored($fb->{path});
|
||||
|
||||
my $hash;
|
||||
my $path = $self->git_path($fb->{path});
|
||||
@@ -4991,11 +5044,22 @@ sub cmd_blame {
|
||||
'--', $path);
|
||||
my ($sha1);
|
||||
my %authors;
|
||||
my @buffer;
|
||||
my %dsha; #distinct sha keys
|
||||
|
||||
while (my $line = <$fh>) {
|
||||
push @buffer, $line;
|
||||
if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
|
||||
$sha1 = $1;
|
||||
(undef, $rev, undef) = ::cmt_metadata($1);
|
||||
$rev = '0' if (!$rev);
|
||||
$dsha{$1} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $s2r = ::cmt_sha2rev_batch([keys %dsha]);
|
||||
|
||||
foreach my $line (@buffer) {
|
||||
if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
|
||||
$rev = $s2r->{$1};
|
||||
$rev = '0' if (!$rev)
|
||||
}
|
||||
elsif ($line =~ /^author (.*)/) {
|
||||
$authors{$rev} = $1;
|
||||
|
||||
6
git.c
6
git.c
@@ -5,7 +5,7 @@
|
||||
#include "run-command.h"
|
||||
|
||||
const char git_usage_string[] =
|
||||
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
|
||||
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
|
||||
|
||||
const char git_more_info_string[] =
|
||||
"See 'git help COMMAND' for more information on a specific command.";
|
||||
@@ -75,6 +75,9 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
|
||||
puts(git_exec_path());
|
||||
exit(0);
|
||||
}
|
||||
} else if (!strcmp(cmd, "--html-path")) {
|
||||
puts(system_path(GIT_HTML_PATH));
|
||||
exit(0);
|
||||
} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
|
||||
use_pager = 1;
|
||||
} else if (!strcmp(cmd, "--no-pager")) {
|
||||
@@ -271,6 +274,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "annotate", cmd_annotate, RUN_SETUP },
|
||||
{ "apply", cmd_apply },
|
||||
{ "archive", cmd_archive },
|
||||
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "blame", cmd_blame, RUN_SETUP },
|
||||
{ "branch", cmd_branch, RUN_SETUP },
|
||||
{ "bundle", cmd_bundle },
|
||||
|
||||
61
http-push.c
61
http-push.c
@@ -186,6 +186,32 @@ enum dav_header_flag {
|
||||
DAV_HEADER_TIMEOUT = (1u << 2)
|
||||
};
|
||||
|
||||
static char *xml_entities(char *s)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
while (*s) {
|
||||
size_t len = strcspn(s, "\"<>&");
|
||||
strbuf_add(&buf, s, len);
|
||||
s += len;
|
||||
switch (*s) {
|
||||
case '"':
|
||||
strbuf_addstr(&buf, """);
|
||||
break;
|
||||
case '<':
|
||||
strbuf_addstr(&buf, "<");
|
||||
break;
|
||||
case '>':
|
||||
strbuf_addstr(&buf, ">");
|
||||
break;
|
||||
case '&':
|
||||
strbuf_addstr(&buf, "&");
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
@@ -567,6 +593,10 @@ static void start_put(struct transfer_request *request)
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.buf.len);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
|
||||
#ifndef NO_CURL_IOCTL
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &request->buffer);
|
||||
#endif
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
|
||||
@@ -748,7 +778,6 @@ static void finish_request(struct transfer_request *request)
|
||||
aborted = 1;
|
||||
}
|
||||
} else if (request->state == RUN_FETCH_LOOSE) {
|
||||
fchmod(request->local_fileno, 0444);
|
||||
close(request->local_fileno); request->local_fileno = -1;
|
||||
|
||||
if (request->curl_result != CURLE_OK &&
|
||||
@@ -1222,6 +1251,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
|
||||
struct remote_lock *lock = NULL;
|
||||
struct curl_slist *dav_headers = NULL;
|
||||
struct xml_ctx ctx;
|
||||
char *escaped;
|
||||
|
||||
url = xmalloc(strlen(repo->url) + strlen(path) + 1);
|
||||
sprintf(url, "%s%s", repo->url, path);
|
||||
@@ -1256,7 +1286,9 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
|
||||
ep = strchr(ep + 1, '/');
|
||||
}
|
||||
|
||||
strbuf_addf(&out_buffer.buf, LOCK_REQUEST, git_default_email);
|
||||
escaped = xml_entities(git_default_email);
|
||||
strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);
|
||||
free(escaped);
|
||||
|
||||
sprintf(timeout_header, "Timeout: Second-%ld", timeout);
|
||||
dav_headers = curl_slist_append(dav_headers, timeout_header);
|
||||
@@ -1267,6 +1299,10 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
|
||||
#ifndef NO_CURL_IOCTL
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
|
||||
#endif
|
||||
curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
||||
@@ -1508,6 +1544,10 @@ static void remote_ls(const char *path, int flags,
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
|
||||
#ifndef NO_CURL_IOCTL
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
|
||||
#endif
|
||||
curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
||||
@@ -1573,8 +1613,11 @@ static int locking_available(void)
|
||||
struct curl_slist *dav_headers = NULL;
|
||||
struct xml_ctx ctx;
|
||||
int lock_flags = 0;
|
||||
char *escaped;
|
||||
|
||||
strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, repo->url);
|
||||
escaped = xml_entities(repo->url);
|
||||
strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, escaped);
|
||||
free(escaped);
|
||||
|
||||
dav_headers = curl_slist_append(dav_headers, "Depth: 0");
|
||||
dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
|
||||
@@ -1584,6 +1627,10 @@ static int locking_available(void)
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
|
||||
#ifndef NO_CURL_IOCTL
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
|
||||
#endif
|
||||
curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, repo->url);
|
||||
@@ -1766,6 +1813,10 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
|
||||
#ifndef NO_CURL_IOCTL
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
|
||||
#endif
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
|
||||
@@ -1910,6 +1961,10 @@ static void update_remote_info_refs(struct remote_lock *lock)
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILE, &buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.buf.len);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
|
||||
#ifndef NO_CURL_IOCTL
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &buffer);
|
||||
#endif
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
|
||||
|
||||
@@ -231,7 +231,6 @@ static void finish_object_request(struct object_request *obj_req)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
fchmod(obj_req->local, 0444);
|
||||
close(obj_req->local); obj_req->local = -1;
|
||||
|
||||
if (obj_req->http_code == 416) {
|
||||
|
||||
19
http.c
19
http.c
@@ -44,6 +44,25 @@ size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifndef NO_CURL_IOCTL
|
||||
curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
|
||||
{
|
||||
struct buffer *buffer = clientp;
|
||||
|
||||
switch (cmd) {
|
||||
case CURLIOCMD_NOP:
|
||||
return CURLIOE_OK;
|
||||
|
||||
case CURLIOCMD_RESTARTREAD:
|
||||
buffer->posn = 0;
|
||||
return CURLIOE_OK;
|
||||
|
||||
default:
|
||||
return CURLIOE_UNKNOWNCMD;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
||||
{
|
||||
size_t size = eltsize * nmemb;
|
||||
|
||||
7
http.h
7
http.h
@@ -37,6 +37,10 @@
|
||||
#define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND
|
||||
#endif
|
||||
|
||||
#if LIBCURL_VERSION_NUM < 0x070c03
|
||||
#define NO_CURL_IOCTL
|
||||
#endif
|
||||
|
||||
struct slot_results
|
||||
{
|
||||
CURLcode curl_result;
|
||||
@@ -67,6 +71,9 @@ struct buffer
|
||||
extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
|
||||
extern size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
|
||||
extern size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
|
||||
#ifndef NO_CURL_IOCTL
|
||||
extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
|
||||
#endif
|
||||
|
||||
/* Slot lifecycle functions */
|
||||
extern struct active_request_slot *get_active_slot(void);
|
||||
|
||||
@@ -823,8 +823,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
|
||||
}
|
||||
if (move_temp_to_file(curr_pack_name, final_pack_name))
|
||||
die("cannot store pack file");
|
||||
}
|
||||
if (from_stdin)
|
||||
} else if (from_stdin)
|
||||
chmod(final_pack_name, 0444);
|
||||
|
||||
if (final_index_name != curr_index_name) {
|
||||
@@ -835,8 +834,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
|
||||
}
|
||||
if (move_temp_to_file(curr_index_name, final_index_name))
|
||||
die("cannot store index file");
|
||||
}
|
||||
chmod(final_index_name, 0444);
|
||||
} else
|
||||
chmod(final_index_name, 0444);
|
||||
|
||||
if (!from_stdin) {
|
||||
printf("%s\n", sha1_to_hex(sha1));
|
||||
|
||||
@@ -23,7 +23,6 @@ static void process_blob(struct rev_info *revs,
|
||||
if (obj->flags & (UNINTERESTING | SEEN))
|
||||
return;
|
||||
obj->flags |= SEEN;
|
||||
name = xstrdup(name);
|
||||
add_object(obj, p, path, name);
|
||||
}
|
||||
|
||||
@@ -78,7 +77,6 @@ static void process_tree(struct rev_info *revs,
|
||||
if (parse_tree(tree) < 0)
|
||||
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
||||
obj->flags |= SEEN;
|
||||
name = xstrdup(name);
|
||||
add_object(obj, p, path, name);
|
||||
me.up = path;
|
||||
me.elem = name;
|
||||
@@ -137,8 +135,9 @@ void mark_edges_uninteresting(struct commit_list *list,
|
||||
}
|
||||
|
||||
void traverse_commit_list(struct rev_info *revs,
|
||||
void (*show_commit)(struct commit *),
|
||||
void (*show_object)(struct object_array_entry *))
|
||||
show_commit_fn show_commit,
|
||||
show_object_fn show_object,
|
||||
void *data)
|
||||
{
|
||||
int i;
|
||||
struct commit *commit;
|
||||
@@ -146,7 +145,7 @@ void traverse_commit_list(struct rev_info *revs,
|
||||
|
||||
while ((commit = get_revision(revs)) != NULL) {
|
||||
process_tree(revs, commit->tree, &objects, NULL, "");
|
||||
show_commit(commit);
|
||||
show_commit(commit, data);
|
||||
}
|
||||
for (i = 0; i < revs->pending.nr; i++) {
|
||||
struct object_array_entry *pending = revs->pending.objects + i;
|
||||
@@ -173,7 +172,7 @@ void traverse_commit_list(struct rev_info *revs,
|
||||
sha1_to_hex(obj->sha1), name);
|
||||
}
|
||||
for (i = 0; i < objects.nr; i++)
|
||||
show_object(&objects.objects[i]);
|
||||
show_object(&objects.objects[i], data);
|
||||
free(objects.objects);
|
||||
if (revs->pending.nr) {
|
||||
free(revs->pending.objects);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef LIST_OBJECTS_H
|
||||
#define LIST_OBJECTS_H
|
||||
|
||||
typedef void (*show_commit_fn)(struct commit *);
|
||||
typedef void (*show_object_fn)(struct object_array_entry *);
|
||||
typedef void (*show_commit_fn)(struct commit *, void *);
|
||||
typedef void (*show_object_fn)(struct object_array_entry *, void *);
|
||||
typedef void (*show_edge_fn)(struct commit *);
|
||||
|
||||
void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn);
|
||||
void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
|
||||
|
||||
void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
|
||||
|
||||
|
||||
35
log-tree.c
35
log-tree.c
@@ -179,13 +179,31 @@ static int has_non_ascii(const char *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
void get_patch_filename(struct commit *commit, int nr, const char *suffix,
|
||||
struct strbuf *buf)
|
||||
{
|
||||
int suffix_len = strlen(suffix) + 1;
|
||||
int start_len = buf->len;
|
||||
|
||||
strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
|
||||
if (commit) {
|
||||
int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
|
||||
|
||||
format_commit_message(commit, "%f", buf, DATE_NORMAL);
|
||||
if (max_len < buf->len)
|
||||
strbuf_setlen(buf, max_len);
|
||||
strbuf_addstr(buf, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
|
||||
const char **subject_p,
|
||||
const char **extra_headers_p,
|
||||
int *need_8bit_cte_p)
|
||||
{
|
||||
const char *subject = NULL;
|
||||
const char *extra_headers = opt->extra_headers;
|
||||
const char *name = sha1_to_hex(commit->object.sha1);
|
||||
|
||||
*need_8bit_cte_p = 0; /* unknown */
|
||||
if (opt->total > 0) {
|
||||
@@ -224,6 +242,7 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
if (opt->mime_boundary) {
|
||||
static char subject_buffer[1024];
|
||||
static char buffer[1024];
|
||||
struct strbuf filename = STRBUF_INIT;
|
||||
*need_8bit_cte_p = -1; /* NEVER */
|
||||
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
|
||||
"%s"
|
||||
@@ -242,18 +261,21 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
mime_boundary_leader, opt->mime_boundary);
|
||||
extra_headers = subject_buffer;
|
||||
|
||||
get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr,
|
||||
opt->patch_suffix, &filename);
|
||||
snprintf(buffer, sizeof(buffer) - 1,
|
||||
"\n--%s%s\n"
|
||||
"Content-Type: text/x-patch;"
|
||||
" name=\"%s.diff\"\n"
|
||||
" name=\"%s\"\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Content-Disposition: %s;"
|
||||
" filename=\"%s.diff\"\n\n",
|
||||
" filename=\"%s\"\n\n",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
name,
|
||||
filename.buf,
|
||||
opt->no_inline ? "attachment" : "inline",
|
||||
name);
|
||||
filename.buf);
|
||||
opt->diffopt.stat_sep = buffer;
|
||||
strbuf_release(&filename);
|
||||
}
|
||||
*subject_p = subject;
|
||||
*extra_headers_p = extra_headers;
|
||||
@@ -333,8 +355,7 @@ void show_log(struct rev_info *opt)
|
||||
*/
|
||||
|
||||
if (opt->commit_format == CMIT_FMT_EMAIL) {
|
||||
log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
|
||||
&subject, &extra_headers,
|
||||
log_write_email_headers(opt, commit, &subject, &extra_headers,
|
||||
&need_8bit_cte);
|
||||
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
|
||||
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
|
||||
|
||||
@@ -13,10 +13,14 @@ int log_tree_commit(struct rev_info *, struct commit *);
|
||||
int log_tree_opt_parse(struct rev_info *, const char **, int);
|
||||
void show_log(struct rev_info *opt);
|
||||
void show_decorations(struct rev_info *opt, struct commit *commit);
|
||||
void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
|
||||
const char **subject_p,
|
||||
const char **extra_headers_p,
|
||||
int *need_8bit_cte_p);
|
||||
void load_ref_decorations(void);
|
||||
|
||||
#define FORMAT_PATCH_NAME_MAX 64
|
||||
void get_patch_filename(struct commit *commit, int nr, const char *suffix,
|
||||
struct strbuf *buf);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1116,21 +1116,13 @@ static int process_entry(struct merge_options *o,
|
||||
o->branch1, o->branch2);
|
||||
|
||||
clean_merge = mfi.clean;
|
||||
if (mfi.clean)
|
||||
update_file(o, 1, mfi.sha, mfi.mode, path);
|
||||
else if (S_ISGITLINK(mfi.mode))
|
||||
output(o, 1, "CONFLICT (submodule): Merge conflict in %s "
|
||||
"- needs %s", path, sha1_to_hex(b.sha1));
|
||||
else {
|
||||
if (!mfi.clean) {
|
||||
if (S_ISGITLINK(mfi.mode))
|
||||
reason = "submodule";
|
||||
output(o, 1, "CONFLICT (%s): Merge conflict in %s",
|
||||
reason, path);
|
||||
|
||||
if (o->call_depth)
|
||||
update_file(o, 0, mfi.sha, mfi.mode, path);
|
||||
else
|
||||
update_file_flags(o, mfi.sha, mfi.mode, path,
|
||||
0 /* update_cache */, 1 /* update_working_directory */);
|
||||
}
|
||||
update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
|
||||
} else if (!o_sha && !a_sha && !b_sha) {
|
||||
/*
|
||||
* this entry was deleted altogether. a_mode == 0 means
|
||||
|
||||
93
patch-ids.c
93
patch-ids.c
@@ -1,6 +1,7 @@
|
||||
#include "cache.h"
|
||||
#include "diff.h"
|
||||
#include "commit.h"
|
||||
#include "sha1-lookup.h"
|
||||
#include "patch-ids.h"
|
||||
|
||||
static int commit_patch_id(struct commit *commit, struct diff_options *options,
|
||||
@@ -15,99 +16,15 @@ static int commit_patch_id(struct commit *commit, struct diff_options *options,
|
||||
return diff_flush_patch_id(options, sha1);
|
||||
}
|
||||
|
||||
static uint32_t take2(const unsigned char *id)
|
||||
static const unsigned char *patch_id_access(size_t index, void *table)
|
||||
{
|
||||
return ((id[0] << 8) | id[1]);
|
||||
struct patch_id **id_table = table;
|
||||
return id_table[index]->patch_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conventional binary search loop looks like this:
|
||||
*
|
||||
* do {
|
||||
* int mi = (lo + hi) / 2;
|
||||
* int cmp = "entry pointed at by mi" minus "target";
|
||||
* if (!cmp)
|
||||
* return (mi is the wanted one)
|
||||
* if (cmp > 0)
|
||||
* hi = mi; "mi is larger than target"
|
||||
* else
|
||||
* lo = mi+1; "mi is smaller than target"
|
||||
* } while (lo < hi);
|
||||
*
|
||||
* The invariants are:
|
||||
*
|
||||
* - When entering the loop, lo points at a slot that is never
|
||||
* above the target (it could be at the target), hi points at a
|
||||
* slot that is guaranteed to be above the target (it can never
|
||||
* be at the target).
|
||||
*
|
||||
* - We find a point 'mi' between lo and hi (mi could be the same
|
||||
* as lo, but never can be the same as hi), and check if it hits
|
||||
* the target. There are three cases:
|
||||
*
|
||||
* - if it is a hit, we are happy.
|
||||
*
|
||||
* - if it is strictly higher than the target, we update hi with
|
||||
* it.
|
||||
*
|
||||
* - if it is strictly lower than the target, we update lo to be
|
||||
* one slot after it, because we allow lo to be at the target.
|
||||
*
|
||||
* When choosing 'mi', we do not have to take the "middle" but
|
||||
* anywhere in between lo and hi, as long as lo <= mi < hi is
|
||||
* satisfied. When we somehow know that the distance between the
|
||||
* target and lo is much shorter than the target and hi, we could
|
||||
* pick mi that is much closer to lo than the midway.
|
||||
*/
|
||||
static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
|
||||
{
|
||||
int hi = nr;
|
||||
int lo = 0;
|
||||
int mi = 0;
|
||||
|
||||
if (!nr)
|
||||
return -1;
|
||||
|
||||
if (nr != 1) {
|
||||
unsigned lov, hiv, miv, ofs;
|
||||
|
||||
for (ofs = 0; ofs < 18; ofs += 2) {
|
||||
lov = take2(table[0]->patch_id + ofs);
|
||||
hiv = take2(table[nr-1]->patch_id + ofs);
|
||||
miv = take2(id + ofs);
|
||||
if (miv < lov)
|
||||
return -1;
|
||||
if (hiv < miv)
|
||||
return -1 - nr;
|
||||
if (lov != hiv) {
|
||||
/*
|
||||
* At this point miv could be equal
|
||||
* to hiv (but id could still be higher);
|
||||
* the invariant of (mi < hi) should be
|
||||
* kept.
|
||||
*/
|
||||
mi = (nr-1) * (miv - lov) / (hiv - lov);
|
||||
if (lo <= mi && mi < hi)
|
||||
break;
|
||||
die("oops");
|
||||
}
|
||||
}
|
||||
if (18 <= ofs)
|
||||
die("cannot happen -- lo and hi are identical");
|
||||
}
|
||||
|
||||
do {
|
||||
int cmp;
|
||||
cmp = hashcmp(table[mi]->patch_id, id);
|
||||
if (!cmp)
|
||||
return mi;
|
||||
if (cmp > 0)
|
||||
hi = mi;
|
||||
else
|
||||
lo = mi + 1;
|
||||
mi = (hi + lo) / 2;
|
||||
} while (lo < hi);
|
||||
return -lo-1;
|
||||
return sha1_pos(id, table, nr, patch_id_access);
|
||||
}
|
||||
|
||||
#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
|
||||
|
||||
53
path.c
53
path.c
@@ -311,36 +311,49 @@ char *enter_repo(char *path, int strict)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int adjust_shared_perm(const char *path)
|
||||
int set_shared_perm(const char *path, int mode)
|
||||
{
|
||||
struct stat st;
|
||||
int mode;
|
||||
int tweak, shared, orig_mode;
|
||||
|
||||
if (!shared_repository)
|
||||
if (!shared_repository) {
|
||||
if (mode)
|
||||
return chmod(path, mode & ~S_IFMT);
|
||||
return 0;
|
||||
if (lstat(path, &st) < 0)
|
||||
return -1;
|
||||
mode = st.st_mode;
|
||||
|
||||
if (shared_repository) {
|
||||
int tweak = shared_repository;
|
||||
if (!(mode & S_IWUSR))
|
||||
tweak &= ~0222;
|
||||
mode |= tweak;
|
||||
} else {
|
||||
/* Preserve old PERM_UMASK behaviour */
|
||||
if (mode & S_IWUSR)
|
||||
mode |= S_IWGRP;
|
||||
}
|
||||
if (!mode) {
|
||||
if (lstat(path, &st) < 0)
|
||||
return -1;
|
||||
mode = st.st_mode;
|
||||
orig_mode = mode;
|
||||
} else
|
||||
orig_mode = 0;
|
||||
if (shared_repository < 0)
|
||||
shared = -shared_repository;
|
||||
else
|
||||
shared = shared_repository;
|
||||
tweak = shared;
|
||||
|
||||
if (!(mode & S_IWUSR))
|
||||
tweak &= ~0222;
|
||||
if (mode & S_IXUSR)
|
||||
/* Copy read bits to execute bits */
|
||||
tweak |= (tweak & 0444) >> 2;
|
||||
if (shared_repository < 0)
|
||||
mode = (mode & ~0777) | tweak;
|
||||
else
|
||||
mode |= tweak;
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
mode |= FORCE_DIR_SET_GID;
|
||||
|
||||
/* Copy read bits to execute bits */
|
||||
mode |= (shared_repository & 0444) >> 2;
|
||||
mode |= (shared & 0444) >> 2;
|
||||
mode |= FORCE_DIR_SET_GID;
|
||||
}
|
||||
|
||||
if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
|
||||
if (((shared_repository < 0
|
||||
? (orig_mode & (FORCE_DIR_SET_GID | 0777))
|
||||
: (orig_mode & mode)) != mode) &&
|
||||
chmod(path, (mode & ~S_IFMT)) < 0)
|
||||
return -2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
12
perl/Git.pm
12
perl/Git.pm
@@ -56,7 +56,7 @@ require Exporter;
|
||||
@EXPORT_OK = qw(command command_oneline command_noisy
|
||||
command_output_pipe command_input_pipe command_close_pipe
|
||||
command_bidi_pipe command_close_bidi_pipe
|
||||
version exec_path hash_object git_cmd_try
|
||||
version exec_path html_path hash_object git_cmd_try
|
||||
remote_refs
|
||||
temp_acquire temp_release temp_reset temp_path);
|
||||
|
||||
@@ -492,6 +492,16 @@ C<git --exec-path>). Useful mostly only internally.
|
||||
sub exec_path { command_oneline('--exec-path') }
|
||||
|
||||
|
||||
=item html_path ()
|
||||
|
||||
Return path to the Git html documentation (the same as
|
||||
C<git --html-path>). Useful mostly only internally.
|
||||
|
||||
=cut
|
||||
|
||||
sub html_path { command_oneline('--html-path') }
|
||||
|
||||
|
||||
=item repo_path ()
|
||||
|
||||
Return path to the git repository. Must be called on a repository instance.
|
||||
|
||||
37
pretty.c
37
pretty.c
@@ -493,6 +493,40 @@ static void parse_commit_header(struct format_commit_context *context)
|
||||
context->commit_header_parsed = 1;
|
||||
}
|
||||
|
||||
static int istitlechar(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
static void format_sanitized_subject(struct strbuf *sb, const char *msg)
|
||||
{
|
||||
size_t trimlen;
|
||||
size_t start_len = sb->len;
|
||||
int space = 2;
|
||||
|
||||
for (; *msg && *msg != '\n'; msg++) {
|
||||
if (istitlechar(*msg)) {
|
||||
if (space == 1)
|
||||
strbuf_addch(sb, '-');
|
||||
space = 0;
|
||||
strbuf_addch(sb, *msg);
|
||||
if (*msg == '.')
|
||||
while (*(msg+1) == '.')
|
||||
msg++;
|
||||
} else
|
||||
space |= 1;
|
||||
}
|
||||
|
||||
/* trim any trailing '.' or '-' characters */
|
||||
trimlen = 0;
|
||||
while (sb->len - trimlen > start_len &&
|
||||
(sb->buf[sb->len - 1 - trimlen] == '.'
|
||||
|| sb->buf[sb->len - 1 - trimlen] == '-'))
|
||||
trimlen++;
|
||||
strbuf_remove(sb, sb->len - trimlen, trimlen);
|
||||
}
|
||||
|
||||
const char *format_subject(struct strbuf *sb, const char *msg,
|
||||
const char *line_separator)
|
||||
{
|
||||
@@ -683,6 +717,9 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
|
||||
case 's': /* subject */
|
||||
format_subject(sb, msg + c->subject_off, " ");
|
||||
return 1;
|
||||
case 'f': /* sanitized subject */
|
||||
format_sanitized_subject(sb, msg + c->subject_off);
|
||||
return 1;
|
||||
case 'b': /* body */
|
||||
strbuf_addstr(sb, msg + c->body_off);
|
||||
return 1;
|
||||
|
||||
35
quote.c
35
quote.c
@@ -72,7 +72,7 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
|
||||
}
|
||||
}
|
||||
|
||||
char *sq_dequote(char *arg)
|
||||
char *sq_dequote_step(char *arg, char **next)
|
||||
{
|
||||
char *dst = arg;
|
||||
char *src = arg;
|
||||
@@ -92,6 +92,8 @@ char *sq_dequote(char *arg)
|
||||
switch (*++src) {
|
||||
case '\0':
|
||||
*dst = 0;
|
||||
if (next)
|
||||
*next = NULL;
|
||||
return arg;
|
||||
case '\\':
|
||||
c = *++src;
|
||||
@@ -101,11 +103,40 @@ char *sq_dequote(char *arg)
|
||||
}
|
||||
/* Fallthrough */
|
||||
default:
|
||||
return NULL;
|
||||
if (!next || !isspace(*src))
|
||||
return NULL;
|
||||
do {
|
||||
c = *++src;
|
||||
} while (isspace(c));
|
||||
*dst = 0;
|
||||
*next = src;
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *sq_dequote(char *arg)
|
||||
{
|
||||
return sq_dequote_step(arg, NULL);
|
||||
}
|
||||
|
||||
int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
|
||||
{
|
||||
char *next = arg;
|
||||
|
||||
if (!*arg)
|
||||
return 0;
|
||||
do {
|
||||
char *dequoted = sq_dequote_step(next, &next);
|
||||
if (!dequoted)
|
||||
return -1;
|
||||
ALLOC_GROW(*argv, *nr + 1, *alloc);
|
||||
(*argv)[(*nr)++] = dequoted;
|
||||
} while (next);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 1 means: quote as octal
|
||||
* 0 means: quote as octal if (quote_path_fully)
|
||||
* -1 means: never quote
|
||||
|
||||
9
quote.h
9
quote.h
@@ -39,6 +39,15 @@ extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
|
||||
*/
|
||||
extern char *sq_dequote(char *);
|
||||
|
||||
/*
|
||||
* Same as the above, but can be used to unwrap many arguments in the
|
||||
* same string separated by space. "next" is changed to point to the
|
||||
* next argument that should be passed as first parameter. When there
|
||||
* is no more argument to be dequoted, "next" is updated to point to NULL.
|
||||
*/
|
||||
extern char *sq_dequote_step(char *arg, char **next);
|
||||
extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
|
||||
|
||||
extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
|
||||
extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
|
||||
extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
|
||||
|
||||
@@ -48,7 +48,6 @@ static void process_tree(struct tree *tree,
|
||||
obj->flags |= SEEN;
|
||||
if (parse_tree(tree) < 0)
|
||||
die("bad tree object %s", sha1_to_hex(obj->sha1));
|
||||
name = xstrdup(name);
|
||||
add_object(obj, p, path, name);
|
||||
me.up = path;
|
||||
me.elem = name;
|
||||
|
||||
126
refs.c
126
refs.c
@@ -647,19 +647,24 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
|
||||
return do_for_each_ref("refs/", fn, 0, 0, cb_data);
|
||||
}
|
||||
|
||||
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data);
|
||||
}
|
||||
|
||||
int for_each_tag_ref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/tags/", fn, 10, 0, cb_data);
|
||||
return for_each_ref_in("refs/tags/", fn, cb_data);
|
||||
}
|
||||
|
||||
int for_each_branch_ref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/heads/", fn, 11, 0, cb_data);
|
||||
return for_each_ref_in("refs/heads/", fn, cb_data);
|
||||
}
|
||||
|
||||
int for_each_remote_ref(each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
return do_for_each_ref("refs/remotes/", fn, 13, 0, cb_data);
|
||||
return for_each_ref_in("refs/remotes/", fn, cb_data);
|
||||
}
|
||||
|
||||
int for_each_rawref(each_ref_fn fn, void *cb_data)
|
||||
@@ -676,6 +681,7 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
|
||||
* - it has double dots "..", or
|
||||
* - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
|
||||
* - it ends with a "/".
|
||||
* - it ends with ".lock"
|
||||
*/
|
||||
|
||||
static inline int bad_ref_char(int ch)
|
||||
@@ -693,7 +699,7 @@ static inline int bad_ref_char(int ch)
|
||||
|
||||
int check_ref_format(const char *ref)
|
||||
{
|
||||
int ch, level, bad_type;
|
||||
int ch, level, bad_type, last;
|
||||
int ret = CHECK_REF_FORMAT_OK;
|
||||
const char *cp = ref;
|
||||
|
||||
@@ -717,21 +723,28 @@ int check_ref_format(const char *ref)
|
||||
return CHECK_REF_FORMAT_ERROR;
|
||||
}
|
||||
|
||||
last = ch;
|
||||
/* scan the rest of the path component */
|
||||
while ((ch = *cp++) != 0) {
|
||||
bad_type = bad_ref_char(ch);
|
||||
if (bad_type) {
|
||||
if (bad_type)
|
||||
return CHECK_REF_FORMAT_ERROR;
|
||||
}
|
||||
if (ch == '/')
|
||||
break;
|
||||
if (ch == '.' && *cp == '.')
|
||||
if (last == '.' && ch == '.')
|
||||
return CHECK_REF_FORMAT_ERROR;
|
||||
if (last == '@' && ch == '{')
|
||||
return CHECK_REF_FORMAT_ERROR;
|
||||
last = ch;
|
||||
}
|
||||
level++;
|
||||
if (!ch) {
|
||||
if (ref <= cp - 2 && cp[-2] == '.')
|
||||
return CHECK_REF_FORMAT_ERROR;
|
||||
if (level < 2)
|
||||
return CHECK_REF_FORMAT_ONELEVEL;
|
||||
if (has_extension(ref, ".lock"))
|
||||
return CHECK_REF_FORMAT_ERROR;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -1644,3 +1657,102 @@ struct ref *find_ref_by_name(const struct ref *list, const char *name)
|
||||
return (struct ref *)list;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* generate a format suitable for scanf from a ref_rev_parse_rules
|
||||
* rule, that is replace the "%.*s" spec with a "%s" spec
|
||||
*/
|
||||
static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
|
||||
{
|
||||
char *spec;
|
||||
|
||||
spec = strstr(rule, "%.*s");
|
||||
if (!spec || strstr(spec + 4, "%.*s"))
|
||||
die("invalid rule in ref_rev_parse_rules: %s", rule);
|
||||
|
||||
/* copy all until spec */
|
||||
strncpy(scanf_fmt, rule, spec - rule);
|
||||
scanf_fmt[spec - rule] = '\0';
|
||||
/* copy new spec */
|
||||
strcat(scanf_fmt, "%s");
|
||||
/* copy remaining rule */
|
||||
strcat(scanf_fmt, spec + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
char *shorten_unambiguous_ref(const char *ref)
|
||||
{
|
||||
int i;
|
||||
static char **scanf_fmts;
|
||||
static int nr_rules;
|
||||
char *short_name;
|
||||
|
||||
/* pre generate scanf formats from ref_rev_parse_rules[] */
|
||||
if (!nr_rules) {
|
||||
size_t total_len = 0;
|
||||
|
||||
/* the rule list is NULL terminated, count them first */
|
||||
for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
|
||||
/* no +1 because strlen("%s") < strlen("%.*s") */
|
||||
total_len += strlen(ref_rev_parse_rules[nr_rules]);
|
||||
|
||||
scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
|
||||
|
||||
total_len = 0;
|
||||
for (i = 0; i < nr_rules; i++) {
|
||||
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
|
||||
+ total_len;
|
||||
gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
|
||||
total_len += strlen(ref_rev_parse_rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* bail out if there are no rules */
|
||||
if (!nr_rules)
|
||||
return xstrdup(ref);
|
||||
|
||||
/* buffer for scanf result, at most ref must fit */
|
||||
short_name = xstrdup(ref);
|
||||
|
||||
/* skip first rule, it will always match */
|
||||
for (i = nr_rules - 1; i > 0 ; --i) {
|
||||
int j;
|
||||
int short_name_len;
|
||||
|
||||
if (1 != sscanf(ref, scanf_fmts[i], short_name))
|
||||
continue;
|
||||
|
||||
short_name_len = strlen(short_name);
|
||||
|
||||
/*
|
||||
* check if the short name resolves to a valid ref,
|
||||
* but use only rules prior to the matched one
|
||||
*/
|
||||
for (j = 0; j < i; j++) {
|
||||
const char *rule = ref_rev_parse_rules[j];
|
||||
unsigned char short_objectname[20];
|
||||
char refname[PATH_MAX];
|
||||
|
||||
/*
|
||||
* the short name is ambiguous, if it resolves
|
||||
* (with this previous rule) to a valid ref
|
||||
* read_ref() returns 0 on success
|
||||
*/
|
||||
mksnpath(refname, sizeof(refname),
|
||||
rule, short_name_len, short_name);
|
||||
if (!read_ref(refname, short_objectname))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* short name is non-ambiguous if all previous rules
|
||||
* haven't resolved to a valid ref
|
||||
*/
|
||||
if (j == i)
|
||||
return short_name;
|
||||
}
|
||||
|
||||
free(short_name);
|
||||
return xstrdup(ref);
|
||||
}
|
||||
|
||||
2
refs.h
2
refs.h
@@ -20,6 +20,7 @@ struct ref_lock {
|
||||
typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
|
||||
extern int head_ref(each_ref_fn, void *);
|
||||
extern int for_each_ref(each_ref_fn, void *);
|
||||
extern int for_each_ref_in(const char *, each_ref_fn, void *);
|
||||
extern int for_each_tag_ref(each_ref_fn, void *);
|
||||
extern int for_each_branch_ref(each_ref_fn, void *);
|
||||
extern int for_each_remote_ref(each_ref_fn, void *);
|
||||
@@ -80,6 +81,7 @@ extern int for_each_reflog(each_ref_fn, void *);
|
||||
extern int check_ref_format(const char *target);
|
||||
|
||||
extern const char *prettify_ref(const struct ref *ref);
|
||||
extern char *shorten_unambiguous_ref(const char *ref);
|
||||
|
||||
/** rename ref, return 0 on success **/
|
||||
extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
|
||||
|
||||
18
remote.c
18
remote.c
@@ -667,6 +667,17 @@ struct remote *remote_get(const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int remote_is_configured(const char *name)
|
||||
{
|
||||
int i;
|
||||
read_config();
|
||||
|
||||
for (i = 0; i < remotes_nr; i++)
|
||||
if (!strcmp(name, remotes[i]->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int for_each_remote(each_remote_fn fn, void *priv)
|
||||
{
|
||||
int i, result = 0;
|
||||
@@ -1170,8 +1181,9 @@ struct branch *branch_get(const char *name)
|
||||
for (i = 0; i < ret->merge_nr; i++) {
|
||||
ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
|
||||
ret->merge[i]->src = xstrdup(ret->merge_name[i]);
|
||||
remote_find_tracking(ret->remote,
|
||||
ret->merge[i]);
|
||||
if (remote_find_tracking(ret->remote, ret->merge[i])
|
||||
&& !strcmp(ret->remote_name, "."))
|
||||
ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1451,6 +1463,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
|
||||
base = branch->merge[0]->dst;
|
||||
if (!prefixcmp(base, "refs/remotes/")) {
|
||||
base += strlen("refs/remotes/");
|
||||
} else if (!prefixcmp(base, "refs/heads/")) {
|
||||
base += strlen("refs/heads/");
|
||||
}
|
||||
if (!num_theirs)
|
||||
strbuf_addf(sb, "Your branch is ahead of '%s' "
|
||||
|
||||
1
remote.h
1
remote.h
@@ -45,6 +45,7 @@ struct remote {
|
||||
};
|
||||
|
||||
struct remote *remote_get(const char *name);
|
||||
int remote_is_configured(const char *name);
|
||||
|
||||
typedef int each_remote_fn(struct remote *remote, void *priv);
|
||||
int for_each_remote(each_remote_fn fn, void *priv);
|
||||
|
||||
@@ -85,6 +85,8 @@ struct rev_info {
|
||||
struct log_info *loginfo;
|
||||
int nr, total;
|
||||
const char *mime_boundary;
|
||||
const char *patch_suffix;
|
||||
int numbered_files;
|
||||
char *message_id;
|
||||
struct string_list *ref_message_ids;
|
||||
const char *add_signoff;
|
||||
|
||||
4
setup.c
4
setup.c
@@ -434,7 +434,7 @@ int git_config_perm(const char *var, const char *value)
|
||||
|
||||
/*
|
||||
* Treat values 0, 1 and 2 as compatibility cases, otherwise it is
|
||||
* a chmod value.
|
||||
* a chmod value to restrict to.
|
||||
*/
|
||||
switch (i) {
|
||||
case PERM_UMASK: /* 0 */
|
||||
@@ -456,7 +456,7 @@ int git_config_perm(const char *var, const char *value)
|
||||
* Mask filemode value. Others can not get write permission.
|
||||
* x flags for directories are handled separately.
|
||||
*/
|
||||
return i & 0666;
|
||||
return -(i & 0666);
|
||||
}
|
||||
|
||||
int check_repository_format_version(const char *var, const char *value, void *cb)
|
||||
|
||||
101
sha1-lookup.c
101
sha1-lookup.c
@@ -1,6 +1,107 @@
|
||||
#include "cache.h"
|
||||
#include "sha1-lookup.h"
|
||||
|
||||
static uint32_t take2(const unsigned char *sha1)
|
||||
{
|
||||
return ((sha1[0] << 8) | sha1[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Conventional binary search loop looks like this:
|
||||
*
|
||||
* do {
|
||||
* int mi = (lo + hi) / 2;
|
||||
* int cmp = "entry pointed at by mi" minus "target";
|
||||
* if (!cmp)
|
||||
* return (mi is the wanted one)
|
||||
* if (cmp > 0)
|
||||
* hi = mi; "mi is larger than target"
|
||||
* else
|
||||
* lo = mi+1; "mi is smaller than target"
|
||||
* } while (lo < hi);
|
||||
*
|
||||
* The invariants are:
|
||||
*
|
||||
* - When entering the loop, lo points at a slot that is never
|
||||
* above the target (it could be at the target), hi points at a
|
||||
* slot that is guaranteed to be above the target (it can never
|
||||
* be at the target).
|
||||
*
|
||||
* - We find a point 'mi' between lo and hi (mi could be the same
|
||||
* as lo, but never can be the same as hi), and check if it hits
|
||||
* the target. There are three cases:
|
||||
*
|
||||
* - if it is a hit, we are happy.
|
||||
*
|
||||
* - if it is strictly higher than the target, we update hi with
|
||||
* it.
|
||||
*
|
||||
* - if it is strictly lower than the target, we update lo to be
|
||||
* one slot after it, because we allow lo to be at the target.
|
||||
*
|
||||
* When choosing 'mi', we do not have to take the "middle" but
|
||||
* anywhere in between lo and hi, as long as lo <= mi < hi is
|
||||
* satisfied. When we somehow know that the distance between the
|
||||
* target and lo is much shorter than the target and hi, we could
|
||||
* pick mi that is much closer to lo than the midway.
|
||||
*/
|
||||
/*
|
||||
* The table should contain "nr" elements.
|
||||
* The sha1 of element i (between 0 and nr - 1) should be returned
|
||||
* by "fn(i, table)".
|
||||
*/
|
||||
int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
|
||||
sha1_access_fn fn)
|
||||
{
|
||||
size_t hi = nr;
|
||||
size_t lo = 0;
|
||||
size_t mi = 0;
|
||||
|
||||
if (!nr)
|
||||
return -1;
|
||||
|
||||
if (nr != 1) {
|
||||
size_t lov, hiv, miv, ofs;
|
||||
|
||||
for (ofs = 0; ofs < 18; ofs += 2) {
|
||||
lov = take2(fn(0, table) + ofs);
|
||||
hiv = take2(fn(nr - 1, table) + ofs);
|
||||
miv = take2(sha1 + ofs);
|
||||
if (miv < lov)
|
||||
return -1;
|
||||
if (hiv < miv)
|
||||
return -1 - nr;
|
||||
if (lov != hiv) {
|
||||
/*
|
||||
* At this point miv could be equal
|
||||
* to hiv (but sha1 could still be higher);
|
||||
* the invariant of (mi < hi) should be
|
||||
* kept.
|
||||
*/
|
||||
mi = (nr - 1) * (miv - lov) / (hiv - lov);
|
||||
if (lo <= mi && mi < hi)
|
||||
break;
|
||||
die("BUG: assertion failed in binary search");
|
||||
}
|
||||
}
|
||||
if (18 <= ofs)
|
||||
die("cannot happen -- lo and hi are identical");
|
||||
}
|
||||
|
||||
do {
|
||||
int cmp;
|
||||
cmp = hashcmp(fn(mi, table), sha1);
|
||||
if (!cmp)
|
||||
return mi;
|
||||
if (cmp > 0)
|
||||
hi = mi;
|
||||
else
|
||||
lo = mi + 1;
|
||||
mi = (hi + lo) / 2;
|
||||
} while (lo < hi);
|
||||
return -lo-1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conventional binary search loop looks like this:
|
||||
*
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
#ifndef SHA1_LOOKUP_H
|
||||
#define SHA1_LOOKUP_H
|
||||
|
||||
typedef const unsigned char *sha1_access_fn(size_t index, void *table);
|
||||
|
||||
extern int sha1_pos(const unsigned char *sha1,
|
||||
void *table,
|
||||
size_t nr,
|
||||
sha1_access_fn fn);
|
||||
|
||||
extern int sha1_entry_pos(const void *table,
|
||||
size_t elem_size,
|
||||
size_t key_offset,
|
||||
|
||||
14
sha1_file.c
14
sha1_file.c
@@ -2216,11 +2216,15 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the just written object into its final resting place
|
||||
* Move the just written object into its final resting place.
|
||||
* NEEDSWORK: this should be renamed to finalize_temp_file() as
|
||||
* "moving" is only a part of what it does, when no patch between
|
||||
* master to pu changes the call sites of this function.
|
||||
*/
|
||||
int move_temp_to_file(const char *tmpfile, const char *filename)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (link(tmpfile, filename))
|
||||
ret = errno;
|
||||
|
||||
@@ -2232,12 +2236,12 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
|
||||
*
|
||||
* The same holds for FAT formatted media.
|
||||
*
|
||||
* When this succeeds, we just return 0. We have nothing
|
||||
* When this succeeds, we just return. We have nothing
|
||||
* left to unlink.
|
||||
*/
|
||||
if (ret && ret != EEXIST) {
|
||||
if (!rename(tmpfile, filename))
|
||||
return 0;
|
||||
goto out;
|
||||
ret = errno;
|
||||
}
|
||||
unlink(tmpfile);
|
||||
@@ -2248,6 +2252,9 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
|
||||
/* FIXME!!! Collision check here ? */
|
||||
}
|
||||
|
||||
out:
|
||||
if (set_shared_perm(filename, (S_IFREG|0444)))
|
||||
return error("unable to set permission to '%s'", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2272,7 +2279,6 @@ static void close_sha1_file(int fd)
|
||||
{
|
||||
if (fsync_object_files)
|
||||
fsync_or_die(fd, "sha1 file");
|
||||
fchmod(fd, 0444);
|
||||
if (close(fd) != 0)
|
||||
die("error when closing sha1 file (%s)", strerror(errno));
|
||||
}
|
||||
|
||||
12
sha1_name.c
12
sha1_name.c
@@ -242,10 +242,10 @@ static int ambiguous_path(const char *path, int len)
|
||||
* *string and *len will only be substituted, and *string returned (for
|
||||
* later free()ing) if the string passed in is of the form @{-<n>}.
|
||||
*/
|
||||
static char *substitute_nth_last_branch(const char **string, int *len)
|
||||
static char *substitute_branch_name(const char **string, int *len)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret = interpret_nth_last_branch(*string, &buf);
|
||||
int ret = interpret_branch_name(*string, &buf);
|
||||
|
||||
if (ret == *len) {
|
||||
size_t size;
|
||||
@@ -259,7 +259,7 @@ static char *substitute_nth_last_branch(const char **string, int *len)
|
||||
|
||||
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
|
||||
{
|
||||
char *last_branch = substitute_nth_last_branch(&str, &len);
|
||||
char *last_branch = substitute_branch_name(&str, &len);
|
||||
const char **p, *r;
|
||||
int refs_found = 0;
|
||||
|
||||
@@ -288,7 +288,7 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
|
||||
|
||||
int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
|
||||
{
|
||||
char *last_branch = substitute_nth_last_branch(&str, &len);
|
||||
char *last_branch = substitute_branch_name(&str, &len);
|
||||
const char **p;
|
||||
int logs_found = 0;
|
||||
|
||||
@@ -355,7 +355,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret;
|
||||
/* try the @{-N} syntax for n-th checkout */
|
||||
ret = interpret_nth_last_branch(str+at, &buf);
|
||||
ret = interpret_branch_name(str+at, &buf);
|
||||
if (ret > 0) {
|
||||
/* substitute this branch name and restart */
|
||||
return get_sha1_1(buf.buf, buf.len, sha1);
|
||||
@@ -750,7 +750,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
|
||||
* If the input was ok but there are not N branch switches in the
|
||||
* reflog, it returns 0.
|
||||
*/
|
||||
int interpret_nth_last_branch(const char *name, struct strbuf *buf)
|
||||
int interpret_branch_name(const char *name, struct strbuf *buf)
|
||||
{
|
||||
long nth;
|
||||
int i, retval;
|
||||
|
||||
17
strbuf.c
17
strbuf.c
@@ -1,4 +1,5 @@
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
|
||||
int prefixcmp(const char *str, const char *prefix)
|
||||
{
|
||||
@@ -357,3 +358,19 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int strbuf_branchname(struct strbuf *sb, const char *name)
|
||||
{
|
||||
int len = strlen(name);
|
||||
if (interpret_branch_name(name, sb) == len)
|
||||
return 0;
|
||||
strbuf_add(sb, name, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
|
||||
{
|
||||
strbuf_branchname(sb, name);
|
||||
strbuf_splice(sb, 0, 0, "refs/heads/", 11);
|
||||
return check_ref_format(sb->buf);
|
||||
}
|
||||
|
||||
3
strbuf.h
3
strbuf.h
@@ -131,4 +131,7 @@ extern int strbuf_getline(struct strbuf *, FILE *, int);
|
||||
extern void stripspace(struct strbuf *buf, int skip_comments);
|
||||
extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
|
||||
|
||||
extern int strbuf_branchname(struct strbuf *sb, const char *name);
|
||||
extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
|
||||
|
||||
#endif /* STRBUF_H */
|
||||
|
||||
@@ -7,7 +7,10 @@ if test -n "$NO_SVN_TESTS"
|
||||
then
|
||||
say 'skipping git svn tests, NO_SVN_TESTS defined'
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git svn tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
GIT_DIR=$PWD/.git
|
||||
@@ -19,7 +22,6 @@ if test $? -ne 1
|
||||
then
|
||||
say 'skipping git svn tests, svn not found'
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
svnrepo=$PWD/svnrepo
|
||||
@@ -43,7 +45,6 @@ then
|
||||
fi
|
||||
say "$err"
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
rawsvnrepo="$svnrepo"
|
||||
@@ -144,7 +145,6 @@ require_svnserve () {
|
||||
then
|
||||
say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ then
|
||||
say "skipping test, network testing disabled by default"
|
||||
say "(define GIT_TEST_HTTPD to enable)"
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
HTTPD_PARA=""
|
||||
@@ -36,7 +35,6 @@ if ! test -x "$LIB_HTTPD_PATH"
|
||||
then
|
||||
say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
|
||||
@@ -50,7 +48,6 @@ then
|
||||
then
|
||||
say "skipping test, at least Apache version 2 is required"
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
|
||||
|
||||
@@ -126,4 +126,41 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
|
||||
esac
|
||||
'
|
||||
|
||||
test_expect_success 'forced modes' '
|
||||
mkdir -p templates/hooks &&
|
||||
echo update-server-info >templates/hooks/post-update &&
|
||||
chmod +x templates/hooks/post-update &&
|
||||
echo : >random-file &&
|
||||
mkdir new &&
|
||||
(
|
||||
cd new &&
|
||||
umask 002 &&
|
||||
git init --shared=0660 --template=../templates &&
|
||||
>frotz &&
|
||||
git add frotz &&
|
||||
git commit -a -m initial &&
|
||||
git repack
|
||||
) &&
|
||||
find new/.git -print |
|
||||
xargs ls -ld >actual &&
|
||||
|
||||
# Everything must be unaccessible to others
|
||||
test -z "$(sed -n -e "/^.......---/d" actual)" &&
|
||||
|
||||
# All directories must have either 2770 or 770
|
||||
test -z "$(sed -n -e "/^drwxrw[sx]---/d" -e "/^d/p" actual)" &&
|
||||
|
||||
# post-update hook must be 0770
|
||||
test -z "$(sed -n -e "/post-update/{
|
||||
/^-rwxrwx---/d
|
||||
p
|
||||
}" actual)" &&
|
||||
|
||||
# All files inside objects must be 0440
|
||||
test -z "$(sed -n -e "/objects\//{
|
||||
/^d/d
|
||||
/^-r--r-----/d
|
||||
}" actual)"
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
42
t/t2013-checkout-submodule.sh
Executable file
42
t/t2013-checkout-submodule.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='checkout can handle submodules'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
mkdir submodule &&
|
||||
(cd submodule &&
|
||||
git init &&
|
||||
test_commit first) &&
|
||||
git add submodule &&
|
||||
test_tick &&
|
||||
git commit -m superproject &&
|
||||
(cd submodule &&
|
||||
test_commit second) &&
|
||||
git add submodule &&
|
||||
test_tick &&
|
||||
git commit -m updated.superproject
|
||||
'
|
||||
|
||||
test_expect_success '"reset <submodule>" updates the index' '
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet &&
|
||||
git diff-index --quiet --cached HEAD &&
|
||||
test_must_fail git reset HEAD^ submodule &&
|
||||
test_must_fail git diff-files --quiet &&
|
||||
git reset submodule &&
|
||||
git diff-files --quiet
|
||||
'
|
||||
|
||||
test_expect_success '"checkout <submodule>" updates the index only' '
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet &&
|
||||
git diff-index --quiet --cached HEAD &&
|
||||
git checkout HEAD^ submodule &&
|
||||
test_must_fail git diff-files --quiet &&
|
||||
git checkout HEAD submodule &&
|
||||
git diff-files --quiet
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -135,4 +135,10 @@ test_expect_success \
|
||||
EOF
|
||||
test_output'
|
||||
|
||||
test_expect_success 'ls-tree filter is leading path match' '
|
||||
git ls-tree $tree pa path3/a >current &&
|
||||
>expected &&
|
||||
test_output
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
test_description='add -i basic tests'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PERL; then
|
||||
say 'skipping git add -i tests, perl not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
test_expect_success 'setup (initial)' '
|
||||
echo content >file &&
|
||||
git add file &&
|
||||
|
||||
@@ -16,7 +16,6 @@ if ! test_have_prereq SYMLINKS
|
||||
then
|
||||
say 'Symbolic links not supported, skipping tests.'
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
test_expect_success \
|
||||
|
||||
@@ -62,4 +62,12 @@ test_expect_success \
|
||||
'git diff-index --cached $tree -- file0/ >current &&
|
||||
compare_diff_raw current expected'
|
||||
|
||||
test_expect_success 'diff-tree pathspec' '
|
||||
tree2=$(git write-tree) &&
|
||||
echo "$tree2" &&
|
||||
git diff-tree -r --name-only $tree $tree2 -- pa path1/a >current &&
|
||||
>expected &&
|
||||
test_cmp expected current
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -13,7 +13,6 @@ if ! test_have_prereq SYMLINKS
|
||||
then
|
||||
say 'Symbolic links not supported, skipping tests.'
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
cat > expected << EOF
|
||||
|
||||
@@ -246,11 +246,12 @@ format-patch --stdout initial..master
|
||||
format-patch --stdout --no-numbered initial..master
|
||||
format-patch --stdout --numbered initial..master
|
||||
format-patch --attach --stdout initial..side
|
||||
format-patch --attach --stdout --suffix=.diff initial..side
|
||||
format-patch --attach --stdout initial..master^
|
||||
format-patch --attach --stdout initial..master
|
||||
format-patch --inline --stdout initial..side
|
||||
format-patch --inline --stdout initial..master^
|
||||
format-patch --inline --stdout initial..master
|
||||
format-patch --inline --stdout --numbered-files initial..master
|
||||
format-patch --inline --stdout initial..master
|
||||
format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
|
||||
config format.subjectprefix DIFFERENT_PREFIX
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
$ git format-patch --attach --stdout --suffix=.diff initial..side
|
||||
From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
|
||||
From: A U Thor <author@example.com>
|
||||
Date: Mon, 26 Jun 2006 00:03:00 +0000
|
||||
Subject: [PATCH] Side
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/plain; charset=UTF-8; format=fixed
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
---
|
||||
dir/sub | 2 ++
|
||||
file0 | 3 +++
|
||||
file3 | 4 ++++
|
||||
3 files changed, 9 insertions(+), 0 deletions(-)
|
||||
create mode 100644 file3
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="0001-Side.diff"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment; filename="0001-Side.diff"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..7289e35 100644
|
||||
--- a/dir/sub
|
||||
+++ b/dir/sub
|
||||
@@ -1,2 +1,4 @@
|
||||
A
|
||||
B
|
||||
+1
|
||||
+2
|
||||
diff --git a/file0 b/file0
|
||||
index 01e79c3..f4615da 100644
|
||||
--- a/file0
|
||||
+++ b/file0
|
||||
@@ -1,3 +1,6 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
+A
|
||||
+B
|
||||
+C
|
||||
diff --git a/file3 b/file3
|
||||
new file mode 100644
|
||||
index 0000000..7289e35
|
||||
--- /dev/null
|
||||
+++ b/file3
|
||||
@@ -0,0 +1,4 @@
|
||||
+A
|
||||
+B
|
||||
+1
|
||||
+2
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n--
|
||||
|
||||
|
||||
$
|
||||
@@ -22,9 +22,9 @@ This is the second commit.
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Type: text/x-patch; name="0001-Second.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Disposition: attachment; filename="0001-Second.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Type: text/x-patch; name="0002-Third.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Disposition: attachment; filename="0002-Third.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 8422d40..cead32e 100644
|
||||
@@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Type: text/x-patch; name="0003-Side.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Disposition: attachment; filename="0003-Side.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..7289e35 100644
|
||||
|
||||
@@ -22,9 +22,9 @@ This is the second commit.
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Type: text/x-patch; name="0001-Second.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Disposition: attachment; filename="0001-Second.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Type: text/x-patch; name="0002-Third.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Disposition: attachment; filename="0002-Third.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 8422d40..cead32e 100644
|
||||
|
||||
@@ -20,9 +20,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Type: text/x-patch; name="0001-Side.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Disposition: attachment; filename="0001-Side.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..7289e35 100644
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
$ git format-patch --inline --stdout --numbered-files initial..master
|
||||
From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
|
||||
From: A U Thor <author@example.com>
|
||||
Date: Mon, 26 Jun 2006 00:01:00 +0000
|
||||
Subject: [PATCH 1/3] Second
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/plain; charset=UTF-8; format=fixed
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
This is the second commit.
|
||||
---
|
||||
dir/sub | 2 ++
|
||||
file0 | 3 +++
|
||||
file2 | 3 ---
|
||||
3 files changed, 5 insertions(+), 3 deletions(-)
|
||||
delete mode 100644 file2
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="1"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="1"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
--- a/dir/sub
|
||||
+++ b/dir/sub
|
||||
@@ -1,2 +1,4 @@
|
||||
A
|
||||
B
|
||||
+C
|
||||
+D
|
||||
diff --git a/file0 b/file0
|
||||
index 01e79c3..b414108 100644
|
||||
--- a/file0
|
||||
+++ b/file0
|
||||
@@ -1,3 +1,6 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
+4
|
||||
+5
|
||||
+6
|
||||
diff --git a/file2 b/file2
|
||||
deleted file mode 100644
|
||||
index 01e79c3..0000000
|
||||
--- a/file2
|
||||
+++ /dev/null
|
||||
@@ -1,3 +0,0 @@
|
||||
-1
|
||||
-2
|
||||
-3
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n--
|
||||
|
||||
|
||||
|
||||
From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
|
||||
From: A U Thor <author@example.com>
|
||||
Date: Mon, 26 Jun 2006 00:02:00 +0000
|
||||
Subject: [PATCH 2/3] Third
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/plain; charset=UTF-8; format=fixed
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
---
|
||||
dir/sub | 2 ++
|
||||
file1 | 3 +++
|
||||
2 files changed, 5 insertions(+), 0 deletions(-)
|
||||
create mode 100644 file1
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="2"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="2"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 8422d40..cead32e 100644
|
||||
--- a/dir/sub
|
||||
+++ b/dir/sub
|
||||
@@ -2,3 +2,5 @@ A
|
||||
B
|
||||
C
|
||||
D
|
||||
+E
|
||||
+F
|
||||
diff --git a/file1 b/file1
|
||||
new file mode 100644
|
||||
index 0000000..b1e6722
|
||||
--- /dev/null
|
||||
+++ b/file1
|
||||
@@ -0,0 +1,3 @@
|
||||
+A
|
||||
+B
|
||||
+C
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n--
|
||||
|
||||
|
||||
|
||||
From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
|
||||
From: A U Thor <author@example.com>
|
||||
Date: Mon, 26 Jun 2006 00:03:00 +0000
|
||||
Subject: [PATCH 3/3] Side
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/plain; charset=UTF-8; format=fixed
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
---
|
||||
dir/sub | 2 ++
|
||||
file0 | 3 +++
|
||||
file3 | 4 ++++
|
||||
3 files changed, 9 insertions(+), 0 deletions(-)
|
||||
create mode 100644 file3
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="3"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="3"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..7289e35 100644
|
||||
--- a/dir/sub
|
||||
+++ b/dir/sub
|
||||
@@ -1,2 +1,4 @@
|
||||
A
|
||||
B
|
||||
+1
|
||||
+2
|
||||
diff --git a/file0 b/file0
|
||||
index 01e79c3..f4615da 100644
|
||||
--- a/file0
|
||||
+++ b/file0
|
||||
@@ -1,3 +1,6 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
+A
|
||||
+B
|
||||
+C
|
||||
diff --git a/file3 b/file3
|
||||
new file mode 100644
|
||||
index 0000000..7289e35
|
||||
--- /dev/null
|
||||
+++ b/file3
|
||||
@@ -0,0 +1,4 @@
|
||||
+A
|
||||
+B
|
||||
+1
|
||||
+2
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n--
|
||||
|
||||
|
||||
$
|
||||
@@ -22,9 +22,9 @@ This is the second commit.
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Type: text/x-patch; name="0001-Second.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Disposition: inline; filename="0001-Second.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Type: text/x-patch; name="0002-Third.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Disposition: inline; filename="0002-Third.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 8422d40..cead32e 100644
|
||||
@@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Type: text/x-patch; name="0003-Side.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Disposition: inline; filename="0003-Side.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..7289e35 100644
|
||||
|
||||
@@ -22,9 +22,9 @@ This is the second commit.
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Type: text/x-patch; name="0001-Second.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Disposition: inline; filename="0001-Second.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Type: text/x-patch; name="0002-Third.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Disposition: inline; filename="0002-Third.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 8422d40..cead32e 100644
|
||||
@@ -129,9 +129,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Type: text/x-patch; name="0003-Side.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Disposition: inline; filename="0003-Side.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..7289e35 100644
|
||||
|
||||
@@ -22,9 +22,9 @@ This is the second commit.
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Type: text/x-patch; name="0001-Second.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Disposition: inline; filename="0001-Second.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
@@ -80,9 +80,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Type: text/x-patch; name="0002-Third.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
|
||||
Content-Disposition: inline; filename="0002-Third.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 8422d40..cead32e 100644
|
||||
|
||||
@@ -22,9 +22,9 @@ This is the second commit.
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Type: text/x-patch; name="0001-Second.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
|
||||
Content-Disposition: inline; filename="0001-Second.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..8422d40 100644
|
||||
|
||||
@@ -20,9 +20,9 @@ Content-Transfer-Encoding: 8bit
|
||||
|
||||
|
||||
--------------g-i-t--v-e-r-s-i-o-n
|
||||
Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Type: text/x-patch; name="0001-Side.patch"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
|
||||
Content-Disposition: inline; filename="0001-Side.patch"
|
||||
|
||||
diff --git a/dir/sub b/dir/sub
|
||||
index 35d242b..7289e35 100644
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user