Compare commits
23 Commits
v1.8.1-rc3
...
v1.8.0.3
Author | SHA1 | Date | |
---|---|---|---|
15999998fb | |||
6ecc01f26c | |||
2b05d9f917 | |||
008c208c2c | |||
197a80d7d9 | |||
91583a6a85 | |||
d658835c19 | |||
4017edcfac | |||
8c6bda0f4d | |||
6cf9614df6 | |||
0a1b59eb86 | |||
4f96f1fbab | |||
0b830ac521 | |||
c2999adcd5 | |||
a7b5e9141e | |||
2b1965863b | |||
ffcd76bda9 | |||
e970ec356b | |||
21b340181b | |||
df54d59566 | |||
ccc3ae799c | |||
66afe50b43 | |||
854dfda8be |
1
.gitignore
vendored
1
.gitignore
vendored
@ -125,7 +125,6 @@
|
||||
/git-remote-fd
|
||||
/git-remote-ext
|
||||
/git-remote-testgit
|
||||
/git-remote-testsvn
|
||||
/git-repack
|
||||
/git-replace
|
||||
/git-repo-config
|
||||
|
@ -21,7 +21,6 @@ ARTICLES += git-tools
|
||||
ARTICLES += git-bisect-lk2009
|
||||
# with their own formatting rules.
|
||||
SP_ARTICLES = user-manual
|
||||
SP_ARTICLES += howto/new-command
|
||||
SP_ARTICLES += howto/revert-branch-rebase
|
||||
SP_ARTICLES += howto/using-merge-subtree
|
||||
SP_ARTICLES += howto/using-signed-tag-in-pull-request
|
||||
@ -32,6 +31,7 @@ SP_ARTICLES += howto/separating-topic-branches
|
||||
SP_ARTICLES += howto/revert-a-faulty-merge
|
||||
SP_ARTICLES += howto/recover-corrupted-blob-object
|
||||
SP_ARTICLES += howto/rebuild-from-update-hook
|
||||
SP_ARTICLES += howto/rebuild-from-update-hook
|
||||
SP_ARTICLES += howto/rebase-from-internal-branch
|
||||
SP_ARTICLES += howto/maintain-git
|
||||
API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
|
||||
@ -331,7 +331,7 @@ $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
|
||||
|
||||
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
|
||||
$(QUIET_GEN)$(RM) $@+ $@ && \
|
||||
'$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
|
||||
'$(SHELL_PATH_SQ)' ./howto-index.sh $(sort $(wildcard howto/*.txt)) >$@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
|
||||
|
14
Documentation/RelNotes/1.8.0.3.txt
Normal file
14
Documentation/RelNotes/1.8.0.3.txt
Normal file
@ -0,0 +1,14 @@
|
||||
Git v1.8.0.3 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.8.0.2
|
||||
--------------------
|
||||
|
||||
* "git log -p -S<string>" did not apply the textconv filter while
|
||||
looking for the <string>.
|
||||
|
||||
* In the documentation, some invalid example e-mail addresses were
|
||||
formatted into mailto: links.
|
||||
|
||||
Also contains many documentation updates backported from the 'master'
|
||||
branch that is preparing for the upcoming 1.8.1 release.
|
@ -1,241 +0,0 @@
|
||||
Git v1.8.1 Release Notes
|
||||
========================
|
||||
|
||||
Backward compatibility notes
|
||||
----------------------------
|
||||
|
||||
In the next major release (not *this* one), we will change the
|
||||
behavior of the "git push" command.
|
||||
|
||||
When "git push [$there]" does not say what to push, we have used the
|
||||
traditional "matching" semantics so far (all your branches were sent
|
||||
to the remote as long as there already are branches of the same name
|
||||
over there). We will use the "simple" semantics that pushes the
|
||||
current branch to the branch with the same name, only when the current
|
||||
branch is set to integrate with that remote branch. There is a user
|
||||
preference configuration variable "push.default" to change this, and
|
||||
"git push" will warn about the upcoming change until you set this
|
||||
variable in this release.
|
||||
|
||||
"git branch --set-upstream" is deprecated and may be removed in a
|
||||
relatively distant future. "git branch [-u|--set-upstream-to]" has
|
||||
been introduced with a saner order of arguments to replace it.
|
||||
|
||||
|
||||
Updates since v1.8.0
|
||||
--------------------
|
||||
|
||||
UI, Workflows & Features
|
||||
|
||||
* Command-line completion scripts for tcsh and zsh have been added.
|
||||
|
||||
* A new remote-helper interface for Mercurial has been added to
|
||||
contrib/remote-helpers.
|
||||
|
||||
* We used to have a workaround for a bug in ancient "less" that
|
||||
causes it to exit without any output when the terminal is resized.
|
||||
The bug has been fixed in "less" version 406 (June 2007), and the
|
||||
workaround has been removed in this release.
|
||||
|
||||
* Some documentation pages that used to ship only in the plain text
|
||||
format are now formatted in HTML as well.
|
||||
|
||||
* "git-prompt" scriptlet (in contrib/completion) can be told to paint
|
||||
pieces of the hints in the prompt string in colors.
|
||||
|
||||
* A new configuration variable "diff.context" can be used to
|
||||
give the default number of context lines in the patch output, to
|
||||
override the hardcoded default of 3 lines.
|
||||
|
||||
* When "git checkout" checks out a branch, it tells the user how far
|
||||
behind (or ahead) the new branch is relative to the remote tracking
|
||||
branch it builds upon. The message now also advises how to sync
|
||||
them up by pushing or pulling. This can be disabled with the
|
||||
advice.statusHints configuration variable.
|
||||
|
||||
* "git config --get" used to diagnose presence of multiple
|
||||
definitions of the same variable in the same configuration file as
|
||||
an error, but it now applies the "last one wins" rule used by the
|
||||
internal configuration logic. Strictly speaking, this may be an
|
||||
API regression but it is expected that nobody will notice it in
|
||||
practice.
|
||||
|
||||
* "git log -p -S<string>" now looks for the <string> after applying
|
||||
the textconv filter (if defined); earlier it inspected the contents
|
||||
of the blobs without filtering.
|
||||
|
||||
* "git format-patch" learned the "--notes=<ref>" option to give
|
||||
notes for the commit after the three-dash lines in its output.
|
||||
|
||||
* "git log --grep=<pcre>" learned to honor the "grep.patterntype"
|
||||
configuration set to "perl".
|
||||
|
||||
* "git replace -d <object>" now interprets <object> as an extended
|
||||
SHA-1 (e.g. HEAD~4 is allowed), instead of only accepting full hex
|
||||
object name.
|
||||
|
||||
* "git rm $submodule" used to punt on removing a submodule working
|
||||
tree to avoid losing the repository embedded in it. Because
|
||||
recent git uses a mechanism to separate the submodule repository
|
||||
from the submodule working tree, "git rm" learned to detect this
|
||||
case and removes the submodule working tree when it is safe to do so.
|
||||
|
||||
* "git send-email" used to prompt for the sender address, even when
|
||||
the committer identity is well specified (e.g. via user.name and
|
||||
user.email configuration variables). The command no longer gives
|
||||
this prompt when not necessary.
|
||||
|
||||
* "git send-email" did not allow non-address garbage strings to
|
||||
appear after addresses on Cc: lines in the patch files (and when
|
||||
told to pick them up to find more recipients), e.g.
|
||||
|
||||
Cc: Stable Kernel <stable@k.org> # for v3.2 and up
|
||||
|
||||
The command now strips " # for v3.2 and up" part before adding the
|
||||
remainder of this line to the list of recipients.
|
||||
|
||||
* "git submodule add" learned to add a new submodule at the same
|
||||
path as the path where an unrelated submodule was bound to in an
|
||||
existing revision via the "--name" option.
|
||||
|
||||
* "git submodule sync" learned the "--recursive" option.
|
||||
|
||||
* "diff.submodule" configuration variable can be used to give custom
|
||||
default value to the "git diff --submodule" option.
|
||||
|
||||
* "git symbolic-ref" learned the "-d $symref" option to delete the
|
||||
named symbolic ref, which is more intuitive way to spell it than
|
||||
"update-ref -d --no-deref $symref".
|
||||
|
||||
|
||||
Foreign Interface
|
||||
|
||||
* "git cvsimport" can be told to record timezones (other than GMT)
|
||||
per-author via its author info file.
|
||||
|
||||
* The remote helper interface to interact with subversion
|
||||
repositories (one of the GSoC 2012 projects) has been merged.
|
||||
|
||||
* The documentation for git(1) was pointing at a page at an external
|
||||
site for the list of authors that no longer existed. The link has
|
||||
been updated to point at an alternative site.
|
||||
|
||||
|
||||
Performance, Internal Implementation, etc.
|
||||
|
||||
* Compilation on Cygwin with newer header files are supported now.
|
||||
|
||||
* A couple of low-level implementation updates on MinGW.
|
||||
|
||||
* The logic to generate the initial advertisement from "upload-pack"
|
||||
(i.e. what is invoked by "git fetch" on the other side of the
|
||||
connection) to list what refs are available in the repository has
|
||||
been optimized.
|
||||
|
||||
* The logic to find set of attributes that match a given path has
|
||||
been optimized.
|
||||
|
||||
* Use preloadindex in "git diff-index" and "git update-index", which
|
||||
has a nice speedup on systems with slow stat calls (and even on
|
||||
Linux).
|
||||
|
||||
|
||||
Also contains minor documentation updates and code clean-ups.
|
||||
|
||||
|
||||
Fixes since v1.8.0
|
||||
------------------
|
||||
|
||||
Unless otherwise noted, all the fixes since v1.8.0 in the maintenance
|
||||
track are contained in this release (see release notes to them for
|
||||
details).
|
||||
|
||||
* The configuration parser had an unnecessary hardcoded limit on
|
||||
variable names that was not checked consistently.
|
||||
|
||||
* The "say" function in the test scaffolding incorrectly allowed
|
||||
"echo" to interpret "\a" as if it were a C-string asking for a
|
||||
BEL output.
|
||||
|
||||
* "git mergetool" feeds /dev/null as a common ancestor when dealing
|
||||
with an add/add conflict, but p4merge backend cannot handle
|
||||
it. Work it around by passing a temporary empty file.
|
||||
|
||||
* "git log -F -E --grep='<ere>'" failed to use the given <ere>
|
||||
pattern as extended regular expression, and instead looked for the
|
||||
string literally.
|
||||
|
||||
* "git grep -e pattern <tree>" asked the attribute system to read
|
||||
"<tree>:.gitattributes" file in the working tree, which was
|
||||
nonsense.
|
||||
|
||||
* A symbolic ref refs/heads/SYM was not correctly removed with "git
|
||||
branch -d SYM"; the command removed the ref pointed by SYM
|
||||
instead.
|
||||
|
||||
* Update "remote tracking branch" in the documentation to
|
||||
"remote-tracking branch".
|
||||
|
||||
* "git pull --rebase" run while the HEAD is detached tried to find
|
||||
the upstream branch of the detached HEAD (which by definition
|
||||
does not exist) and emitted unnecessary error messages.
|
||||
|
||||
* The refs/replace hierarchy was not mentioned in the
|
||||
repository-layout docs.
|
||||
|
||||
* Various rfc2047 quoting issues around a non-ASCII name on the
|
||||
From: line in the output from format-patch have been corrected.
|
||||
|
||||
* Sometimes curl_multi_timeout() function suggested a wrong timeout
|
||||
value when there is no file descriptor to wait on and the http
|
||||
transport ended up sleeping for minutes in select(2) system call.
|
||||
A workaround has been added for this.
|
||||
|
||||
* For a fetch refspec (or the result of applying wildcard on one),
|
||||
we always want the RHS to map to something inside "refs/"
|
||||
hierarchy, but the logic to check it was not exactly right.
|
||||
(merge 5c08c1f jc/maint-fetch-tighten-refname-check later to maint).
|
||||
|
||||
* "git diff -G<pattern>" did not honor textconv filter when looking
|
||||
for changes.
|
||||
|
||||
* Some HTTP servers ask for auth only during the actual packing phase
|
||||
(not in ls-remote phase); this is not really a recommended
|
||||
configuration, but the clients used to fail to authenticate with
|
||||
such servers.
|
||||
(merge 2e736fd jk/maint-http-half-auth-fetch later to maint).
|
||||
|
||||
* "git p4" used to try expanding malformed "$keyword$" that spans
|
||||
across multiple lines.
|
||||
|
||||
* Syntax highlighting in "gitweb" was not quite working.
|
||||
|
||||
* RSS feed from "gitweb" had a xss hole in its title output.
|
||||
|
||||
* "git config --path $key" segfaulted on "[section] key" (a boolean
|
||||
"true" spelled without "=", not "[section] key = true").
|
||||
|
||||
* "git checkout -b foo" while on an unborn branch did not say
|
||||
"Switched to a new branch 'foo'" like other cases.
|
||||
|
||||
* Various codepaths have workaround for a common misconfiguration to
|
||||
spell "UTF-8" as "utf8", but it was not used uniformly. Most
|
||||
notably, mailinfo (which is used by "git am") lacked this support.
|
||||
|
||||
* We failed to mention a file without any content change but whose
|
||||
permission bit was modified, or (worse yet) a new file without any
|
||||
content in the "git diff --stat" output.
|
||||
|
||||
* When "--stat-count" hides a diffstat for binary contents, the total
|
||||
number of added and removed lines at the bottom was computed
|
||||
incorrectly.
|
||||
|
||||
* When "--stat-count" hides a diffstat for unmerged paths, the total
|
||||
number of affected files at the bottom of the "diff --stat" output
|
||||
was computed incorrectly.
|
||||
|
||||
* "diff --shortstat" miscounted the total number of affected files
|
||||
when there were unmerged paths.
|
||||
|
||||
* "update-ref -d --deref SYM" to delete a ref through a symbolic ref
|
||||
that points to it did not remove it correctly.
|
@ -174,8 +174,7 @@ message starts, you can put a "From: " line to name that person.
|
||||
|
||||
You often want to add additional explanation about the patch,
|
||||
other than the commit message itself. Place such "cover letter"
|
||||
material between the three dash lines and the diffstat. Git-notes
|
||||
can also be inserted using the `--notes` option.
|
||||
material between the three dash lines and the diffstat.
|
||||
|
||||
Do not attach the patch as a MIME attachment, compressed or not.
|
||||
Do not let your e-mail client send quoted-printable. Do not let
|
||||
|
@ -160,10 +160,9 @@ advice.*::
|
||||
it resulted in a non-fast-forward error.
|
||||
statusHints::
|
||||
Show directions on how to proceed from the current
|
||||
state in the output of linkgit:git-status[1], in
|
||||
state in the output of linkgit:git-status[1] and in
|
||||
the template shown when writing commit messages in
|
||||
linkgit:git-commit[1], and in the help message shown
|
||||
by linkgit:git-checkout[1] when switching branch.
|
||||
linkgit:git-commit[1].
|
||||
commitBeforeMerge::
|
||||
Advice shown when linkgit:git-merge[1] refuses to
|
||||
merge to avoid overwriting local changes.
|
||||
@ -539,14 +538,14 @@ core.pager::
|
||||
`LESS` variable to some other value. Alternately,
|
||||
these settings can be overridden on a project or
|
||||
global basis by setting the `core.pager` option.
|
||||
Setting `core.pager` has no effect on the `LESS`
|
||||
Setting `core.pager` has no affect on the `LESS`
|
||||
environment variable behaviour above, so if you want
|
||||
to override git's default settings this way, you need
|
||||
to be explicit. For example, to disable the S option
|
||||
in a backward compatible manner, set `core.pager`
|
||||
to `less -+S`. This will be passed to the shell by
|
||||
git, which will translate the final command to
|
||||
`LESS=FRSX less -+S`.
|
||||
to `less -+$LESS -FRX`. This will be passed to the
|
||||
shell by git, which will translate the final command to
|
||||
`LESS=FRSX less -+FRSX -FRX`.
|
||||
|
||||
core.whitespace::
|
||||
A comma separated list of common whitespace problems to
|
||||
|
@ -56,10 +56,6 @@ diff.statGraphWidth::
|
||||
Limit the width of the graph part in --stat output. If set, applies
|
||||
to all commands generating --stat output except format-patch.
|
||||
|
||||
diff.context::
|
||||
Generate diffs with <n> lines of context instead of the default
|
||||
of 3. This value is overridden by the -U option.
|
||||
|
||||
diff.external::
|
||||
If this config variable is set, diff generation is not
|
||||
performed using the internal diff machinery, but using the
|
||||
@ -107,13 +103,6 @@ diff.suppressBlankEmpty::
|
||||
A boolean to inhibit the standard behavior of printing a space
|
||||
before each empty output line. Defaults to false.
|
||||
|
||||
diff.submodule::
|
||||
Specify the format in which differences in submodules are
|
||||
shown. The "log" format lists the commits in the range like
|
||||
linkgit:git-submodule[1] `summary` does. The "short" format
|
||||
format just shows the names of the commits at the beginning
|
||||
and end of the range. Defaults to short.
|
||||
|
||||
diff.wordRegex::
|
||||
A POSIX Extended Regular Expression used to determine what is a "word"
|
||||
when performing word-by-word difference calculations. Character
|
||||
|
@ -170,8 +170,7 @@ any of those replacements occurred.
|
||||
the commits in the range like linkgit:git-submodule[1] `summary` does.
|
||||
Omitting the `--submodule` option or specifying `--submodule=short`,
|
||||
uses the 'short' format. This format just shows the names of the commits
|
||||
at the beginning and end of the range. Can be tweaked via the
|
||||
`diff.submodule` configuration variable.
|
||||
at the beginning and end of the range.
|
||||
|
||||
--color[=<when>]::
|
||||
Show colored diff.
|
||||
|
@ -109,10 +109,6 @@ OPTIONS
|
||||
format. See linkgit:git-status[1] for details. Implies
|
||||
`--dry-run`.
|
||||
|
||||
--long::
|
||||
When doing a dry-run, give the output in a the long-format.
|
||||
Implies `--dry-run`.
|
||||
|
||||
-z::
|
||||
--null::
|
||||
When showing `short` or `porcelain` status output, terminate
|
||||
|
@ -137,19 +137,17 @@ This option can be used several times to provide several detection regexes.
|
||||
-A <author-conv-file>::
|
||||
CVS by default uses the Unix username when writing its
|
||||
commit logs. Using this option and an author-conv-file
|
||||
maps the name recorded in CVS to author name, e-mail and
|
||||
optional timezone:
|
||||
in this format
|
||||
+
|
||||
---------
|
||||
exon=Andreas Ericsson <ae@op5.se>
|
||||
spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
|
||||
spawn=Simon Pawn <spawn@frog-pond.org>
|
||||
|
||||
---------
|
||||
+
|
||||
'git cvsimport' will make it appear as those authors had
|
||||
their GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL set properly
|
||||
all along. If a timezone is specified, GIT_AUTHOR_DATE will
|
||||
have the corresponding offset applied.
|
||||
all along.
|
||||
+
|
||||
For convenience, this data is saved to `$GIT_DIR/cvs-authors`
|
||||
each time the '-A' option is provided and read from that same
|
||||
|
@ -20,7 +20,7 @@ SYNOPSIS
|
||||
[--ignore-if-in-upstream]
|
||||
[--subject-prefix=Subject-Prefix]
|
||||
[--to=<email>] [--cc=<email>]
|
||||
[--cover-letter] [--quiet] [--notes[=<ref>]]
|
||||
[--cover-letter] [--quiet]
|
||||
[<common diff options>]
|
||||
[ <since> | <revision range> ]
|
||||
|
||||
@ -191,18 +191,6 @@ will want to ensure that threading is disabled for `git send-email`.
|
||||
containing the shortlog and the overall diffstat. You can
|
||||
fill in a description in the file before sending it out.
|
||||
|
||||
--notes[=<ref>]::
|
||||
Append the notes (see linkgit:git-notes[1]) for the commit
|
||||
after the three-dash line.
|
||||
+
|
||||
The expected use case of this is to write supporting explanation for
|
||||
the commit that does not belong to the commit log message proper,
|
||||
and include it with the patch submission. While one can simply write
|
||||
these explanations after `format-patch` has run but before sending,
|
||||
keeping them as git notes allows them to be maintained between versions
|
||||
of the patch series (but see the discussion of the `notes.rewrite`
|
||||
configuration options in linkgit:git-notes[1] to use this workflow).
|
||||
|
||||
--[no]-signature=<signature>::
|
||||
Add a signature to each message produced. Per RFC 3676 the signature
|
||||
is separated from the body by a line with '-- ' on it. If the
|
||||
|
@ -39,10 +39,6 @@ message stored in the commit object, the notes are indented like the
|
||||
message, after an unindented line saying "Notes (<refname>):" (or
|
||||
"Notes:" for `refs/notes/commits`).
|
||||
|
||||
Notes can also be added to patches prepared with `git format-patch` by
|
||||
using the `--notes` option. Such notes are added as a patch commentary
|
||||
after a three dash separator line.
|
||||
|
||||
To change which notes are shown by 'git log', see the
|
||||
"notes.displayRef" configuration in linkgit:git-log[1].
|
||||
|
||||
|
@ -88,17 +88,53 @@ Each remote helper is expected to support only a subset of commands.
|
||||
The operations a helper supports are declared to git in the response
|
||||
to the `capabilities` command (see COMMANDS, below).
|
||||
|
||||
In the following, we list all defined capabilities and for
|
||||
each we list which commands a helper with that capability
|
||||
must provide.
|
||||
'option'::
|
||||
For specifying settings like `verbosity` (how much output to
|
||||
write to stderr) and `depth` (how much history is wanted in the
|
||||
case of a shallow clone) that affect how other commands are
|
||||
carried out.
|
||||
|
||||
'connect'::
|
||||
For fetching and pushing using git's native packfile protocol
|
||||
that requires a bidirectional, full-duplex connection.
|
||||
|
||||
'push'::
|
||||
For listing remote refs and pushing specified objects from the
|
||||
local object store to remote refs.
|
||||
|
||||
'fetch'::
|
||||
For listing remote refs and fetching the associated history to
|
||||
the local object store.
|
||||
|
||||
'import'::
|
||||
For listing remote refs and fetching the associated history as
|
||||
a fast-import stream.
|
||||
|
||||
'refspec' <refspec>::
|
||||
This modifies the 'import' capability, allowing the produced
|
||||
fast-import stream to modify refs in a private namespace
|
||||
instead of writing to refs/heads or refs/remotes directly.
|
||||
It is recommended that all importers providing the 'import'
|
||||
capability use this.
|
||||
+
|
||||
A helper advertising the capability
|
||||
`refspec refs/heads/*:refs/svn/origin/branches/*`
|
||||
is saying that, when it is asked to `import refs/heads/topic`, the
|
||||
stream it outputs will update the `refs/svn/origin/branches/topic`
|
||||
ref.
|
||||
+
|
||||
This capability can be advertised multiple times. The first
|
||||
applicable refspec takes precedence. The left-hand of refspecs
|
||||
advertised with this capability must cover all refs reported by
|
||||
the list command. If no 'refspec' capability is advertised,
|
||||
there is an implied `refspec *:*`.
|
||||
|
||||
Capabilities for Pushing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
'connect'::
|
||||
Can attempt to connect to 'git receive-pack' (for pushing),
|
||||
'git upload-pack', etc for communication using
|
||||
git's native packfile protocol. This
|
||||
requires a bidirectional, full-duplex connection.
|
||||
'git upload-pack', etc for communication using the
|
||||
packfile protocol.
|
||||
+
|
||||
Supported commands: 'connect'.
|
||||
|
||||
@ -108,26 +144,16 @@ Supported commands: 'connect'.
|
||||
+
|
||||
Supported commands: 'list for-push', 'push'.
|
||||
|
||||
'export'::
|
||||
Can discover remote refs and push specified objects from a
|
||||
fast-import stream to remote refs.
|
||||
+
|
||||
Supported commands: 'list for-push', 'export'.
|
||||
|
||||
If a helper advertises 'connect', git will use it if possible and
|
||||
fall back to another capability if the helper requests so when
|
||||
connecting (see the 'connect' command under COMMANDS).
|
||||
When choosing between 'push' and 'export', git prefers 'push'.
|
||||
Other frontends may have some other order of preference.
|
||||
|
||||
If a helper advertises both 'connect' and 'push', git will use
|
||||
'connect' if possible and fall back to 'push' if the helper requests
|
||||
so when connecting (see the 'connect' command under COMMANDS).
|
||||
|
||||
Capabilities for Fetching
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
'connect'::
|
||||
Can try to connect to 'git upload-pack' (for fetching),
|
||||
'git receive-pack', etc for communication using the
|
||||
git's native packfile protocol. This
|
||||
requires a bidirectional, full-duplex connection.
|
||||
packfile protocol.
|
||||
+
|
||||
Supported commands: 'connect'.
|
||||
|
||||
@ -149,27 +175,14 @@ connecting (see the 'connect' command under COMMANDS).
|
||||
When choosing between 'fetch' and 'import', git prefers 'fetch'.
|
||||
Other frontends may have some other order of preference.
|
||||
|
||||
Miscellaneous capabilities
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
'option'::
|
||||
For specifying settings like `verbosity` (how much output to
|
||||
write to stderr) and `depth` (how much history is wanted in the
|
||||
case of a shallow clone) that affect how other commands are
|
||||
carried out.
|
||||
|
||||
'refspec' <refspec>::
|
||||
This modifies the 'import' capability, allowing the produced
|
||||
fast-import stream to modify refs in a private namespace
|
||||
instead of writing to refs/heads or refs/remotes directly.
|
||||
It is recommended that all importers providing the 'import'
|
||||
capability use this.
|
||||
This modifies the 'import' capability.
|
||||
+
|
||||
A helper advertising the capability
|
||||
A helper advertising
|
||||
`refspec refs/heads/*:refs/svn/origin/branches/*`
|
||||
is saying that, when it is asked to `import refs/heads/topic`, the
|
||||
stream it outputs will update the `refs/svn/origin/branches/topic`
|
||||
ref.
|
||||
in its capabilities is saying that, when it handles
|
||||
`import refs/heads/topic`, the stream it outputs will update the
|
||||
`refs/svn/origin/branches/topic` ref.
|
||||
+
|
||||
This capability can be advertised multiple times. The first
|
||||
applicable refspec takes precedence. The left-hand of refspecs
|
||||
@ -177,34 +190,6 @@ advertised with this capability must cover all refs reported by
|
||||
the list command. If no 'refspec' capability is advertised,
|
||||
there is an implied `refspec *:*`.
|
||||
|
||||
'bidi-import'::
|
||||
This modifies the 'import' capability.
|
||||
The fast-import commands 'cat-blob' and 'ls' can be used by remote-helpers
|
||||
to retrieve information about blobs and trees that already exist in
|
||||
fast-import's memory. This requires a channel from fast-import to the
|
||||
remote-helper.
|
||||
If it is advertised in addition to "import", git establishes a pipe from
|
||||
fast-import to the remote-helper's stdin.
|
||||
It follows that git and fast-import are both connected to the
|
||||
remote-helper's stdin. Because git can send multiple commands to
|
||||
the remote-helper it is required that helpers that use 'bidi-import'
|
||||
buffer all 'import' commands of a batch before sending data to fast-import.
|
||||
This is to prevent mixing commands and fast-import responses on the
|
||||
helper's stdin.
|
||||
|
||||
'export-marks' <file>::
|
||||
This modifies the 'export' capability, instructing git to dump the
|
||||
internal marks table to <file> when complete. For details,
|
||||
read up on '--export-marks=<file>' in linkgit:git-fast-export[1].
|
||||
|
||||
'import-marks' <file>::
|
||||
This modifies the 'export' capability, instructing git to load the
|
||||
marks specified in <file> before processing any input. For details,
|
||||
read up on '--import-marks=<file>' in linkgit:git-fast-export[1].
|
||||
|
||||
|
||||
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
|
||||
@ -213,11 +198,9 @@ Commands are given by the caller on the helper's standard input, one per line.
|
||||
'capabilities'::
|
||||
Lists the capabilities of the helper, one per line, ending
|
||||
with a blank line. Each capability may be preceded with '*',
|
||||
which marks them mandatory for git versions using the remote
|
||||
helper to understand. Any unknown mandatory capability is a
|
||||
fatal error.
|
||||
+
|
||||
Support for this command is mandatory.
|
||||
which marks them mandatory for git version using the remote
|
||||
helper to understand (unknown mandatory capability is fatal
|
||||
error).
|
||||
|
||||
'list'::
|
||||
Lists the refs, one per line, in the format "<value> <name>
|
||||
@ -227,20 +210,9 @@ Support for this command is mandatory.
|
||||
the name; unrecognized attributes are ignored. The list ends
|
||||
with a blank line.
|
||||
+
|
||||
See REF LIST ATTRIBUTES for a list of currently defined attributes.
|
||||
+
|
||||
Supported if the helper has the "fetch" or "import" capability.
|
||||
|
||||
'list for-push'::
|
||||
Similar to 'list', except that it is used if and only if
|
||||
the caller wants to the resulting ref list to prepare
|
||||
push commands.
|
||||
A helper supporting both push and fetch can use this
|
||||
to distinguish for which operation the output of 'list'
|
||||
is going to be used, possibly reducing the amount
|
||||
of work that needs to be performed.
|
||||
+
|
||||
Supported if the helper has the "push" or "export" capability.
|
||||
If 'push' is supported this may be called as 'list for-push'
|
||||
to obtain the current refs prior to sending one or more 'push'
|
||||
commands to the helper.
|
||||
|
||||
'option' <name> <value>::
|
||||
Sets the transport helper option <name> to <value>. Outputs a
|
||||
@ -250,8 +222,6 @@ Supported if the helper has the "push" or "export" capability.
|
||||
for it). Options should be set before other commands,
|
||||
and may influence the behavior of those commands.
|
||||
+
|
||||
See OPTIONS for a list of currently defined options.
|
||||
+
|
||||
Supported if the helper has the "option" capability.
|
||||
|
||||
'fetch' <sha1> <name>::
|
||||
@ -260,7 +230,7 @@ Supported if the helper has the "option" capability.
|
||||
per line, terminated with a blank line.
|
||||
Outputs a single blank line when all fetch commands in the
|
||||
same batch are complete. Only objects which were reported
|
||||
in the output of 'list' with a sha1 may be fetched this way.
|
||||
in the ref list with a sha1 may be fetched this way.
|
||||
+
|
||||
Optionally may output a 'lock <file>' line indicating a file under
|
||||
GIT_DIR/objects/pack which is keeping a pack until refs can be
|
||||
@ -316,29 +286,8 @@ terminated with a blank line. For each batch of 'import', the remote
|
||||
helper should produce a fast-import stream terminated by a 'done'
|
||||
command.
|
||||
+
|
||||
Note that if the 'bidi-import' capability is used the complete batch
|
||||
sequence has to be buffered before starting to send data to fast-import
|
||||
to prevent mixing of commands and fast-import responses on the helper's
|
||||
stdin.
|
||||
+
|
||||
Supported if the helper has the "import" capability.
|
||||
|
||||
'export'::
|
||||
Instructs the remote helper that any subsequent input is
|
||||
part of a fast-import stream (generated by 'git fast-export')
|
||||
containing objects which should be pushed to the remote.
|
||||
+
|
||||
Especially useful for interoperability with a foreign versioning
|
||||
system.
|
||||
+
|
||||
The 'export-marks' and 'import-marks' capabilities, if specified,
|
||||
affect this command in so far as they are passed on to 'git
|
||||
fast-export', which then will load/store a table of marks for
|
||||
local objects. This can be used to implement for incremental
|
||||
operations.
|
||||
+
|
||||
Supported if the helper has the "export" capability.
|
||||
|
||||
'connect' <service>::
|
||||
Connects to given service. Standard input and standard output
|
||||
of helper are connected to specified service (git prefix is
|
||||
@ -364,9 +313,10 @@ capabilities reported by the helper.
|
||||
REF LIST ATTRIBUTES
|
||||
-------------------
|
||||
|
||||
The 'list' command produces a list of refs in which each ref
|
||||
may be followed by a list of attributes. The following ref list
|
||||
attributes are defined.
|
||||
'for-push'::
|
||||
The caller wants to use the ref list to prepare push
|
||||
commands. A helper might chose to acquire the ref list by
|
||||
opening a different type of connection to the destination.
|
||||
|
||||
'unchanged'::
|
||||
This ref is unchanged since the last import or fetch, although
|
||||
@ -374,10 +324,6 @@ attributes are defined.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
The following options are defined and (under suitable circumstances)
|
||||
set by git if the remote helper has the 'option' capability.
|
||||
|
||||
'option verbosity' <n>::
|
||||
Changes the verbosity of messages displayed by the helper.
|
||||
A value of 0 for <n> means that processes operate
|
||||
|
@ -134,21 +134,6 @@ use the following command:
|
||||
git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
|
||||
----------------
|
||||
|
||||
Submodules
|
||||
~~~~~~~~~~
|
||||
Only submodules using a gitfile (which means they were cloned
|
||||
with a git version 1.7.8 or newer) will be removed from the work
|
||||
tree, as their repository lives inside the .git directory of the
|
||||
superproject. If a submodule (or one of those nested inside it)
|
||||
still uses a .git directory, `git rm` will fail - no matter if forced
|
||||
or not - to protect the submodule's history.
|
||||
|
||||
A submodule is considered up-to-date when the HEAD is the same as
|
||||
recorded in the index, no tracked files are modified and no untracked
|
||||
files that aren't ignored are present in the submodules work tree.
|
||||
Ignored files are deemed expendable and won't stop a submodule's work
|
||||
tree from being removed.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
`git rm Documentation/\*.txt`::
|
||||
|
@ -126,10 +126,6 @@ The --to option must be repeated for each user you want on the to list.
|
||||
+
|
||||
Note that no attempts whatsoever are made to validate the encoding.
|
||||
|
||||
--compose-encoding=<encoding>::
|
||||
Specify encoding of compose message. Default is the value of the
|
||||
'sendemail.composeencoding'; if that is unspecified, UTF-8 is assumed.
|
||||
|
||||
|
||||
Sending
|
||||
~~~~~~~
|
||||
|
@ -38,9 +38,6 @@ OPTIONS
|
||||
across git versions and regardless of user configuration. See
|
||||
below for details.
|
||||
|
||||
--long::
|
||||
Give the output in the long-format. This is the default.
|
||||
|
||||
-u[<mode>]::
|
||||
--untracked-files[=<mode>]::
|
||||
Show untracked files.
|
||||
|
@ -9,7 +9,7 @@ git-submodule - Initialize, update or inspect submodules
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git submodule' [--quiet] add [-b <branch>] [-f|--force] [--name <name>]
|
||||
'git submodule' [--quiet] add [-b <branch>] [-f|--force]
|
||||
[--reference <repository>] [--] <repository> [<path>]
|
||||
'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
|
||||
'git submodule' [--quiet] init [--] [<path>...]
|
||||
@ -265,11 +265,6 @@ OPTIONS
|
||||
Initialize all submodules for which "git submodule init" has not been
|
||||
called so far before updating.
|
||||
|
||||
--name::
|
||||
This option is only valid for the add command. It sets the submodule's
|
||||
name to the given string instead of defaulting to its path. The name
|
||||
must be valid as a directory name and may not end with a '/'.
|
||||
|
||||
--reference <repository>::
|
||||
This option is only valid for add and update commands. These
|
||||
commands sometimes need to clone a remote repository. In this case,
|
||||
|
@ -3,14 +3,13 @@ git-symbolic-ref(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-symbolic-ref - Read, modify and delete symbolic refs
|
||||
git-symbolic-ref - Read and modify symbolic refs
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git symbolic-ref' [-m <reason>] <name> <ref>
|
||||
'git symbolic-ref' [-q] [--short] <name>
|
||||
'git symbolic-ref' --delete [-q] <name>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -22,9 +21,6 @@ argument to see which branch your working tree is on.
|
||||
Given two arguments, creates or updates a symbolic ref <name> to
|
||||
point at the given branch <ref>.
|
||||
|
||||
Given `--delete` and an additional argument, deletes the given
|
||||
symbolic ref.
|
||||
|
||||
A symbolic ref is a regular file that stores a string that
|
||||
begins with `ref: refs/`. For example, your `.git/HEAD` is
|
||||
a regular file whose contents is `ref: refs/heads/master`.
|
||||
@ -32,10 +28,6 @@ a regular file whose contents is `ref: refs/heads/master`.
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
-d::
|
||||
--delete::
|
||||
Delete the symbolic ref <name>.
|
||||
|
||||
-q::
|
||||
--quiet::
|
||||
Do not issue an error message if the <name> is not a
|
||||
|
@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.8.0.2/git.html[documentation for release 1.8.0.2]
|
||||
* link:v1.8.0.3/git.html[documentation for release 1.8.0.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes/1.8.0.3.txt[1.8.0.3],
|
||||
link:RelNotes/1.8.0.2.txt[1.8.0.2],
|
||||
link:RelNotes/1.8.0.1.txt[1.8.0.1],
|
||||
link:RelNotes/1.8.0.txt[1.8.0].
|
||||
|
@ -56,7 +56,6 @@ When more than one pattern matches the path, a later line
|
||||
overrides an earlier line. This overriding is done per
|
||||
attribute. The rules how the pattern matches paths are the
|
||||
same as in `.gitignore` files; see linkgit:gitignore[5].
|
||||
Unlike `.gitignore`, negative patterns are forbidden.
|
||||
|
||||
When deciding what attributes are assigned to a path, git
|
||||
consults `$GIT_DIR/info/attributes` file (which has the highest
|
||||
|
@ -18,9 +18,7 @@ working tree, is a text file with a syntax matching the requirements
|
||||
of linkgit:git-config[1].
|
||||
|
||||
The file contains one subsection per submodule, and the subsection value
|
||||
is the name of the submodule. The name is set to the path where the
|
||||
submodule has been added unless it was customized with the '--name'
|
||||
option of 'git submodule add'. Each submodule section also contains the
|
||||
is the name of the submodule. Each submodule section also contains the
|
||||
following required keys:
|
||||
|
||||
submodule.<name>.path::
|
||||
|
@ -1,104 +0,0 @@
|
||||
From: Eric S. Raymond <esr@thyrsus.com>
|
||||
Abstract: This is how-to documentation for people who want to add extension
|
||||
commands to git. It should be read alongside api-builtin.txt.
|
||||
Content-type: text/asciidoc
|
||||
|
||||
How to integrate new subcommands
|
||||
================================
|
||||
|
||||
This is how-to documentation for people who want to add extension
|
||||
commands to git. It should be read alongside api-builtin.txt.
|
||||
|
||||
Runtime environment
|
||||
-------------------
|
||||
|
||||
git subcommands are standalone executables that live in the git exec
|
||||
path, normally /usr/lib/git-core. The git executable itself is a
|
||||
thin wrapper that knows where the subcommands live, and runs them by
|
||||
passing command-line arguments to them.
|
||||
|
||||
(If "git foo" is not found in the git exec path, the wrapper
|
||||
will look in the rest of your $PATH for it. Thus, it's possible
|
||||
to write local git extensions that don't live in system space.)
|
||||
|
||||
Implementation languages
|
||||
------------------------
|
||||
|
||||
Most subcommands are written in C or shell. A few are written in
|
||||
Perl.
|
||||
|
||||
While we strongly encourage coding in portable C for portability,
|
||||
these specific scripting languages are also acceptable. We won't
|
||||
accept more without a very strong technical case, as we don't want
|
||||
to broaden the git suite's required dependencies. Import utilities,
|
||||
surgical tools, remote helpers and other code at the edges of the
|
||||
git suite are more lenient and we allow Python (and even Tcl/tk),
|
||||
but they should not be used for core functions.
|
||||
|
||||
This may change in the future. Especially Python is not allowed in
|
||||
core because we need better Python integration in the git Windows
|
||||
installer before we can be confident people in that environment
|
||||
won't experience an unacceptably large loss of capability.
|
||||
|
||||
C commands are normally written as single modules, named after the
|
||||
command, that link a collection of functions called libgit. Thus,
|
||||
your command 'git-foo' would normally be implemented as a single
|
||||
"git-foo.c" (or "builtin/foo.c" if it is to be linked to the main
|
||||
binary); this organization makes it easy for people reading the code
|
||||
to find things.
|
||||
|
||||
See the CodingGuidelines document for other guidance on what we consider
|
||||
good practice in C and shell, and api-builtin.txt for the support
|
||||
functions available to built-in commands written in C.
|
||||
|
||||
What every extension command needs
|
||||
----------------------------------
|
||||
|
||||
You must have a man page, written in asciidoc (this is what git help
|
||||
followed by your subcommand name will display). Be aware that there is
|
||||
a local asciidoc configuration and macros which you should use. It's
|
||||
often helpful to start by cloning an existing page and replacing the
|
||||
text content.
|
||||
|
||||
You must have a test, written to report in TAP (Test Anything Protocol).
|
||||
Tests are executables (usually shell scripts) that live in the 't'
|
||||
subdirectory of the tree. Each test name begins with 't' and a sequence
|
||||
number that controls where in the test sequence it will be executed;
|
||||
conventionally the rest of the name stem is that of the command
|
||||
being tested.
|
||||
|
||||
Read the file t/README to learn more about the conventions to be used
|
||||
in writing tests, and the test support library.
|
||||
|
||||
Integrating a command
|
||||
---------------------
|
||||
|
||||
Here are the things you need to do when you want to merge a new
|
||||
subcommand into the git tree.
|
||||
|
||||
1. Don't forget to sign off your patch!
|
||||
|
||||
2. Append your command name to one of the variables BUILTIN_OBJS,
|
||||
EXTRA_PROGRAMS, SCRIPT_SH, SCRIPT_PERL or SCRIPT_PYTHON.
|
||||
|
||||
3. Drop its test in the t directory.
|
||||
|
||||
4. If your command is implemented in an interpreted language with a
|
||||
p-code intermediate form, make sure .gitignore in the main directory
|
||||
includes a pattern entry that ignores such files. Python .pyc and
|
||||
.pyo files will already be covered.
|
||||
|
||||
5. If your command has any dependency on a particular version of
|
||||
your language, document it in the INSTALL file.
|
||||
|
||||
6. There is a file command-list.txt in the distribution main directory
|
||||
that categorizes commands by type, so they can be listed in appropriate
|
||||
subsections in the documentation's summary command list. Add an entry
|
||||
for yours. To understand the categories, look at git-cmmands.txt
|
||||
in the main directory.
|
||||
|
||||
7. Give the maintainer one paragraph to include in the RelNotes file
|
||||
to describe the new feature; a good place to do so is in the cover
|
||||
letter [PATCH 0/n].
|
||||
|
||||
That's all there is to it.
|
@ -79,11 +79,6 @@ if it is part of the log message.
|
||||
|
||||
Match the regexp limiting patterns without regard to letters case.
|
||||
|
||||
--basic-regexp::
|
||||
|
||||
Consider the limiting patterns to be basic regular expressions;
|
||||
this is the default.
|
||||
|
||||
-E::
|
||||
--extended-regexp::
|
||||
|
||||
@ -96,11 +91,6 @@ if it is part of the log message.
|
||||
Consider the limiting patterns to be fixed strings (don't interpret
|
||||
pattern as a regular expression).
|
||||
|
||||
--perl-regexp::
|
||||
|
||||
Consider the limiting patterns to be Perl-compatible regexp.
|
||||
Requires libpcre to be compiled in.
|
||||
|
||||
--remove-empty::
|
||||
|
||||
Stop when a given path disappears from the tree.
|
||||
|
@ -53,11 +53,3 @@ Functions
|
||||
`argv_array_clear`::
|
||||
Free all memory associated with the array and return it to the
|
||||
initial, empty state.
|
||||
|
||||
`argv_array_detach`::
|
||||
Detach the argv array from the `struct argv_array`, transfering
|
||||
ownership of the allocated array and strings.
|
||||
|
||||
`argv_array_free_detached`::
|
||||
Free the memory allocated by a `struct argv_array` that was later
|
||||
detached and is now no longer needed.
|
||||
|
@ -279,22 +279,6 @@ same behaviour as well.
|
||||
Strip whitespace from a buffer. The second parameter controls if
|
||||
comments are considered contents to be removed or not.
|
||||
|
||||
`strbuf_split_buf`::
|
||||
`strbuf_split_str`::
|
||||
`strbuf_split_max`::
|
||||
`strbuf_split`::
|
||||
|
||||
Split a string or strbuf into a list of strbufs at a specified
|
||||
terminator character. The returned substrings include the
|
||||
terminator characters. Some of these functions take a `max`
|
||||
parameter, which, if positive, limits the output to that
|
||||
number of substrings.
|
||||
|
||||
`strbuf_list_free`::
|
||||
|
||||
Free a list of strbufs (for example, the return values of the
|
||||
`strbuf_split()` functions).
|
||||
|
||||
`launch_editor`::
|
||||
|
||||
Launch the user preferred editor to edit a file and fill the buffer
|
||||
|
@ -38,8 +38,7 @@ member (you need this if you add things later) and you should set the
|
||||
`unsorted_string_list_delete_item`.
|
||||
|
||||
. Can remove items not matching a criterion from a sorted or unsorted
|
||||
list using `filter_string_list`, or remove empty strings using
|
||||
`string_list_remove_empty_items`.
|
||||
list using `filter_string_list`.
|
||||
|
||||
. Finally it should free the list using `string_list_clear`.
|
||||
|
||||
@ -76,12 +75,6 @@ Functions
|
||||
to be deleted. Preserve the order of the items that are
|
||||
retained.
|
||||
|
||||
`string_list_remove_empty_items`::
|
||||
|
||||
Remove any empty strings from the list. If free_util is true,
|
||||
call free() on the util members of any items that have to be
|
||||
deleted. Preserve the order of the items that are retained.
|
||||
|
||||
`string_list_longest_prefix`::
|
||||
|
||||
Return the longest string within a string_list that is a
|
||||
|
@ -1787,13 +1787,6 @@ $ git format-patch origin
|
||||
will produce a numbered series of files in the current directory, one
|
||||
for each patch in the current branch but not in origin/HEAD.
|
||||
|
||||
`git format-patch` can include an initial "cover letter". You can insert
|
||||
commentary on individual patches after the three dash line which
|
||||
`format-patch` places after the commit message but before the patch
|
||||
itself. If you use `git notes` to track your cover letter material,
|
||||
`git format-patch --notes` will include the commit's notes in a similar
|
||||
manner.
|
||||
|
||||
You can then import these into your mail client and send them by
|
||||
hand. However, if you have a lot to send at once, you may prefer to
|
||||
use the linkgit:git-send-email[1] script to automate the process.
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
GVF=GIT-VERSION-FILE
|
||||
DEF_VER=v1.8.1-rc3
|
||||
DEF_VER=v1.8.0.3
|
||||
|
||||
LF='
|
||||
'
|
||||
|
66
Makefile
66
Makefile
@ -374,7 +374,7 @@ htmldir = share/doc/git-doc
|
||||
ETC_GITCONFIG = $(sysconfdir)/gitconfig
|
||||
ETC_GITATTRIBUTES = $(sysconfdir)/gitattributes
|
||||
lib = lib
|
||||
# DESTDIR =
|
||||
# DESTDIR=
|
||||
pathsep = :
|
||||
|
||||
export prefix bindir sharedir sysconfdir gitwebdir localedir
|
||||
@ -495,7 +495,6 @@ PROGRAM_OBJS += sh-i18n--envsubst.o
|
||||
PROGRAM_OBJS += shell.o
|
||||
PROGRAM_OBJS += show-index.o
|
||||
PROGRAM_OBJS += upload-pack.o
|
||||
PROGRAM_OBJS += remote-testsvn.o
|
||||
|
||||
# Binary suffix, set to .exe for Windows builds
|
||||
X =
|
||||
@ -575,9 +574,9 @@ endif
|
||||
export PERL_PATH
|
||||
export PYTHON_PATH
|
||||
|
||||
LIB_FILE = libgit.a
|
||||
XDIFF_LIB = xdiff/lib.a
|
||||
VCSSVN_LIB = vcs-svn/lib.a
|
||||
LIB_FILE=libgit.a
|
||||
XDIFF_LIB=xdiff/lib.a
|
||||
VCSSVN_LIB=vcs-svn/lib.a
|
||||
|
||||
LIB_H += xdiff/xinclude.h
|
||||
LIB_H += xdiff/xmacros.h
|
||||
@ -746,7 +745,6 @@ LIB_OBJS += editor.o
|
||||
LIB_OBJS += entry.o
|
||||
LIB_OBJS += environment.o
|
||||
LIB_OBJS += exec_cmd.o
|
||||
LIB_OBJS += fetch-pack.o
|
||||
LIB_OBJS += fsck.o
|
||||
LIB_OBJS += gettext.o
|
||||
LIB_OBJS += gpg-interface.o
|
||||
@ -764,7 +762,6 @@ LIB_OBJS += lockfile.o
|
||||
LIB_OBJS += log-tree.o
|
||||
LIB_OBJS += mailmap.o
|
||||
LIB_OBJS += match-trees.o
|
||||
LIB_OBJS += merge.o
|
||||
LIB_OBJS += merge-file.o
|
||||
LIB_OBJS += merge-recursive.o
|
||||
LIB_OBJS += mergesort.o
|
||||
@ -799,7 +796,6 @@ LIB_OBJS += rerere.o
|
||||
LIB_OBJS += resolve-undo.o
|
||||
LIB_OBJS += revision.o
|
||||
LIB_OBJS += run-command.o
|
||||
LIB_OBJS += send-pack.o
|
||||
LIB_OBJS += sequencer.o
|
||||
LIB_OBJS += server-info.o
|
||||
LIB_OBJS += setup.o
|
||||
@ -1086,7 +1082,6 @@ ifeq ($(uname_O),Cygwin)
|
||||
NO_SYMLINK_HEAD = YesPlease
|
||||
NO_IPV6 = YesPlease
|
||||
OLD_ICONV = UnfortunatelyYes
|
||||
CYGWIN_V15_WIN32API = YesPlease
|
||||
endif
|
||||
NO_THREAD_SAFE_PREAD = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
@ -1139,7 +1134,7 @@ ifeq ($(uname_S),NetBSD)
|
||||
endif
|
||||
ifeq ($(uname_S),AIX)
|
||||
DEFAULT_PAGER = more
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_MKSTEMPS = YesPlease
|
||||
@ -1147,7 +1142,7 @@ ifeq ($(uname_S),AIX)
|
||||
NO_NSEC = YesPlease
|
||||
FREAD_READS_DIRECTORIES = UnfortunatelyYes
|
||||
INTERNAL_QSORT = UnfortunatelyYes
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
NEEDS_LIBICONV=YesPlease
|
||||
BASIC_CFLAGS += -D_LARGE_FILES
|
||||
ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
|
||||
NO_PTHREADS = YesPlease
|
||||
@ -1155,13 +1150,13 @@ ifeq ($(uname_S),AIX)
|
||||
PTHREAD_LIBS = -lpthread
|
||||
endif
|
||||
ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
|
||||
INLINE = ''
|
||||
INLINE=''
|
||||
endif
|
||||
GIT_TEST_CMP = cmp
|
||||
endif
|
||||
ifeq ($(uname_S),GNU)
|
||||
# GNU/Hurd
|
||||
NO_STRLCPY = YesPlease
|
||||
NO_STRLCPY=YesPlease
|
||||
NO_MKSTEMPS = YesPlease
|
||||
HAVE_PATHS_H = YesPlease
|
||||
LIBC_CONTAINS_LIBINTL = YesPlease
|
||||
@ -1187,9 +1182,9 @@ ifeq ($(uname_S),IRIX)
|
||||
NEEDS_LIBGEN = YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),IRIX64)
|
||||
NO_SETENV = YesPlease
|
||||
NO_SETENV=YesPlease
|
||||
NO_UNSETENV = YesPlease
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_MKSTEMPS = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
@ -1203,14 +1198,14 @@ ifeq ($(uname_S),IRIX64)
|
||||
NO_REGEX = YesPlease
|
||||
NO_FNMATCH_CASEFOLD = YesPlease
|
||||
SNPRINTF_RETURNS_BOGUS = YesPlease
|
||||
SHELL_PATH = /usr/gnu/bin/bash
|
||||
SHELL_PATH=/usr/gnu/bin/bash
|
||||
NEEDS_LIBGEN = YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),HP-UX)
|
||||
INLINE = __inline
|
||||
NO_IPV6 = YesPlease
|
||||
NO_SETENV = YesPlease
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_IPV6=YesPlease
|
||||
NO_SETENV=YesPlease
|
||||
NO_STRCASESTR=YesPlease
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_MKSTEMPS = YesPlease
|
||||
NO_STRLCPY = YesPlease
|
||||
@ -1386,10 +1381,6 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
|
||||
MKDIR_WO_TRAILING_SLASH = YesPlease
|
||||
# RFE 10-120912-4693 submitted to HP NonStop development.
|
||||
NO_SETITIMER = UnfortunatelyYes
|
||||
SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
|
||||
SHELL_PATH = /usr/local/bin/bash
|
||||
# as of H06.25/J06.14, we might better use this
|
||||
#SHELL_PATH = /usr/coreutils/bin/bash
|
||||
endif
|
||||
ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
pathsep = ;
|
||||
@ -1437,7 +1428,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
X = .exe
|
||||
SPARSE_FLAGS = -Wno-one-bit-signed-bitfield
|
||||
ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
|
||||
htmldir = doc/git/html/
|
||||
htmldir=doc/git/html/
|
||||
prefix =
|
||||
INSTALL = /bin/install
|
||||
EXTLIBS += /mingw/lib/libz.a
|
||||
@ -1559,7 +1550,7 @@ else
|
||||
CURL_LIBCURL = -lcurl
|
||||
endif
|
||||
ifdef NEEDS_SSL_WITH_CURL
|
||||
CURL_LIBCURL += -lssl
|
||||
CURL_LIBCURL += -lssl
|
||||
ifdef NEEDS_CRYPTO_WITH_SSL
|
||||
CURL_LIBCURL += -lcrypto
|
||||
endif
|
||||
@ -1768,7 +1759,7 @@ ifdef OBJECT_CREATION_USES_RENAMES
|
||||
endif
|
||||
ifdef NO_STRUCT_ITIMERVAL
|
||||
COMPAT_CFLAGS += -DNO_STRUCT_ITIMERVAL
|
||||
NO_SETITIMER = YesPlease
|
||||
NO_SETITIMER=YesPlease
|
||||
endif
|
||||
ifdef NO_SETITIMER
|
||||
COMPAT_CFLAGS += -DNO_SETITIMER
|
||||
@ -1898,9 +1889,6 @@ ifdef NO_REGEX
|
||||
COMPAT_CFLAGS += -Icompat/regex
|
||||
COMPAT_OBJS += compat/regex/regex.o
|
||||
endif
|
||||
ifdef CYGWIN_V15_WIN32API
|
||||
COMPAT_CFLAGS += -DCYGWIN_V15_WIN32API
|
||||
endif
|
||||
|
||||
ifdef USE_NED_ALLOCATOR
|
||||
COMPAT_CFLAGS += -Icompat/nedmalloc
|
||||
@ -1920,15 +1908,15 @@ ifneq (,$(XDL_FAST_HASH))
|
||||
endif
|
||||
|
||||
ifeq ($(TCLTK_PATH),)
|
||||
NO_TCLTK = NoThanks
|
||||
NO_TCLTK=NoThanks
|
||||
endif
|
||||
|
||||
ifeq ($(PERL_PATH),)
|
||||
NO_PERL = NoThanks
|
||||
NO_PERL=NoThanks
|
||||
endif
|
||||
|
||||
ifeq ($(PYTHON_PATH),)
|
||||
NO_PYTHON = NoThanks
|
||||
NO_PYTHON=NoThanks
|
||||
endif
|
||||
|
||||
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
|
||||
@ -1975,13 +1963,13 @@ PROFILE_DIR := $(CURDIR)
|
||||
ifeq ("$(PROFILE)","GEN")
|
||||
CFLAGS += -fprofile-generate=$(PROFILE_DIR) -DNO_NORETURN=1
|
||||
EXTLIBS += -lgcov
|
||||
export CCACHE_DISABLE = t
|
||||
V = 1
|
||||
export CCACHE_DISABLE=t
|
||||
V=1
|
||||
else
|
||||
ifneq ("$(PROFILE)","")
|
||||
CFLAGS += -fprofile-use=$(PROFILE_DIR) -fprofile-correction -DNO_NORETURN=1
|
||||
export CCACHE_DISABLE = t
|
||||
V = 1
|
||||
export CCACHE_DISABLE=t
|
||||
V=1
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -2461,10 +2449,6 @@ git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
|
||||
|
||||
git-remote-testsvn$X: remote-testsvn.o GIT-LDFLAGS $(GITLIBS) $(VCSSVN_LIB)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) \
|
||||
$(VCSSVN_LIB)
|
||||
|
||||
$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
|
||||
$(QUIET_LNCP)$(RM) $@ && \
|
||||
ln $< $@ 2>/dev/null || \
|
||||
@ -2830,7 +2814,7 @@ git.spec: git.spec.in GIT-VERSION-FILE
|
||||
sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
|
||||
mv $@+ $@
|
||||
|
||||
GIT_TARNAME = git-$(GIT_VERSION)
|
||||
GIT_TARNAME=git-$(GIT_VERSION)
|
||||
dist: git.spec git-archive$(X) configure
|
||||
./git-archive --format=tar \
|
||||
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
|
||||
|
20
argv-array.c
20
argv-array.c
@ -68,23 +68,3 @@ void argv_array_clear(struct argv_array *array)
|
||||
}
|
||||
argv_array_init(array);
|
||||
}
|
||||
|
||||
const char **argv_array_detach(struct argv_array *array, int *argc)
|
||||
{
|
||||
const char **argv =
|
||||
array->argv == empty_argv || array->argc == 0 ? NULL : array->argv;
|
||||
if (argc)
|
||||
*argc = array->argc;
|
||||
argv_array_init(array);
|
||||
return argv;
|
||||
}
|
||||
|
||||
void argv_array_free_detached(const char **argv)
|
||||
{
|
||||
if (argv) {
|
||||
int i;
|
||||
for (i = 0; argv[i]; i++)
|
||||
free((char **)argv[i]);
|
||||
free(argv);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,5 @@ void argv_array_pushf(struct argv_array *, const char *fmt, ...);
|
||||
void argv_array_pushl(struct argv_array *, ...);
|
||||
void argv_array_pop(struct argv_array *);
|
||||
void argv_array_clear(struct argv_array *);
|
||||
const char **argv_array_detach(struct argv_array *array, int *argc);
|
||||
void argv_array_free_detached(const char **argv);
|
||||
|
||||
#endif /* ARGV_ARRAY_H */
|
||||
|
71
attr.c
71
attr.c
@ -115,13 +115,6 @@ struct attr_state {
|
||||
const char *setto;
|
||||
};
|
||||
|
||||
struct pattern {
|
||||
const char *pattern;
|
||||
int patternlen;
|
||||
int nowildcardlen;
|
||||
int flags; /* EXC_FLAG_* */
|
||||
};
|
||||
|
||||
/*
|
||||
* One rule, as from a .gitattributes file.
|
||||
*
|
||||
@ -138,7 +131,7 @@ struct pattern {
|
||||
*/
|
||||
struct match_attr {
|
||||
union {
|
||||
struct pattern pat;
|
||||
char *pattern;
|
||||
struct git_attr *attr;
|
||||
} u;
|
||||
char is_macro;
|
||||
@ -248,16 +241,9 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
|
||||
if (is_macro)
|
||||
res->u.attr = git_attr_internal(name, namelen);
|
||||
else {
|
||||
char *p = (char *)&(res->state[num_attr]);
|
||||
memcpy(p, name, namelen);
|
||||
res->u.pat.pattern = p;
|
||||
parse_exclude_pattern(&res->u.pat.pattern,
|
||||
&res->u.pat.patternlen,
|
||||
&res->u.pat.flags,
|
||||
&res->u.pat.nowildcardlen);
|
||||
if (res->u.pat.flags & EXC_FLAG_NEGATIVE)
|
||||
die(_("Negative patterns are forbidden in git attributes\n"
|
||||
"Use '\\!' for literal leading exclamation."));
|
||||
res->u.pattern = (char *)&(res->state[num_attr]);
|
||||
memcpy(res->u.pattern, name, namelen);
|
||||
res->u.pattern[namelen] = 0;
|
||||
}
|
||||
res->is_macro = is_macro;
|
||||
res->num_attr = num_attr;
|
||||
@ -291,7 +277,6 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
|
||||
static struct attr_stack {
|
||||
struct attr_stack *prev;
|
||||
char *origin;
|
||||
size_t originlen;
|
||||
unsigned num_matches;
|
||||
unsigned alloc;
|
||||
struct match_attr **attrs;
|
||||
@ -550,7 +535,6 @@ static void bootstrap_attr_stack(void)
|
||||
if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
|
||||
elem = read_attr(GITATTRIBUTES_FILE, 1);
|
||||
elem->origin = xstrdup("");
|
||||
elem->originlen = 0;
|
||||
elem->prev = attr_stack;
|
||||
attr_stack = elem;
|
||||
debug_push(elem);
|
||||
@ -644,7 +628,7 @@ static void prepare_attr_stack(const char *path)
|
||||
strbuf_addstr(&pathbuf, GITATTRIBUTES_FILE);
|
||||
elem = read_attr(pathbuf.buf, 0);
|
||||
strbuf_setlen(&pathbuf, cp - path);
|
||||
elem->origin = strbuf_detach(&pathbuf, &elem->originlen);
|
||||
elem->origin = strbuf_detach(&pathbuf, NULL);
|
||||
elem->prev = attr_stack;
|
||||
attr_stack = elem;
|
||||
debug_push(elem);
|
||||
@ -661,22 +645,28 @@ static void prepare_attr_stack(const char *path)
|
||||
}
|
||||
|
||||
static int path_matches(const char *pathname, int pathlen,
|
||||
const char *basename,
|
||||
const struct pattern *pat,
|
||||
const char *pattern,
|
||||
const char *base, int baselen)
|
||||
{
|
||||
const char *pattern = pat->pattern;
|
||||
int prefix = pat->nowildcardlen;
|
||||
|
||||
if (pat->flags & EXC_FLAG_NODIR) {
|
||||
return match_basename(basename,
|
||||
pathlen - (basename - pathname),
|
||||
pattern, prefix,
|
||||
pat->patternlen, pat->flags);
|
||||
if (!strchr(pattern, '/')) {
|
||||
/* match basename */
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = basename ? basename + 1 : pathname;
|
||||
return (fnmatch_icase(pattern, basename, 0) == 0);
|
||||
}
|
||||
return match_pathname(pathname, pathlen,
|
||||
base, baselen,
|
||||
pattern, prefix, pat->patternlen, pat->flags);
|
||||
/*
|
||||
* match with FNM_PATHNAME; the pattern has base implicitly
|
||||
* in front of it.
|
||||
*/
|
||||
if (*pattern == '/')
|
||||
pattern++;
|
||||
if (pathlen < baselen ||
|
||||
(baselen && pathname[baselen] != '/') ||
|
||||
strncmp(pathname, base, baselen))
|
||||
return 0;
|
||||
if (baselen != 0)
|
||||
baselen++;
|
||||
return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;
|
||||
}
|
||||
|
||||
static int macroexpand_one(int attr_nr, int rem);
|
||||
@ -703,8 +693,7 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
|
||||
return rem;
|
||||
}
|
||||
|
||||
static int fill(const char *path, int pathlen, const char *basename,
|
||||
struct attr_stack *stk, int rem)
|
||||
static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
|
||||
{
|
||||
int i;
|
||||
const char *base = stk->origin ? stk->origin : "";
|
||||
@ -713,8 +702,8 @@ static int fill(const char *path, int pathlen, const char *basename,
|
||||
struct match_attr *a = stk->attrs[i];
|
||||
if (a->is_macro)
|
||||
continue;
|
||||
if (path_matches(path, pathlen, basename,
|
||||
&a->u.pat, base, stk->originlen))
|
||||
if (path_matches(path, pathlen,
|
||||
a->u.pattern, base, strlen(base)))
|
||||
rem = fill_one("fill", a, rem);
|
||||
}
|
||||
return rem;
|
||||
@ -752,19 +741,15 @@ static void collect_all_attrs(const char *path)
|
||||
{
|
||||
struct attr_stack *stk;
|
||||
int i, pathlen, rem;
|
||||
const char *basename;
|
||||
|
||||
prepare_attr_stack(path);
|
||||
for (i = 0; i < attr_nr; i++)
|
||||
check_all_attr[i].value = ATTR__UNKNOWN;
|
||||
|
||||
basename = strrchr(path, '/');
|
||||
basename = basename ? basename + 1 : path;
|
||||
|
||||
pathlen = strlen(path);
|
||||
rem = attr_nr;
|
||||
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
|
||||
rem = fill(path, pathlen, basename, stk, rem);
|
||||
rem = fill(path, pathlen, stk, rem);
|
||||
}
|
||||
|
||||
int git_check_attr(const char *path, int num, struct git_attr_check *check)
|
||||
|
38
bisect.c
38
bisect.c
@ -956,41 +956,3 @@ int bisect_next_all(const char *prefix, int no_checkout)
|
||||
return bisect_checkout(bisect_rev_hex, no_checkout);
|
||||
}
|
||||
|
||||
static inline int log2i(int n)
|
||||
{
|
||||
int log2 = 0;
|
||||
|
||||
for (; n > 1; n >>= 1)
|
||||
log2++;
|
||||
|
||||
return log2;
|
||||
}
|
||||
|
||||
static inline int exp2i(int n)
|
||||
{
|
||||
return 1 << n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimate the number of bisect steps left (after the current step)
|
||||
*
|
||||
* For any x between 0 included and 2^n excluded, the probability for
|
||||
* n - 1 steps left looks like:
|
||||
*
|
||||
* P(2^n + x) == (2^n - x) / (2^n + x)
|
||||
*
|
||||
* and P(2^n + x) < 0.5 means 2^n < 3x
|
||||
*/
|
||||
int estimate_bisect_steps(int all)
|
||||
{
|
||||
int n, x, e;
|
||||
|
||||
if (all < 3)
|
||||
return 0;
|
||||
|
||||
n = log2i(all);
|
||||
e = exp2i(n);
|
||||
x = all - e;
|
||||
|
||||
return (e < 3 * x) ? n : n - 1;
|
||||
}
|
||||
|
4
bisect.h
4
bisect.h
@ -11,6 +11,10 @@ extern struct commit_list *filter_skipped(struct commit_list *list,
|
||||
int *count,
|
||||
int *skipped_first);
|
||||
|
||||
extern void print_commit_list(struct commit_list *list,
|
||||
const char *format_cur,
|
||||
const char *format_last);
|
||||
|
||||
#define BISECT_SHOW_ALL (1<<0)
|
||||
#define REV_LIST_QUIET (1<<1)
|
||||
|
||||
|
@ -37,6 +37,10 @@ int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
|
||||
const unsigned char *from_obj, const unsigned char *to_obj);
|
||||
void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
|
||||
|
||||
extern int check_pager_config(const char *cmd);
|
||||
struct diff_options;
|
||||
extern void setup_diff_pager(struct diff_options *);
|
||||
|
||||
extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
|
||||
|
||||
extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||
|
@ -1425,7 +1425,7 @@ static void get_commit_info(struct commit *commit,
|
||||
int detailed)
|
||||
{
|
||||
int len;
|
||||
const char *subject, *encoding;
|
||||
const char *subject;
|
||||
char *reencoded, *message;
|
||||
static char author_name[1024];
|
||||
static char author_mail[1024];
|
||||
@ -1446,8 +1446,7 @@ static void get_commit_info(struct commit *commit,
|
||||
die("Cannot read commit %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
encoding = get_log_output_encoding();
|
||||
reencoded = logmsg_reencode(commit, encoding);
|
||||
reencoded = reencode_commit_message(commit, NULL);
|
||||
message = reencoded ? reencoded : commit->buffer;
|
||||
ret->author = author_name;
|
||||
ret->author_mail = author_mail;
|
||||
|
@ -112,11 +112,10 @@ static const char *only_include_assumed;
|
||||
static struct strbuf message = STRBUF_INIT;
|
||||
|
||||
static enum {
|
||||
STATUS_FORMAT_NONE = 0,
|
||||
STATUS_FORMAT_LONG,
|
||||
STATUS_FORMAT_SHORT,
|
||||
STATUS_FORMAT_PORCELAIN
|
||||
} status_format;
|
||||
} status_format = STATUS_FORMAT_LONG;
|
||||
|
||||
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
@ -455,7 +454,6 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
|
||||
case STATUS_FORMAT_PORCELAIN:
|
||||
wt_porcelain_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_NONE:
|
||||
case STATUS_FORMAT_LONG:
|
||||
wt_status_print(s);
|
||||
break;
|
||||
@ -755,7 +753,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
ident_shown++ ? "" : "\n",
|
||||
author_ident->buf);
|
||||
|
||||
if (!committer_ident_sufficiently_given())
|
||||
if (!user_ident_sufficiently_given())
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL,
|
||||
_("%s"
|
||||
"Committer: %s"),
|
||||
@ -1060,13 +1058,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
||||
if (all && argc > 0)
|
||||
die(_("Paths with -a does not make sense."));
|
||||
|
||||
if (s->null_termination) {
|
||||
if (status_format == STATUS_FORMAT_NONE)
|
||||
status_format = STATUS_FORMAT_PORCELAIN;
|
||||
else if (status_format == STATUS_FORMAT_LONG)
|
||||
die(_("--long and -z are incompatible"));
|
||||
}
|
||||
if (status_format != STATUS_FORMAT_NONE)
|
||||
if (s->null_termination && status_format == STATUS_FORMAT_LONG)
|
||||
status_format = STATUS_FORMAT_PORCELAIN;
|
||||
if (status_format != STATUS_FORMAT_LONG)
|
||||
dry_run = 1;
|
||||
|
||||
return argc;
|
||||
@ -1165,9 +1159,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
OPT_SET_INT(0, "porcelain", &status_format,
|
||||
N_("machine-readable output"),
|
||||
STATUS_FORMAT_PORCELAIN),
|
||||
OPT_SET_INT(0, "long", &status_format,
|
||||
N_("show status in long format (default)"),
|
||||
STATUS_FORMAT_LONG),
|
||||
OPT_BOOLEAN('z', "null", &s.null_termination,
|
||||
N_("terminate entries with NUL")),
|
||||
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
|
||||
@ -1195,12 +1186,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
builtin_status_usage, 0);
|
||||
finalize_colopts(&s.colopts, -1);
|
||||
|
||||
if (s.null_termination) {
|
||||
if (status_format == STATUS_FORMAT_NONE)
|
||||
status_format = STATUS_FORMAT_PORCELAIN;
|
||||
else if (status_format == STATUS_FORMAT_LONG)
|
||||
die(_("--long and -z are incompatible"));
|
||||
}
|
||||
if (s.null_termination && status_format == STATUS_FORMAT_LONG)
|
||||
status_format = STATUS_FORMAT_PORCELAIN;
|
||||
|
||||
handle_untracked_files_arg(&s);
|
||||
if (show_ignored_in_status)
|
||||
@ -1229,7 +1216,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
case STATUS_FORMAT_PORCELAIN:
|
||||
wt_porcelain_print(&s);
|
||||
break;
|
||||
case STATUS_FORMAT_NONE:
|
||||
case STATUS_FORMAT_LONG:
|
||||
s.verbose = verbose;
|
||||
s.ignore_submodule_arg = ignore_submodule_arg;
|
||||
@ -1265,7 +1251,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
|
||||
strbuf_addstr(&format, "\n Author: ");
|
||||
strbuf_addbuf_percentquote(&format, &author_ident);
|
||||
}
|
||||
if (!committer_ident_sufficiently_given()) {
|
||||
if (!user_ident_sufficiently_given()) {
|
||||
strbuf_addstr(&format, "\n Committer: ");
|
||||
strbuf_addbuf_percentquote(&format, &committer_ident);
|
||||
if (advice_implicit_identity) {
|
||||
@ -1400,9 +1386,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
|
||||
OPT_SET_INT(0, "porcelain", &status_format,
|
||||
N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
|
||||
OPT_SET_INT(0, "long", &status_format,
|
||||
N_("show status in long format (default)"),
|
||||
STATUS_FORMAT_LONG),
|
||||
OPT_BOOLEAN('z', "null", &s.null_termination,
|
||||
N_("terminate entries with NUL")),
|
||||
OPT_BOOLEAN(0, "amend", &amend, N_("amend previous commit")),
|
||||
|
106
builtin/config.c
106
builtin/config.c
@ -15,6 +15,7 @@ static int show_keys;
|
||||
static int use_key_regexp;
|
||||
static int do_all;
|
||||
static int do_not_match;
|
||||
static int seen;
|
||||
static char delim = '=';
|
||||
static char key_delim = ' ';
|
||||
static char term = '\n';
|
||||
@ -94,19 +95,12 @@ static int show_all_config(const char *key_, const char *value_, void *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct strbuf_list {
|
||||
struct strbuf *items;
|
||||
int nr;
|
||||
int alloc;
|
||||
};
|
||||
|
||||
static int collect_config(const char *key_, const char *value_, void *cb)
|
||||
static int show_config(const char *key_, const char *value_, void *cb)
|
||||
{
|
||||
struct strbuf_list *values = cb;
|
||||
struct strbuf *buf;
|
||||
char value[256];
|
||||
const char *vptr = value;
|
||||
int must_free_vptr = 0;
|
||||
int dup_error = 0;
|
||||
int must_print_delim = 0;
|
||||
|
||||
if (!use_key_regexp && strcmp(key_, key))
|
||||
@ -117,14 +111,12 @@ static int collect_config(const char *key_, const char *value_, void *cb)
|
||||
(do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
|
||||
return 0;
|
||||
|
||||
ALLOC_GROW(values->items, values->nr + 1, values->alloc);
|
||||
buf = &values->items[values->nr++];
|
||||
strbuf_init(buf, 0);
|
||||
|
||||
if (show_keys) {
|
||||
strbuf_addstr(buf, key_);
|
||||
printf("%s", key_);
|
||||
must_print_delim = 1;
|
||||
}
|
||||
if (seen && !do_all)
|
||||
dup_error = 1;
|
||||
if (types == TYPE_INT)
|
||||
sprintf(value, "%d", git_config_int(key_, value_?value_:""));
|
||||
else if (types == TYPE_BOOL)
|
||||
@ -147,12 +139,16 @@ static int collect_config(const char *key_, const char *value_, void *cb)
|
||||
vptr = "";
|
||||
must_print_delim = 0;
|
||||
}
|
||||
|
||||
if (must_print_delim)
|
||||
strbuf_addch(buf, key_delim);
|
||||
strbuf_addstr(buf, vptr);
|
||||
strbuf_addch(buf, term);
|
||||
|
||||
seen++;
|
||||
if (dup_error) {
|
||||
error("More than one value for the key %s: %s",
|
||||
key_, vptr);
|
||||
}
|
||||
else {
|
||||
if (must_print_delim)
|
||||
printf("%c", key_delim);
|
||||
printf("%s%c", vptr, term);
|
||||
}
|
||||
if (must_free_vptr)
|
||||
/* If vptr must be freed, it's a pointer to a
|
||||
* dynamically allocated buffer, it's safe to cast to
|
||||
@ -166,8 +162,19 @@ static int collect_config(const char *key_, const char *value_, void *cb)
|
||||
static int get_value(const char *key_, const char *regex_)
|
||||
{
|
||||
int ret = CONFIG_GENERIC_ERROR;
|
||||
struct strbuf_list values = {NULL};
|
||||
int i;
|
||||
char *global = NULL, *xdg = NULL, *repo_config = NULL;
|
||||
const char *system_wide = NULL, *local;
|
||||
struct config_include_data inc = CONFIG_INCLUDE_INIT;
|
||||
config_fn_t fn;
|
||||
void *data;
|
||||
|
||||
local = given_config_file;
|
||||
if (!local) {
|
||||
local = repo_config = git_pathdup("config");
|
||||
if (git_config_system())
|
||||
system_wide = git_etc_gitconfig();
|
||||
home_config_paths(&global, &xdg, "config");
|
||||
}
|
||||
|
||||
if (use_key_regexp) {
|
||||
char *tl;
|
||||
@ -189,8 +196,7 @@ static int get_value(const char *key_, const char *regex_)
|
||||
key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
|
||||
if (regcomp(key_regexp, key, REG_EXTENDED)) {
|
||||
fprintf(stderr, "Invalid key pattern: %s\n", key_);
|
||||
free(key_regexp);
|
||||
key_regexp = NULL;
|
||||
free(key);
|
||||
ret = CONFIG_INVALID_PATTERN;
|
||||
goto free_strings;
|
||||
}
|
||||
@ -210,37 +216,53 @@ static int get_value(const char *key_, const char *regex_)
|
||||
regexp = (regex_t*)xmalloc(sizeof(regex_t));
|
||||
if (regcomp(regexp, regex_, REG_EXTENDED)) {
|
||||
fprintf(stderr, "Invalid pattern: %s\n", regex_);
|
||||
free(regexp);
|
||||
regexp = NULL;
|
||||
ret = CONFIG_INVALID_PATTERN;
|
||||
goto free_strings;
|
||||
}
|
||||
}
|
||||
|
||||
git_config_with_options(collect_config, &values,
|
||||
given_config_file, respect_includes);
|
||||
|
||||
ret = !values.nr;
|
||||
|
||||
for (i = 0; i < values.nr; i++) {
|
||||
struct strbuf *buf = values.items + i;
|
||||
if (do_all || i == values.nr - 1)
|
||||
fwrite(buf->buf, 1, buf->len, stdout);
|
||||
strbuf_release(buf);
|
||||
fn = show_config;
|
||||
data = NULL;
|
||||
if (respect_includes) {
|
||||
inc.fn = fn;
|
||||
inc.data = data;
|
||||
fn = git_config_include;
|
||||
data = &inc;
|
||||
}
|
||||
free(values.items);
|
||||
|
||||
free_strings:
|
||||
if (do_all && system_wide)
|
||||
git_config_from_file(fn, system_wide, data);
|
||||
if (do_all && xdg)
|
||||
git_config_from_file(fn, xdg, data);
|
||||
if (do_all && global)
|
||||
git_config_from_file(fn, global, data);
|
||||
if (do_all)
|
||||
git_config_from_file(fn, local, data);
|
||||
git_config_from_parameters(fn, data);
|
||||
if (!do_all && !seen)
|
||||
git_config_from_file(fn, local, data);
|
||||
if (!do_all && !seen && global)
|
||||
git_config_from_file(fn, global, data);
|
||||
if (!do_all && !seen && xdg)
|
||||
git_config_from_file(fn, xdg, data);
|
||||
if (!do_all && !seen && system_wide)
|
||||
git_config_from_file(fn, system_wide, data);
|
||||
|
||||
free(key);
|
||||
if (key_regexp) {
|
||||
regfree(key_regexp);
|
||||
free(key_regexp);
|
||||
}
|
||||
if (regexp) {
|
||||
regfree(regexp);
|
||||
free(regexp);
|
||||
}
|
||||
|
||||
if (do_all)
|
||||
ret = !seen;
|
||||
else
|
||||
ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
|
||||
|
||||
free_strings:
|
||||
free(repo_config);
|
||||
free(global);
|
||||
free(xdg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
|
||||
if (!all && !might_be_tag)
|
||||
return 0;
|
||||
|
||||
if (!peel_ref(path, peeled)) {
|
||||
if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
|
||||
is_tag = !!hashcmp(sha1, peeled);
|
||||
} else {
|
||||
hashcpy(peeled, sha1);
|
||||
|
@ -41,13 +41,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
|
||||
if (rev.pending.nr != 1 ||
|
||||
rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
|
||||
usage(diff_cache_usage);
|
||||
if (!cached) {
|
||||
if (!cached)
|
||||
setup_work_tree();
|
||||
if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
} else if (read_cache() < 0) {
|
||||
if (read_cache() < 0) {
|
||||
perror("read_cache");
|
||||
return -1;
|
||||
}
|
||||
|
@ -130,6 +130,8 @@ static int builtin_diff_index(struct rev_info *revs,
|
||||
usage(builtin_diff_usage);
|
||||
argv++; argc--;
|
||||
}
|
||||
if (!cached)
|
||||
setup_work_tree();
|
||||
/*
|
||||
* Make sure there is one revision (i.e. pending object),
|
||||
* and there is no revision filtering parameters.
|
||||
@ -138,14 +140,8 @@ static int builtin_diff_index(struct rev_info *revs,
|
||||
revs->max_count != -1 || revs->min_age != -1 ||
|
||||
revs->max_age != -1)
|
||||
usage(builtin_diff_usage);
|
||||
if (!cached) {
|
||||
setup_work_tree();
|
||||
if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
} else if (read_cache() < 0) {
|
||||
perror("read_cache");
|
||||
if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
|
||||
perror("read_cache_preload");
|
||||
return -1;
|
||||
}
|
||||
return run_diff_index(revs, cached);
|
||||
@ -422,3 +418,19 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
refresh_index_quietly();
|
||||
return result;
|
||||
}
|
||||
|
||||
void setup_diff_pager(struct diff_options *opt)
|
||||
{
|
||||
/*
|
||||
* If the user asked for our exit code, then either they want --quiet
|
||||
* or --exit-code. We should definitely not bother with a pager in the
|
||||
* former case, as we will generate no output. Since we still properly
|
||||
* report our exit code even when a pager is run, we _could_ run a
|
||||
* pager with --exit-code. But since we have not done so historically,
|
||||
* and because it is easy to find people oneline advising "git diff
|
||||
* --exit-code" in hooks and other scripts, we do not do so.
|
||||
*/
|
||||
if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
|
||||
check_pager_config("diff") != 0)
|
||||
setup_pager();
|
||||
}
|
||||
|
@ -1,12 +1,895 @@
|
||||
#include "builtin.h"
|
||||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
#include "commit.h"
|
||||
#include "tag.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "pack.h"
|
||||
#include "sideband.h"
|
||||
#include "fetch-pack.h"
|
||||
#include "remote.h"
|
||||
#include "run-command.h"
|
||||
#include "transport.h"
|
||||
#include "version.h"
|
||||
|
||||
static int transfer_unpack_limit = -1;
|
||||
static int fetch_unpack_limit = -1;
|
||||
static int unpack_limit = 100;
|
||||
static int prefer_ofs_delta = 1;
|
||||
static int no_done;
|
||||
static int fetch_fsck_objects = -1;
|
||||
static int transfer_fsck_objects = -1;
|
||||
static int agent_supported;
|
||||
static struct fetch_pack_args args = {
|
||||
/* .uploadpack = */ "git-upload-pack",
|
||||
};
|
||||
|
||||
static const char fetch_pack_usage[] =
|
||||
"git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
|
||||
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
|
||||
"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
|
||||
|
||||
#define COMPLETE (1U << 0)
|
||||
#define COMMON (1U << 1)
|
||||
#define COMMON_REF (1U << 2)
|
||||
#define SEEN (1U << 3)
|
||||
#define POPPED (1U << 4)
|
||||
|
||||
static int marked;
|
||||
|
||||
/*
|
||||
* After sending this many "have"s if we do not get any new ACK , we
|
||||
* give up traversing our history.
|
||||
*/
|
||||
#define MAX_IN_VAIN 256
|
||||
|
||||
static struct commit_list *rev_list;
|
||||
static int non_common_revs, multi_ack, use_sideband;
|
||||
|
||||
static void rev_list_push(struct commit *commit, int mark)
|
||||
{
|
||||
if (!(commit->object.flags & mark)) {
|
||||
commit->object.flags |= mark;
|
||||
|
||||
if (!(commit->object.parsed))
|
||||
if (parse_commit(commit))
|
||||
return;
|
||||
|
||||
commit_list_insert_by_date(commit, &rev_list);
|
||||
|
||||
if (!(commit->object.flags & COMMON))
|
||||
non_common_revs++;
|
||||
}
|
||||
}
|
||||
|
||||
static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct object *o = deref_tag(parse_object(sha1), refname, 0);
|
||||
|
||||
if (o && o->type == OBJ_COMMIT)
|
||||
rev_list_push((struct commit *)o, SEEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct object *o = deref_tag(parse_object(sha1), refname, 0);
|
||||
|
||||
if (o && o->type == OBJ_COMMIT)
|
||||
clear_commit_marks((struct commit *)o,
|
||||
COMMON | COMMON_REF | SEEN | POPPED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
This function marks a rev and its ancestors as common.
|
||||
In some cases, it is desirable to mark only the ancestors (for example
|
||||
when only the server does not yet know that they are common).
|
||||
*/
|
||||
|
||||
static void mark_common(struct commit *commit,
|
||||
int ancestors_only, int dont_parse)
|
||||
{
|
||||
if (commit != NULL && !(commit->object.flags & COMMON)) {
|
||||
struct object *o = (struct object *)commit;
|
||||
|
||||
if (!ancestors_only)
|
||||
o->flags |= COMMON;
|
||||
|
||||
if (!(o->flags & SEEN))
|
||||
rev_list_push(commit, SEEN);
|
||||
else {
|
||||
struct commit_list *parents;
|
||||
|
||||
if (!ancestors_only && !(o->flags & POPPED))
|
||||
non_common_revs--;
|
||||
if (!o->parsed && !dont_parse)
|
||||
if (parse_commit(commit))
|
||||
return;
|
||||
|
||||
for (parents = commit->parents;
|
||||
parents;
|
||||
parents = parents->next)
|
||||
mark_common(parents->item, 0, dont_parse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Get the next rev to send, ignoring the common.
|
||||
*/
|
||||
|
||||
static const unsigned char *get_rev(void)
|
||||
{
|
||||
struct commit *commit = NULL;
|
||||
|
||||
while (commit == NULL) {
|
||||
unsigned int mark;
|
||||
struct commit_list *parents;
|
||||
|
||||
if (rev_list == NULL || non_common_revs == 0)
|
||||
return NULL;
|
||||
|
||||
commit = rev_list->item;
|
||||
if (!commit->object.parsed)
|
||||
parse_commit(commit);
|
||||
parents = commit->parents;
|
||||
|
||||
commit->object.flags |= POPPED;
|
||||
if (!(commit->object.flags & COMMON))
|
||||
non_common_revs--;
|
||||
|
||||
if (commit->object.flags & COMMON) {
|
||||
/* do not send "have", and ignore ancestors */
|
||||
commit = NULL;
|
||||
mark = COMMON | SEEN;
|
||||
} else if (commit->object.flags & COMMON_REF)
|
||||
/* send "have", and ignore ancestors */
|
||||
mark = COMMON | SEEN;
|
||||
else
|
||||
/* send "have", also for its ancestors */
|
||||
mark = SEEN;
|
||||
|
||||
while (parents) {
|
||||
if (!(parents->item->object.flags & SEEN))
|
||||
rev_list_push(parents->item, mark);
|
||||
if (mark & COMMON)
|
||||
mark_common(parents->item, 1, 0);
|
||||
parents = parents->next;
|
||||
}
|
||||
|
||||
rev_list = rev_list->next;
|
||||
}
|
||||
|
||||
return commit->object.sha1;
|
||||
}
|
||||
|
||||
enum ack_type {
|
||||
NAK = 0,
|
||||
ACK,
|
||||
ACK_continue,
|
||||
ACK_common,
|
||||
ACK_ready
|
||||
};
|
||||
|
||||
static void consume_shallow_list(int fd)
|
||||
{
|
||||
if (args.stateless_rpc && args.depth > 0) {
|
||||
/* If we sent a depth we will get back "duplicate"
|
||||
* shallow and unshallow commands every time there
|
||||
* is a block of have lines exchanged.
|
||||
*/
|
||||
char line[1000];
|
||||
while (packet_read_line(fd, line, sizeof(line))) {
|
||||
if (!prefixcmp(line, "shallow "))
|
||||
continue;
|
||||
if (!prefixcmp(line, "unshallow "))
|
||||
continue;
|
||||
die("git fetch-pack: expected shallow list");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct write_shallow_data {
|
||||
struct strbuf *out;
|
||||
int use_pack_protocol;
|
||||
int count;
|
||||
};
|
||||
|
||||
static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
|
||||
{
|
||||
struct write_shallow_data *data = cb_data;
|
||||
const char *hex = sha1_to_hex(graft->sha1);
|
||||
data->count++;
|
||||
if (data->use_pack_protocol)
|
||||
packet_buf_write(data->out, "shallow %s", hex);
|
||||
else {
|
||||
strbuf_addstr(data->out, hex);
|
||||
strbuf_addch(data->out, '\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
|
||||
{
|
||||
struct write_shallow_data data;
|
||||
data.out = out;
|
||||
data.use_pack_protocol = use_pack_protocol;
|
||||
data.count = 0;
|
||||
for_each_commit_graft(write_one_shallow, &data);
|
||||
return data.count;
|
||||
}
|
||||
|
||||
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
|
||||
{
|
||||
static char line[1000];
|
||||
int len = packet_read_line(fd, line, sizeof(line));
|
||||
|
||||
if (!len)
|
||||
die("git fetch-pack: expected ACK/NAK, got EOF");
|
||||
if (line[len-1] == '\n')
|
||||
line[--len] = 0;
|
||||
if (!strcmp(line, "NAK"))
|
||||
return NAK;
|
||||
if (!prefixcmp(line, "ACK ")) {
|
||||
if (!get_sha1_hex(line+4, result_sha1)) {
|
||||
if (strstr(line+45, "continue"))
|
||||
return ACK_continue;
|
||||
if (strstr(line+45, "common"))
|
||||
return ACK_common;
|
||||
if (strstr(line+45, "ready"))
|
||||
return ACK_ready;
|
||||
return ACK;
|
||||
}
|
||||
}
|
||||
die("git fetch_pack: expected ACK/NAK, got '%s'", line);
|
||||
}
|
||||
|
||||
static void send_request(int fd, struct strbuf *buf)
|
||||
{
|
||||
if (args.stateless_rpc) {
|
||||
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
|
||||
packet_flush(fd);
|
||||
} else
|
||||
safe_write(fd, buf->buf, buf->len);
|
||||
}
|
||||
|
||||
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
|
||||
{
|
||||
rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
|
||||
}
|
||||
|
||||
#define INITIAL_FLUSH 16
|
||||
#define PIPESAFE_FLUSH 32
|
||||
#define LARGE_FLUSH 1024
|
||||
|
||||
static int next_flush(int count)
|
||||
{
|
||||
int flush_limit = args.stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
|
||||
|
||||
if (count < flush_limit)
|
||||
count <<= 1;
|
||||
else
|
||||
count += flush_limit;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
struct ref *refs)
|
||||
{
|
||||
int fetching;
|
||||
int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
|
||||
const unsigned char *sha1;
|
||||
unsigned in_vain = 0;
|
||||
int got_continue = 0;
|
||||
int got_ready = 0;
|
||||
struct strbuf req_buf = STRBUF_INIT;
|
||||
size_t state_len = 0;
|
||||
|
||||
if (args.stateless_rpc && multi_ack == 1)
|
||||
die("--stateless-rpc requires multi_ack_detailed");
|
||||
if (marked)
|
||||
for_each_ref(clear_marks, NULL);
|
||||
marked = 1;
|
||||
|
||||
for_each_ref(rev_list_insert_ref, NULL);
|
||||
for_each_alternate_ref(insert_one_alternate_ref, NULL);
|
||||
|
||||
fetching = 0;
|
||||
for ( ; refs ; refs = refs->next) {
|
||||
unsigned char *remote = refs->old_sha1;
|
||||
const char *remote_hex;
|
||||
struct object *o;
|
||||
|
||||
/*
|
||||
* If that object is complete (i.e. it is an ancestor of a
|
||||
* local ref), we tell them we have it but do not have to
|
||||
* tell them about its ancestors, which they already know
|
||||
* about.
|
||||
*
|
||||
* We use lookup_object here because we are only
|
||||
* interested in the case we *know* the object is
|
||||
* reachable and we have already scanned it.
|
||||
*/
|
||||
if (((o = lookup_object(remote)) != NULL) &&
|
||||
(o->flags & COMPLETE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
remote_hex = sha1_to_hex(remote);
|
||||
if (!fetching) {
|
||||
struct strbuf c = STRBUF_INIT;
|
||||
if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed");
|
||||
if (multi_ack == 1) strbuf_addstr(&c, " multi_ack");
|
||||
if (no_done) strbuf_addstr(&c, " no-done");
|
||||
if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k");
|
||||
if (use_sideband == 1) strbuf_addstr(&c, " side-band");
|
||||
if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack");
|
||||
if (args.no_progress) strbuf_addstr(&c, " no-progress");
|
||||
if (args.include_tag) strbuf_addstr(&c, " include-tag");
|
||||
if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta");
|
||||
if (agent_supported) strbuf_addf(&c, " agent=%s",
|
||||
git_user_agent_sanitized());
|
||||
packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
|
||||
strbuf_release(&c);
|
||||
} else
|
||||
packet_buf_write(&req_buf, "want %s\n", remote_hex);
|
||||
fetching++;
|
||||
}
|
||||
|
||||
if (!fetching) {
|
||||
strbuf_release(&req_buf);
|
||||
packet_flush(fd[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_repository_shallow())
|
||||
write_shallow_commits(&req_buf, 1);
|
||||
if (args.depth > 0)
|
||||
packet_buf_write(&req_buf, "deepen %d", args.depth);
|
||||
packet_buf_flush(&req_buf);
|
||||
state_len = req_buf.len;
|
||||
|
||||
if (args.depth > 0) {
|
||||
char line[1024];
|
||||
unsigned char sha1[20];
|
||||
|
||||
send_request(fd[1], &req_buf);
|
||||
while (packet_read_line(fd[0], line, sizeof(line))) {
|
||||
if (!prefixcmp(line, "shallow ")) {
|
||||
if (get_sha1_hex(line + 8, sha1))
|
||||
die("invalid shallow line: %s", line);
|
||||
register_shallow(sha1);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(line, "unshallow ")) {
|
||||
if (get_sha1_hex(line + 10, sha1))
|
||||
die("invalid unshallow line: %s", line);
|
||||
if (!lookup_object(sha1))
|
||||
die("object not found: %s", line);
|
||||
/* make sure that it is parsed as shallow */
|
||||
if (!parse_object(sha1))
|
||||
die("error in object: %s", line);
|
||||
if (unregister_shallow(sha1))
|
||||
die("no shallow found: %s", line);
|
||||
continue;
|
||||
}
|
||||
die("expected shallow/unshallow, got %s", line);
|
||||
}
|
||||
} else if (!args.stateless_rpc)
|
||||
send_request(fd[1], &req_buf);
|
||||
|
||||
if (!args.stateless_rpc) {
|
||||
/* If we aren't using the stateless-rpc interface
|
||||
* we don't need to retain the headers.
|
||||
*/
|
||||
strbuf_setlen(&req_buf, 0);
|
||||
state_len = 0;
|
||||
}
|
||||
|
||||
flushes = 0;
|
||||
retval = -1;
|
||||
while ((sha1 = get_rev())) {
|
||||
packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
|
||||
in_vain++;
|
||||
if (flush_at <= ++count) {
|
||||
int ack;
|
||||
|
||||
packet_buf_flush(&req_buf);
|
||||
send_request(fd[1], &req_buf);
|
||||
strbuf_setlen(&req_buf, state_len);
|
||||
flushes++;
|
||||
flush_at = next_flush(count);
|
||||
|
||||
/*
|
||||
* We keep one window "ahead" of the other side, and
|
||||
* will wait for an ACK only on the next one
|
||||
*/
|
||||
if (!args.stateless_rpc && count == INITIAL_FLUSH)
|
||||
continue;
|
||||
|
||||
consume_shallow_list(fd[0]);
|
||||
do {
|
||||
ack = get_ack(fd[0], result_sha1);
|
||||
if (args.verbose && ack)
|
||||
fprintf(stderr, "got ack %d %s\n", ack,
|
||||
sha1_to_hex(result_sha1));
|
||||
switch (ack) {
|
||||
case ACK:
|
||||
flushes = 0;
|
||||
multi_ack = 0;
|
||||
retval = 0;
|
||||
goto done;
|
||||
case ACK_common:
|
||||
case ACK_ready:
|
||||
case ACK_continue: {
|
||||
struct commit *commit =
|
||||
lookup_commit(result_sha1);
|
||||
if (!commit)
|
||||
die("invalid commit %s", sha1_to_hex(result_sha1));
|
||||
if (args.stateless_rpc
|
||||
&& ack == ACK_common
|
||||
&& !(commit->object.flags & COMMON)) {
|
||||
/* We need to replay the have for this object
|
||||
* on the next RPC request so the peer knows
|
||||
* it is in common with us.
|
||||
*/
|
||||
const char *hex = sha1_to_hex(result_sha1);
|
||||
packet_buf_write(&req_buf, "have %s\n", hex);
|
||||
state_len = req_buf.len;
|
||||
}
|
||||
mark_common(commit, 0, 1);
|
||||
retval = 0;
|
||||
in_vain = 0;
|
||||
got_continue = 1;
|
||||
if (ack == ACK_ready) {
|
||||
rev_list = NULL;
|
||||
got_ready = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (ack);
|
||||
flushes--;
|
||||
if (got_continue && MAX_IN_VAIN < in_vain) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "giving up\n");
|
||||
break; /* give up */
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (!got_ready || !no_done) {
|
||||
packet_buf_write(&req_buf, "done\n");
|
||||
send_request(fd[1], &req_buf);
|
||||
}
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "done\n");
|
||||
if (retval != 0) {
|
||||
multi_ack = 0;
|
||||
flushes++;
|
||||
}
|
||||
strbuf_release(&req_buf);
|
||||
|
||||
consume_shallow_list(fd[0]);
|
||||
while (flushes || multi_ack) {
|
||||
int ack = get_ack(fd[0], result_sha1);
|
||||
if (ack) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "got ack (%d) %s\n", ack,
|
||||
sha1_to_hex(result_sha1));
|
||||
if (ack == ACK)
|
||||
return 0;
|
||||
multi_ack = 1;
|
||||
continue;
|
||||
}
|
||||
flushes--;
|
||||
}
|
||||
/* it is no error to fetch into a completely empty repo */
|
||||
return count ? retval : 0;
|
||||
}
|
||||
|
||||
static struct commit_list *complete;
|
||||
|
||||
static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct object *o = parse_object(sha1);
|
||||
|
||||
while (o && o->type == OBJ_TAG) {
|
||||
struct tag *t = (struct tag *) o;
|
||||
if (!t->tagged)
|
||||
break; /* broken repository */
|
||||
o->flags |= COMPLETE;
|
||||
o = parse_object(t->tagged->sha1);
|
||||
}
|
||||
if (o && o->type == OBJ_COMMIT) {
|
||||
struct commit *commit = (struct commit *)o;
|
||||
if (!(commit->object.flags & COMPLETE)) {
|
||||
commit->object.flags |= COMPLETE;
|
||||
commit_list_insert_by_date(commit, &complete);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark_recent_complete_commits(unsigned long cutoff)
|
||||
{
|
||||
while (complete && cutoff <= complete->item->date) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Marking %s as complete\n",
|
||||
sha1_to_hex(complete->item->object.sha1));
|
||||
pop_most_recent_commit(&complete, COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
static int non_matching_ref(struct string_list_item *item, void *unused)
|
||||
{
|
||||
if (item->util) {
|
||||
item->util = NULL;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void filter_refs(struct ref **refs, struct string_list *sought)
|
||||
{
|
||||
struct ref *newlist = NULL;
|
||||
struct ref **newtail = &newlist;
|
||||
struct ref *ref, *next;
|
||||
int sought_pos;
|
||||
|
||||
sought_pos = 0;
|
||||
for (ref = *refs; ref; ref = next) {
|
||||
int keep = 0;
|
||||
next = ref->next;
|
||||
if (!memcmp(ref->name, "refs/", 5) &&
|
||||
check_refname_format(ref->name + 5, 0))
|
||||
; /* trash */
|
||||
else {
|
||||
while (sought_pos < sought->nr) {
|
||||
int cmp = strcmp(ref->name, sought->items[sought_pos].string);
|
||||
if (cmp < 0)
|
||||
break; /* definitely do not have it */
|
||||
else if (cmp == 0) {
|
||||
keep = 1; /* definitely have it */
|
||||
sought->items[sought_pos++].util = "matched";
|
||||
break;
|
||||
}
|
||||
else
|
||||
sought_pos++; /* might have it; keep looking */
|
||||
}
|
||||
}
|
||||
|
||||
if (! keep && args.fetch_all &&
|
||||
(!args.depth || prefixcmp(ref->name, "refs/tags/")))
|
||||
keep = 1;
|
||||
|
||||
if (keep) {
|
||||
*newtail = ref;
|
||||
ref->next = NULL;
|
||||
newtail = &ref->next;
|
||||
} else {
|
||||
free(ref);
|
||||
}
|
||||
}
|
||||
|
||||
filter_string_list(sought, 0, non_matching_ref, NULL);
|
||||
*refs = newlist;
|
||||
}
|
||||
|
||||
static void mark_alternate_complete(const struct ref *ref, void *unused)
|
||||
{
|
||||
mark_complete(NULL, ref->old_sha1, 0, NULL);
|
||||
}
|
||||
|
||||
static int everything_local(struct ref **refs, struct string_list *sought)
|
||||
{
|
||||
struct ref *ref;
|
||||
int retval;
|
||||
unsigned long cutoff = 0;
|
||||
|
||||
save_commit_buffer = 0;
|
||||
|
||||
for (ref = *refs; ref; ref = ref->next) {
|
||||
struct object *o;
|
||||
|
||||
o = parse_object(ref->old_sha1);
|
||||
if (!o)
|
||||
continue;
|
||||
|
||||
/* We already have it -- which may mean that we were
|
||||
* in sync with the other side at some time after
|
||||
* that (it is OK if we guess wrong here).
|
||||
*/
|
||||
if (o->type == OBJ_COMMIT) {
|
||||
struct commit *commit = (struct commit *)o;
|
||||
if (!cutoff || cutoff < commit->date)
|
||||
cutoff = commit->date;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.depth) {
|
||||
for_each_ref(mark_complete, NULL);
|
||||
for_each_alternate_ref(mark_alternate_complete, NULL);
|
||||
if (cutoff)
|
||||
mark_recent_complete_commits(cutoff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark all complete remote refs as common refs.
|
||||
* Don't mark them common yet; the server has to be told so first.
|
||||
*/
|
||||
for (ref = *refs; ref; ref = ref->next) {
|
||||
struct object *o = deref_tag(lookup_object(ref->old_sha1),
|
||||
NULL, 0);
|
||||
|
||||
if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
|
||||
continue;
|
||||
|
||||
if (!(o->flags & SEEN)) {
|
||||
rev_list_push((struct commit *)o, COMMON_REF | SEEN);
|
||||
|
||||
mark_common((struct commit *)o, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
filter_refs(refs, sought);
|
||||
|
||||
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
|
||||
const unsigned char *remote = ref->old_sha1;
|
||||
unsigned char local[20];
|
||||
struct object *o;
|
||||
|
||||
o = lookup_object(remote);
|
||||
if (!o || !(o->flags & COMPLETE)) {
|
||||
retval = 0;
|
||||
if (!args.verbose)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"want %s (%s)\n", sha1_to_hex(remote),
|
||||
ref->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
hashcpy(ref->new_sha1, local);
|
||||
if (!args.verbose)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"already have %s (%s)\n", sha1_to_hex(remote),
|
||||
ref->name);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sideband_demux(int in, int out, void *data)
|
||||
{
|
||||
int *xd = data;
|
||||
|
||||
int ret = recv_sideband("fetch-pack", xd[0], out);
|
||||
close(out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_pack(int xd[2], char **pack_lockfile)
|
||||
{
|
||||
struct async demux;
|
||||
const char *argv[20];
|
||||
char keep_arg[256];
|
||||
char hdr_arg[256];
|
||||
const char **av;
|
||||
int do_keep = args.keep_pack;
|
||||
struct child_process cmd;
|
||||
|
||||
memset(&demux, 0, sizeof(demux));
|
||||
if (use_sideband) {
|
||||
/* xd[] is talking with upload-pack; subprocess reads from
|
||||
* xd[0], spits out band#2 to stderr, and feeds us band#1
|
||||
* through demux->out.
|
||||
*/
|
||||
demux.proc = sideband_demux;
|
||||
demux.data = xd;
|
||||
demux.out = -1;
|
||||
if (start_async(&demux))
|
||||
die("fetch-pack: unable to fork off sideband"
|
||||
" demultiplexer");
|
||||
}
|
||||
else
|
||||
demux.out = xd[0];
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.argv = argv;
|
||||
av = argv;
|
||||
*hdr_arg = 0;
|
||||
if (!args.keep_pack && unpack_limit) {
|
||||
struct pack_header header;
|
||||
|
||||
if (read_pack_header(demux.out, &header))
|
||||
die("protocol error: bad pack header");
|
||||
snprintf(hdr_arg, sizeof(hdr_arg),
|
||||
"--pack_header=%"PRIu32",%"PRIu32,
|
||||
ntohl(header.hdr_version), ntohl(header.hdr_entries));
|
||||
if (ntohl(header.hdr_entries) < unpack_limit)
|
||||
do_keep = 0;
|
||||
else
|
||||
do_keep = 1;
|
||||
}
|
||||
|
||||
if (do_keep) {
|
||||
if (pack_lockfile)
|
||||
cmd.out = -1;
|
||||
*av++ = "index-pack";
|
||||
*av++ = "--stdin";
|
||||
if (!args.quiet && !args.no_progress)
|
||||
*av++ = "-v";
|
||||
if (args.use_thin_pack)
|
||||
*av++ = "--fix-thin";
|
||||
if (args.lock_pack || unpack_limit) {
|
||||
int s = sprintf(keep_arg,
|
||||
"--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
|
||||
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
|
||||
strcpy(keep_arg + s, "localhost");
|
||||
*av++ = keep_arg;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*av++ = "unpack-objects";
|
||||
if (args.quiet || args.no_progress)
|
||||
*av++ = "-q";
|
||||
}
|
||||
if (*hdr_arg)
|
||||
*av++ = hdr_arg;
|
||||
if (fetch_fsck_objects >= 0
|
||||
? fetch_fsck_objects
|
||||
: transfer_fsck_objects >= 0
|
||||
? transfer_fsck_objects
|
||||
: 0)
|
||||
*av++ = "--strict";
|
||||
*av++ = NULL;
|
||||
|
||||
cmd.in = demux.out;
|
||||
cmd.git_cmd = 1;
|
||||
if (start_command(&cmd))
|
||||
die("fetch-pack: unable to fork off %s", argv[0]);
|
||||
if (do_keep && pack_lockfile) {
|
||||
*pack_lockfile = index_pack_lockfile(cmd.out);
|
||||
close(cmd.out);
|
||||
}
|
||||
|
||||
if (finish_command(&cmd))
|
||||
die("%s failed", argv[0]);
|
||||
if (use_sideband && finish_async(&demux))
|
||||
die("error in sideband demultiplexer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ref *do_fetch_pack(int fd[2],
|
||||
const struct ref *orig_ref,
|
||||
struct string_list *sought,
|
||||
char **pack_lockfile)
|
||||
{
|
||||
struct ref *ref = copy_ref_list(orig_ref);
|
||||
unsigned char sha1[20];
|
||||
const char *agent_feature;
|
||||
int agent_len;
|
||||
|
||||
sort_ref_list(&ref, ref_compare_name);
|
||||
|
||||
if (is_repository_shallow() && !server_supports("shallow"))
|
||||
die("Server does not support shallow clients");
|
||||
if (server_supports("multi_ack_detailed")) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports multi_ack_detailed\n");
|
||||
multi_ack = 2;
|
||||
if (server_supports("no-done")) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports no-done\n");
|
||||
if (args.stateless_rpc)
|
||||
no_done = 1;
|
||||
}
|
||||
}
|
||||
else if (server_supports("multi_ack")) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports multi_ack\n");
|
||||
multi_ack = 1;
|
||||
}
|
||||
if (server_supports("side-band-64k")) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports side-band-64k\n");
|
||||
use_sideband = 2;
|
||||
}
|
||||
else if (server_supports("side-band")) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports side-band\n");
|
||||
use_sideband = 1;
|
||||
}
|
||||
if (!server_supports("thin-pack"))
|
||||
args.use_thin_pack = 0;
|
||||
if (!server_supports("no-progress"))
|
||||
args.no_progress = 0;
|
||||
if (!server_supports("include-tag"))
|
||||
args.include_tag = 0;
|
||||
if (server_supports("ofs-delta")) {
|
||||
if (args.verbose)
|
||||
fprintf(stderr, "Server supports ofs-delta\n");
|
||||
} else
|
||||
prefer_ofs_delta = 0;
|
||||
|
||||
if ((agent_feature = server_feature_value("agent", &agent_len))) {
|
||||
agent_supported = 1;
|
||||
if (args.verbose && agent_len)
|
||||
fprintf(stderr, "Server version is %.*s\n",
|
||||
agent_len, agent_feature);
|
||||
}
|
||||
|
||||
if (everything_local(&ref, sought)) {
|
||||
packet_flush(fd[1]);
|
||||
goto all_done;
|
||||
}
|
||||
if (find_common(fd, sha1, ref) < 0)
|
||||
if (!args.keep_pack)
|
||||
/* When cloning, it is not unusual to have
|
||||
* no common commit.
|
||||
*/
|
||||
warning("no common commits");
|
||||
|
||||
if (args.stateless_rpc)
|
||||
packet_flush(fd[1]);
|
||||
if (get_pack(fd, pack_lockfile))
|
||||
die("git fetch-pack: fetch failed.");
|
||||
|
||||
all_done:
|
||||
return ref;
|
||||
}
|
||||
|
||||
static int fetch_pack_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (strcmp(var, "fetch.unpacklimit") == 0) {
|
||||
fetch_unpack_limit = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(var, "transfer.unpacklimit") == 0) {
|
||||
transfer_unpack_limit = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
|
||||
prefer_ofs_delta = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "fetch.fsckobjects")) {
|
||||
fetch_fsck_objects = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "transfer.fsckobjects")) {
|
||||
transfer_fsck_objects = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static struct lock_file lock;
|
||||
|
||||
static void fetch_pack_setup(void)
|
||||
{
|
||||
static int did_setup;
|
||||
if (did_setup)
|
||||
return;
|
||||
git_config(fetch_pack_config, NULL);
|
||||
if (0 <= transfer_unpack_limit)
|
||||
unpack_limit = transfer_unpack_limit;
|
||||
else if (0 <= fetch_unpack_limit)
|
||||
unpack_limit = fetch_unpack_limit;
|
||||
did_setup = 1;
|
||||
}
|
||||
|
||||
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, ret;
|
||||
@ -17,13 +900,9 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
||||
char *pack_lockfile = NULL;
|
||||
char **pack_lockfile_ptr = NULL;
|
||||
struct child_process *conn;
|
||||
struct fetch_pack_args args;
|
||||
|
||||
packet_trace_identity("fetch-pack");
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
args.uploadpack = "git-upload-pack";
|
||||
|
||||
for (i = 1; i < argc && *argv[i] == '-'; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
@ -159,3 +1038,66 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ref *fetch_pack(struct fetch_pack_args *my_args,
|
||||
int fd[], struct child_process *conn,
|
||||
const struct ref *ref,
|
||||
const char *dest,
|
||||
struct string_list *sought,
|
||||
char **pack_lockfile)
|
||||
{
|
||||
struct stat st;
|
||||
struct ref *ref_cpy;
|
||||
|
||||
fetch_pack_setup();
|
||||
if (&args != my_args)
|
||||
memcpy(&args, my_args, sizeof(args));
|
||||
if (args.depth > 0) {
|
||||
if (stat(git_path("shallow"), &st))
|
||||
st.st_mtime = 0;
|
||||
}
|
||||
|
||||
if (sought->nr) {
|
||||
sort_string_list(sought);
|
||||
string_list_remove_duplicates(sought, 0);
|
||||
}
|
||||
|
||||
if (!ref) {
|
||||
packet_flush(fd[1]);
|
||||
die("no matching remote head");
|
||||
}
|
||||
ref_cpy = do_fetch_pack(fd, ref, sought, pack_lockfile);
|
||||
|
||||
if (args.depth > 0) {
|
||||
struct cache_time mtime;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
char *shallow = git_path("shallow");
|
||||
int fd;
|
||||
|
||||
mtime.sec = st.st_mtime;
|
||||
mtime.nsec = ST_MTIME_NSEC(st);
|
||||
if (stat(shallow, &st)) {
|
||||
if (mtime.sec)
|
||||
die("shallow file was removed during fetch");
|
||||
} else if (st.st_mtime != mtime.sec
|
||||
#ifdef USE_NSEC
|
||||
|| ST_MTIME_NSEC(st) != mtime.nsec
|
||||
#endif
|
||||
)
|
||||
die("shallow file was changed during fetch");
|
||||
|
||||
fd = hold_lock_file_for_update(&lock, shallow,
|
||||
LOCK_DIE_ON_ERROR);
|
||||
if (!write_shallow_commits(&sb, 0)
|
||||
|| write_in_full(fd, sb.buf, sb.len) != sb.len) {
|
||||
unlink_or_warn(shallow);
|
||||
rollback_lock_file(&lock);
|
||||
} else {
|
||||
commit_lock_file(&lock);
|
||||
}
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
reprepare_packed_git();
|
||||
return ref_cpy;
|
||||
}
|
||||
|
@ -351,8 +351,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
|
||||
}
|
||||
if (!prefixcmp(var, "color.decorate."))
|
||||
return parse_decorate_color_config(var, 15, value);
|
||||
if (grep_config(var, value, cb) < 0)
|
||||
return -1;
|
||||
|
||||
return git_diff_ui_config(var, value, cb);
|
||||
}
|
||||
|
||||
@ -361,7 +360,6 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
|
||||
struct rev_info rev;
|
||||
struct setup_revision_opt opt;
|
||||
|
||||
init_grep_defaults();
|
||||
git_config(git_log_config, NULL);
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
@ -452,7 +450,6 @@ int cmd_show(int argc, const char **argv, const char *prefix)
|
||||
struct pathspec match_all;
|
||||
int i, count, ret = 0;
|
||||
|
||||
init_grep_defaults();
|
||||
git_config(git_log_config, NULL);
|
||||
|
||||
init_pathspec(&match_all, NULL);
|
||||
@ -533,7 +530,6 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
|
||||
struct rev_info rev;
|
||||
struct setup_revision_opt opt;
|
||||
|
||||
init_grep_defaults();
|
||||
git_config(git_log_config, NULL);
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
@ -556,7 +552,6 @@ int cmd_log(int argc, const char **argv, const char *prefix)
|
||||
struct rev_info rev;
|
||||
struct setup_revision_opt opt;
|
||||
|
||||
init_grep_defaults();
|
||||
git_config(git_log_config, NULL);
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
@ -1126,7 +1121,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
extra_hdr.strdup_strings = 1;
|
||||
extra_to.strdup_strings = 1;
|
||||
extra_cc.strdup_strings = 1;
|
||||
init_grep_defaults();
|
||||
git_config(git_format_config, NULL);
|
||||
init_revisions(&rev, prefix);
|
||||
rev.commit_format = CMIT_FMT_EMAIL;
|
||||
|
106
builtin/merge.c
106
builtin/merge.c
@ -628,6 +628,59 @@ static void write_tree_trivial(unsigned char *sha1)
|
||||
die(_("git write-tree failed to write a tree"));
|
||||
}
|
||||
|
||||
static const char *merge_argument(struct commit *commit)
|
||||
{
|
||||
if (commit)
|
||||
return sha1_to_hex(commit->object.sha1);
|
||||
else
|
||||
return EMPTY_TREE_SHA1_HEX;
|
||||
}
|
||||
|
||||
int try_merge_command(const char *strategy, size_t xopts_nr,
|
||||
const char **xopts, struct commit_list *common,
|
||||
const char *head_arg, struct commit_list *remotes)
|
||||
{
|
||||
const char **args;
|
||||
int i = 0, x = 0, ret;
|
||||
struct commit_list *j;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
args = xmalloc((4 + xopts_nr + commit_list_count(common) +
|
||||
commit_list_count(remotes)) * sizeof(char *));
|
||||
strbuf_addf(&buf, "merge-%s", strategy);
|
||||
args[i++] = buf.buf;
|
||||
for (x = 0; x < xopts_nr; x++) {
|
||||
char *s = xmalloc(strlen(xopts[x])+2+1);
|
||||
strcpy(s, "--");
|
||||
strcpy(s+2, xopts[x]);
|
||||
args[i++] = s;
|
||||
}
|
||||
for (j = common; j; j = j->next)
|
||||
args[i++] = xstrdup(merge_argument(j->item));
|
||||
args[i++] = "--";
|
||||
args[i++] = head_arg;
|
||||
for (j = remotes; j; j = j->next)
|
||||
args[i++] = xstrdup(merge_argument(j->item));
|
||||
args[i] = NULL;
|
||||
ret = run_command_v_opt(args, RUN_GIT_CMD);
|
||||
strbuf_release(&buf);
|
||||
i = 1;
|
||||
for (x = 0; x < xopts_nr; x++)
|
||||
free((void *)args[i++]);
|
||||
for (j = common; j; j = j->next)
|
||||
free((void *)args[i++]);
|
||||
i += 2;
|
||||
for (j = remotes; j; j = j->next)
|
||||
free((void *)args[i++]);
|
||||
free(args);
|
||||
discard_cache();
|
||||
if (read_cache() < 0)
|
||||
die(_("failed to read the cache"));
|
||||
resolve_undo_clear();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int try_merge_strategy(const char *strategy, struct commit_list *common,
|
||||
struct commit_list *remoteheads,
|
||||
struct commit *head, const char *head_arg)
|
||||
@ -709,6 +762,56 @@ static int count_unmerged_entries(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int checkout_fast_forward(const unsigned char *head, const unsigned char *remote)
|
||||
{
|
||||
struct tree *trees[MAX_UNPACK_TREES];
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc t[MAX_UNPACK_TREES];
|
||||
int i, fd, nr_trees = 0;
|
||||
struct dir_struct dir;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
fd = hold_locked_index(lock_file, 1);
|
||||
|
||||
memset(&trees, 0, sizeof(trees));
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
memset(&t, 0, sizeof(t));
|
||||
if (overwrite_ignore) {
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
dir.flags |= DIR_SHOW_IGNORED;
|
||||
setup_standard_excludes(&dir);
|
||||
opts.dir = &dir;
|
||||
}
|
||||
|
||||
opts.head_idx = 1;
|
||||
opts.src_index = &the_index;
|
||||
opts.dst_index = &the_index;
|
||||
opts.update = 1;
|
||||
opts.verbose_update = 1;
|
||||
opts.merge = 1;
|
||||
opts.fn = twoway_merge;
|
||||
setup_unpack_trees_porcelain(&opts, "merge");
|
||||
|
||||
trees[nr_trees] = parse_tree_indirect(head);
|
||||
if (!trees[nr_trees++])
|
||||
return -1;
|
||||
trees[nr_trees] = parse_tree_indirect(remote);
|
||||
if (!trees[nr_trees++])
|
||||
return -1;
|
||||
for (i = 0; i < nr_trees; i++) {
|
||||
parse_tree(trees[i]);
|
||||
init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
|
||||
}
|
||||
if (unpack_trees(nr_trees, t, &opts))
|
||||
return -1;
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
die(_("unable to write new index file"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void split_merge_strategies(const char *string, struct strategy **list,
|
||||
int *nr, int *alloc)
|
||||
{
|
||||
@ -1321,8 +1424,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (checkout_fast_forward(head_commit->object.sha1,
|
||||
commit->object.sha1,
|
||||
overwrite_ignore)) {
|
||||
commit->object.sha1)) {
|
||||
ret = 1;
|
||||
goto done;
|
||||
}
|
||||
|
@ -2033,6 +2033,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo
|
||||
|
||||
if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
|
||||
!peel_ref(path, peeled) && /* peelable? */
|
||||
!is_null_sha1(peeled) && /* annotated tag? */
|
||||
locate_object_entry(peeled)) /* object packed? */
|
||||
add_object_entry(sha1, OBJ_TAG, NULL, 0);
|
||||
return 0;
|
||||
|
@ -46,27 +46,24 @@ typedef int (*each_replace_name_fn)(const char *name, const char *ref,
|
||||
|
||||
static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
|
||||
{
|
||||
const char **p, *full_hex;
|
||||
const char **p;
|
||||
char ref[PATH_MAX];
|
||||
int had_error = 0;
|
||||
unsigned char sha1[20];
|
||||
|
||||
for (p = argv; *p; p++) {
|
||||
if (get_sha1(*p, sha1)) {
|
||||
error("Failed to resolve '%s' as a valid ref.", *p);
|
||||
if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p)
|
||||
>= sizeof(ref)) {
|
||||
error("replace ref name too long: %.*s...", 50, *p);
|
||||
had_error = 1;
|
||||
continue;
|
||||
}
|
||||
full_hex = sha1_to_hex(sha1);
|
||||
snprintf(ref, sizeof(ref), "refs/replace/%s", full_hex);
|
||||
/* read_ref() may reuse the buffer */
|
||||
full_hex = ref + strlen("refs/replace/");
|
||||
if (read_ref(ref, sha1)) {
|
||||
error("replace ref '%s' not found.", full_hex);
|
||||
error("replace ref '%s' not found.", *p);
|
||||
had_error = 1;
|
||||
continue;
|
||||
}
|
||||
if (fn(full_hex, ref, sha1))
|
||||
if (fn(*p, ref, sha1))
|
||||
had_error = 1;
|
||||
}
|
||||
return had_error;
|
||||
|
@ -201,6 +201,55 @@ static void show_edge(struct commit *commit)
|
||||
printf("-%s\n", sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
|
||||
static inline int log2i(int n)
|
||||
{
|
||||
int log2 = 0;
|
||||
|
||||
for (; n > 1; n >>= 1)
|
||||
log2++;
|
||||
|
||||
return log2;
|
||||
}
|
||||
|
||||
static inline int exp2i(int n)
|
||||
{
|
||||
return 1 << n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimate the number of bisect steps left (after the current step)
|
||||
*
|
||||
* For any x between 0 included and 2^n excluded, the probability for
|
||||
* n - 1 steps left looks like:
|
||||
*
|
||||
* P(2^n + x) == (2^n - x) / (2^n + x)
|
||||
*
|
||||
* and P(2^n + x) < 0.5 means 2^n < 3x
|
||||
*/
|
||||
int estimate_bisect_steps(int all)
|
||||
{
|
||||
int n, x, e;
|
||||
|
||||
if (all < 3)
|
||||
return 0;
|
||||
|
||||
n = log2i(all);
|
||||
e = exp2i(n);
|
||||
x = all - e;
|
||||
|
||||
return (e < 3 * x) ? n : n - 1;
|
||||
}
|
||||
|
||||
void print_commit_list(struct commit_list *list,
|
||||
const char *format_cur,
|
||||
const char *format_last)
|
||||
{
|
||||
for ( ; list; list = list->next) {
|
||||
const char *format = list->next ? format_cur : format_last;
|
||||
printf(format, sha1_to_hex(list->item->object.sha1));
|
||||
}
|
||||
}
|
||||
|
||||
static void print_var_str(const char *var, const char *val)
|
||||
{
|
||||
printf("%s='%s'\n", var, val);
|
||||
|
140
builtin/rm.c
140
builtin/rm.c
@ -9,7 +9,6 @@
|
||||
#include "cache-tree.h"
|
||||
#include "tree-walk.h"
|
||||
#include "parse-options.h"
|
||||
#include "submodule.h"
|
||||
|
||||
static const char * const builtin_rm_usage[] = {
|
||||
N_("git rm [options] [--] <file>..."),
|
||||
@ -18,58 +17,9 @@ static const char * const builtin_rm_usage[] = {
|
||||
|
||||
static struct {
|
||||
int nr, alloc;
|
||||
struct {
|
||||
const char *name;
|
||||
char is_submodule;
|
||||
} *entry;
|
||||
const char **name;
|
||||
} list;
|
||||
|
||||
static int get_ours_cache_pos(const char *path, int pos)
|
||||
{
|
||||
int i = -pos - 1;
|
||||
|
||||
while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) {
|
||||
if (ce_stage(active_cache[i]) == 2)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int check_submodules_use_gitfiles(void)
|
||||
{
|
||||
int i;
|
||||
int errs = 0;
|
||||
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *name = list.entry[i].name;
|
||||
int pos;
|
||||
struct cache_entry *ce;
|
||||
struct stat st;
|
||||
|
||||
pos = cache_name_pos(name, strlen(name));
|
||||
if (pos < 0) {
|
||||
pos = get_ours_cache_pos(name, pos);
|
||||
if (pos < 0)
|
||||
continue;
|
||||
}
|
||||
ce = active_cache[pos];
|
||||
|
||||
if (!S_ISGITLINK(ce->ce_mode) ||
|
||||
(lstat(ce->name, &st) < 0) ||
|
||||
is_empty_dir(name))
|
||||
continue;
|
||||
|
||||
if (!submodule_uses_gitfile(name))
|
||||
errs = error(_("submodule '%s' (or one of its nested "
|
||||
"submodules) uses a .git directory\n"
|
||||
"(use 'rm -rf' if you really want to remove "
|
||||
"it including all of its history)"), name);
|
||||
}
|
||||
|
||||
return errs;
|
||||
}
|
||||
|
||||
static int check_local_mod(unsigned char *head, int index_only)
|
||||
{
|
||||
/*
|
||||
@ -87,26 +37,15 @@ static int check_local_mod(unsigned char *head, int index_only)
|
||||
struct stat st;
|
||||
int pos;
|
||||
struct cache_entry *ce;
|
||||
const char *name = list.entry[i].name;
|
||||
const char *name = list.name[i];
|
||||
unsigned char sha1[20];
|
||||
unsigned mode;
|
||||
int local_changes = 0;
|
||||
int staged_changes = 0;
|
||||
|
||||
pos = cache_name_pos(name, strlen(name));
|
||||
if (pos < 0) {
|
||||
/*
|
||||
* Skip unmerged entries except for populated submodules
|
||||
* that could lose history when removed.
|
||||
*/
|
||||
pos = get_ours_cache_pos(name, pos);
|
||||
if (pos < 0)
|
||||
continue;
|
||||
|
||||
if (!S_ISGITLINK(active_cache[pos]->ce_mode) ||
|
||||
is_empty_dir(name))
|
||||
continue;
|
||||
}
|
||||
if (pos < 0)
|
||||
continue; /* removing unmerged entry */
|
||||
ce = active_cache[pos];
|
||||
|
||||
if (lstat(ce->name, &st) < 0) {
|
||||
@ -119,10 +58,9 @@ static int check_local_mod(unsigned char *head, int index_only)
|
||||
/* if a file was removed and it is now a
|
||||
* directory, that is the same as ENOENT as
|
||||
* far as git is concerned; we do not track
|
||||
* directories unless they are submodules.
|
||||
* directories.
|
||||
*/
|
||||
if (!S_ISGITLINK(ce->ce_mode))
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -142,11 +80,8 @@ static int check_local_mod(unsigned char *head, int index_only)
|
||||
|
||||
/*
|
||||
* Is the index different from the file in the work tree?
|
||||
* If it's a submodule, is its work tree modified?
|
||||
*/
|
||||
if (ce_match_stat(ce, &st, 0) ||
|
||||
(S_ISGITLINK(ce->ce_mode) &&
|
||||
!ok_to_remove_submodule(ce->name)))
|
||||
if (ce_match_stat(ce, &st, 0))
|
||||
local_changes = 1;
|
||||
|
||||
/*
|
||||
@ -180,18 +115,10 @@ static int check_local_mod(unsigned char *head, int index_only)
|
||||
errs = error(_("'%s' has changes staged in the index\n"
|
||||
"(use --cached to keep the file, "
|
||||
"or -f to force removal)"), name);
|
||||
if (local_changes) {
|
||||
if (S_ISGITLINK(ce->ce_mode) &&
|
||||
!submodule_uses_gitfile(name)) {
|
||||
errs = error(_("submodule '%s' (or one of its nested "
|
||||
"submodules) uses a .git directory\n"
|
||||
"(use 'rm -rf' if you really want to remove "
|
||||
"it including all of its history)"), name);
|
||||
} else
|
||||
errs = error(_("'%s' has local modifications\n"
|
||||
"(use --cached to keep the file, "
|
||||
"or -f to force removal)"), name);
|
||||
}
|
||||
if (local_changes)
|
||||
errs = error(_("'%s' has local modifications\n"
|
||||
"(use --cached to keep the file, "
|
||||
"or -f to force removal)"), name);
|
||||
}
|
||||
}
|
||||
return errs;
|
||||
@ -234,21 +161,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
|
||||
/*
|
||||
* Drop trailing directory separators from directories so we'll find
|
||||
* submodules in the index.
|
||||
*/
|
||||
for (i = 0; i < argc; i++) {
|
||||
size_t pathlen = strlen(argv[i]);
|
||||
if (pathlen && is_dir_sep(argv[i][pathlen - 1]) &&
|
||||
is_directory(argv[i])) {
|
||||
do {
|
||||
pathlen--;
|
||||
} while (pathlen && is_dir_sep(argv[i][pathlen - 1]));
|
||||
argv[i] = xmemdupz(argv[i], pathlen);
|
||||
}
|
||||
}
|
||||
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);
|
||||
|
||||
@ -261,9 +173,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
|
||||
continue;
|
||||
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
|
||||
list.entry[list.nr].name = ce->name;
|
||||
list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode);
|
||||
ALLOC_GROW(list.name, list.nr + 1, list.alloc);
|
||||
list.name[list.nr++] = ce->name;
|
||||
}
|
||||
|
||||
if (pathspec) {
|
||||
@ -304,9 +215,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
hashclr(sha1);
|
||||
if (check_local_mod(sha1, index_only))
|
||||
exit(1);
|
||||
} else if (!index_only) {
|
||||
if (check_submodules_use_gitfiles())
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -314,7 +222,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
* the index unless all of them succeed.
|
||||
*/
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *path = list.entry[i].name;
|
||||
const char *path = list.name[i];
|
||||
if (!quiet)
|
||||
printf("rm '%s'\n", path);
|
||||
|
||||
@ -336,25 +244,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
||||
if (!index_only) {
|
||||
int removed = 0;
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *path = list.entry[i].name;
|
||||
if (list.entry[i].is_submodule) {
|
||||
if (is_empty_dir(path)) {
|
||||
if (!rmdir(path)) {
|
||||
removed = 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addstr(&buf, path);
|
||||
if (!remove_dir_recursively(&buf, 0)) {
|
||||
removed = 1;
|
||||
strbuf_release(&buf);
|
||||
continue;
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
/* Fallthrough and let remove_path() fail. */
|
||||
}
|
||||
}
|
||||
const char *path = list.name[i];
|
||||
if (!remove_path(path)) {
|
||||
removed = 1;
|
||||
continue;
|
||||
|
@ -16,6 +16,164 @@ static const char send_pack_usage[] =
|
||||
|
||||
static struct send_pack_args args;
|
||||
|
||||
static int feed_object(const unsigned char *sha1, int fd, int negative)
|
||||
{
|
||||
char buf[42];
|
||||
|
||||
if (negative && !has_sha1_file(sha1))
|
||||
return 1;
|
||||
|
||||
memcpy(buf + negative, sha1_to_hex(sha1), 40);
|
||||
if (negative)
|
||||
buf[0] = '^';
|
||||
buf[40 + negative] = '\n';
|
||||
return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a pack stream and spit it out into file descriptor fd
|
||||
*/
|
||||
static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
|
||||
{
|
||||
/*
|
||||
* The child becomes pack-objects --revs; we feed
|
||||
* the revision parameters to it via its stdin and
|
||||
* let its stdout go back to the other end.
|
||||
*/
|
||||
const char *argv[] = {
|
||||
"pack-objects",
|
||||
"--all-progress-implied",
|
||||
"--revs",
|
||||
"--stdout",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
struct child_process po;
|
||||
int i;
|
||||
|
||||
i = 4;
|
||||
if (args->use_thin_pack)
|
||||
argv[i++] = "--thin";
|
||||
if (args->use_ofs_delta)
|
||||
argv[i++] = "--delta-base-offset";
|
||||
if (args->quiet || !args->progress)
|
||||
argv[i++] = "-q";
|
||||
if (args->progress)
|
||||
argv[i++] = "--progress";
|
||||
memset(&po, 0, sizeof(po));
|
||||
po.argv = argv;
|
||||
po.in = -1;
|
||||
po.out = args->stateless_rpc ? -1 : fd;
|
||||
po.git_cmd = 1;
|
||||
if (start_command(&po))
|
||||
die_errno("git pack-objects failed");
|
||||
|
||||
/*
|
||||
* We feed the pack-objects we just spawned with revision
|
||||
* parameters by writing to the pipe.
|
||||
*/
|
||||
for (i = 0; i < extra->nr; i++)
|
||||
if (!feed_object(extra->array[i], po.in, 1))
|
||||
break;
|
||||
|
||||
while (refs) {
|
||||
if (!is_null_sha1(refs->old_sha1) &&
|
||||
!feed_object(refs->old_sha1, po.in, 1))
|
||||
break;
|
||||
if (!is_null_sha1(refs->new_sha1) &&
|
||||
!feed_object(refs->new_sha1, po.in, 0))
|
||||
break;
|
||||
refs = refs->next;
|
||||
}
|
||||
|
||||
close(po.in);
|
||||
|
||||
if (args->stateless_rpc) {
|
||||
char *buf = xmalloc(LARGE_PACKET_MAX);
|
||||
while (1) {
|
||||
ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
|
||||
if (n <= 0)
|
||||
break;
|
||||
send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
|
||||
}
|
||||
free(buf);
|
||||
close(po.out);
|
||||
po.out = -1;
|
||||
}
|
||||
|
||||
if (finish_command(&po))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int receive_status(int in, struct ref *refs)
|
||||
{
|
||||
struct ref *hint;
|
||||
char line[1000];
|
||||
int ret = 0;
|
||||
int len = packet_read_line(in, line, sizeof(line));
|
||||
if (len < 10 || memcmp(line, "unpack ", 7))
|
||||
return error("did not receive remote status");
|
||||
if (memcmp(line, "unpack ok\n", 10)) {
|
||||
char *p = line + strlen(line) - 1;
|
||||
if (*p == '\n')
|
||||
*p = '\0';
|
||||
error("unpack failed: %s", line + 7);
|
||||
ret = -1;
|
||||
}
|
||||
hint = NULL;
|
||||
while (1) {
|
||||
char *refname;
|
||||
char *msg;
|
||||
len = packet_read_line(in, line, sizeof(line));
|
||||
if (!len)
|
||||
break;
|
||||
if (len < 3 ||
|
||||
(memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
|
||||
fprintf(stderr, "protocol error: %s\n", line);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
line[strlen(line)-1] = '\0';
|
||||
refname = line + 3;
|
||||
msg = strchr(refname, ' ');
|
||||
if (msg)
|
||||
*msg++ = '\0';
|
||||
|
||||
/* first try searching at our hint, falling back to all refs */
|
||||
if (hint)
|
||||
hint = find_ref_by_name(hint, refname);
|
||||
if (!hint)
|
||||
hint = find_ref_by_name(refs, refname);
|
||||
if (!hint) {
|
||||
warning("remote reported status on unknown ref: %s",
|
||||
refname);
|
||||
continue;
|
||||
}
|
||||
if (hint->status != REF_STATUS_EXPECTING_REPORT) {
|
||||
warning("remote reported status on unexpected ref: %s",
|
||||
refname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == 'o' && line[1] == 'k')
|
||||
hint->status = REF_STATUS_OK;
|
||||
else {
|
||||
hint->status = REF_STATUS_REMOTE_REJECT;
|
||||
ret = -1;
|
||||
}
|
||||
if (msg)
|
||||
hint->remote_status = xstrdup(msg);
|
||||
/* start our next search from the next ref */
|
||||
hint = hint->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_helper_status(struct ref *ref)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
@ -69,6 +227,181 @@ static void print_helper_status(struct ref *ref)
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static int sideband_demux(int in, int out, void *data)
|
||||
{
|
||||
int *fd = data, ret;
|
||||
#ifdef NO_PTHREADS
|
||||
close(fd[1]);
|
||||
#endif
|
||||
ret = recv_sideband("send-pack", fd[0], out);
|
||||
close(out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int send_pack(struct send_pack_args *args,
|
||||
int fd[], struct child_process *conn,
|
||||
struct ref *remote_refs,
|
||||
struct extra_have_objects *extra_have)
|
||||
{
|
||||
int in = fd[0];
|
||||
int out = fd[1];
|
||||
struct strbuf req_buf = STRBUF_INIT;
|
||||
struct ref *ref;
|
||||
int new_refs;
|
||||
int allow_deleting_refs = 0;
|
||||
int status_report = 0;
|
||||
int use_sideband = 0;
|
||||
int quiet_supported = 0;
|
||||
int agent_supported = 0;
|
||||
unsigned cmds_sent = 0;
|
||||
int ret;
|
||||
struct async demux;
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
if (server_supports("report-status"))
|
||||
status_report = 1;
|
||||
if (server_supports("delete-refs"))
|
||||
allow_deleting_refs = 1;
|
||||
if (server_supports("ofs-delta"))
|
||||
args->use_ofs_delta = 1;
|
||||
if (server_supports("side-band-64k"))
|
||||
use_sideband = 1;
|
||||
if (server_supports("quiet"))
|
||||
quiet_supported = 1;
|
||||
if (server_supports("agent"))
|
||||
agent_supported = 1;
|
||||
|
||||
if (!remote_refs) {
|
||||
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
|
||||
"Perhaps you should specify a branch such as 'master'.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, tell the other end!
|
||||
*/
|
||||
new_refs = 0;
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
if (!ref->peer_ref && !args->send_mirror)
|
||||
continue;
|
||||
|
||||
/* Check for statuses set by set_ref_status_for_push() */
|
||||
switch (ref->status) {
|
||||
case REF_STATUS_REJECT_NONFASTFORWARD:
|
||||
case REF_STATUS_UPTODATE:
|
||||
continue;
|
||||
default:
|
||||
; /* do nothing */
|
||||
}
|
||||
|
||||
if (ref->deletion && !allow_deleting_refs) {
|
||||
ref->status = REF_STATUS_REJECT_NODELETE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ref->deletion)
|
||||
new_refs++;
|
||||
|
||||
if (args->dry_run) {
|
||||
ref->status = REF_STATUS_OK;
|
||||
} else {
|
||||
char *old_hex = sha1_to_hex(ref->old_sha1);
|
||||
char *new_hex = sha1_to_hex(ref->new_sha1);
|
||||
int quiet = quiet_supported && (args->quiet || !args->progress);
|
||||
|
||||
if (!cmds_sent && (status_report || use_sideband ||
|
||||
quiet || agent_supported)) {
|
||||
packet_buf_write(&req_buf,
|
||||
"%s %s %s%c%s%s%s%s%s",
|
||||
old_hex, new_hex, ref->name, 0,
|
||||
status_report ? " report-status" : "",
|
||||
use_sideband ? " side-band-64k" : "",
|
||||
quiet ? " quiet" : "",
|
||||
agent_supported ? " agent=" : "",
|
||||
agent_supported ? git_user_agent_sanitized() : ""
|
||||
);
|
||||
}
|
||||
else
|
||||
packet_buf_write(&req_buf, "%s %s %s",
|
||||
old_hex, new_hex, ref->name);
|
||||
ref->status = status_report ?
|
||||
REF_STATUS_EXPECTING_REPORT :
|
||||
REF_STATUS_OK;
|
||||
cmds_sent++;
|
||||
}
|
||||
}
|
||||
|
||||
if (args->stateless_rpc) {
|
||||
if (!args->dry_run && cmds_sent) {
|
||||
packet_buf_flush(&req_buf);
|
||||
send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
|
||||
}
|
||||
} else {
|
||||
safe_write(out, req_buf.buf, req_buf.len);
|
||||
packet_flush(out);
|
||||
}
|
||||
strbuf_release(&req_buf);
|
||||
|
||||
if (use_sideband && cmds_sent) {
|
||||
memset(&demux, 0, sizeof(demux));
|
||||
demux.proc = sideband_demux;
|
||||
demux.data = fd;
|
||||
demux.out = -1;
|
||||
if (start_async(&demux))
|
||||
die("send-pack: unable to fork off sideband demultiplexer");
|
||||
in = demux.out;
|
||||
}
|
||||
|
||||
if (new_refs && cmds_sent) {
|
||||
if (pack_objects(out, remote_refs, extra_have, args) < 0) {
|
||||
for (ref = remote_refs; ref; ref = ref->next)
|
||||
ref->status = REF_STATUS_NONE;
|
||||
if (args->stateless_rpc)
|
||||
close(out);
|
||||
if (git_connection_is_socket(conn))
|
||||
shutdown(fd[0], SHUT_WR);
|
||||
if (use_sideband)
|
||||
finish_async(&demux);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (args->stateless_rpc && cmds_sent)
|
||||
packet_flush(out);
|
||||
|
||||
if (status_report && cmds_sent)
|
||||
ret = receive_status(in, remote_refs);
|
||||
else
|
||||
ret = 0;
|
||||
if (args->stateless_rpc)
|
||||
packet_flush(out);
|
||||
|
||||
if (use_sideband && cmds_sent) {
|
||||
if (finish_async(&demux)) {
|
||||
error("error in sideband demultiplexer");
|
||||
ret = -1;
|
||||
}
|
||||
close(demux.out);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (args->porcelain)
|
||||
return 0;
|
||||
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
switch (ref->status) {
|
||||
case REF_STATUS_NONE:
|
||||
case REF_STATUS_UPTODATE:
|
||||
case REF_STATUS_OK:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_send_pack(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, nr_refspecs = 0;
|
||||
|
@ -28,6 +28,7 @@ static void show_one(const char *refname, const unsigned char *sha1)
|
||||
|
||||
static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
|
||||
{
|
||||
struct object *obj;
|
||||
const char *hex;
|
||||
unsigned char peeled[20];
|
||||
|
||||
@ -78,9 +79,25 @@ match:
|
||||
if (!deref_tags)
|
||||
return 0;
|
||||
|
||||
if (!peel_ref(refname, peeled)) {
|
||||
hex = find_unique_abbrev(peeled, abbrev);
|
||||
printf("%s %s^{}\n", hex, refname);
|
||||
if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
|
||||
if (!is_null_sha1(peeled)) {
|
||||
hex = find_unique_abbrev(peeled, abbrev);
|
||||
printf("%s %s^{}\n", hex, refname);
|
||||
}
|
||||
}
|
||||
else {
|
||||
obj = parse_object(sha1);
|
||||
if (!obj)
|
||||
die("git show-ref: bad ref %s (%s)", refname,
|
||||
sha1_to_hex(sha1));
|
||||
if (obj->type == OBJ_TAG) {
|
||||
obj = deref_tag(obj, refname, 0);
|
||||
if (!obj)
|
||||
die("git show-ref: bad tag at ref %s (%s)", refname,
|
||||
sha1_to_hex(sha1));
|
||||
hex = find_unique_abbrev(obj->sha1, abbrev);
|
||||
printf("%s %s^{}\n", hex, refname);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -5,11 +5,12 @@
|
||||
|
||||
static const char * const git_symbolic_ref_usage[] = {
|
||||
N_("git symbolic-ref [options] name [ref]"),
|
||||
N_("git symbolic-ref -d [-q] name"),
|
||||
NULL
|
||||
};
|
||||
|
||||
static int check_symref(const char *HEAD, int quiet, int shorten, int print)
|
||||
static int shorten;
|
||||
|
||||
static void check_symref(const char *HEAD, int quiet)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
int flag;
|
||||
@ -21,24 +22,20 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
|
||||
if (!quiet)
|
||||
die("ref %s is not a symbolic ref", HEAD);
|
||||
else
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
if (print) {
|
||||
if (shorten)
|
||||
refname = shorten_unambiguous_ref(refname, 0);
|
||||
puts(refname);
|
||||
}
|
||||
return 0;
|
||||
if (shorten)
|
||||
refname = shorten_unambiguous_ref(refname, 0);
|
||||
puts(refname);
|
||||
}
|
||||
|
||||
int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int quiet = 0, delete = 0, shorten = 0, ret = 0;
|
||||
int quiet = 0;
|
||||
const char *msg = NULL;
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&quiet,
|
||||
N_("suppress error message for non-symbolic (detached) refs")),
|
||||
OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")),
|
||||
OPT_BOOL(0, "short", &shorten, N_("shorten ref output")),
|
||||
OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")),
|
||||
OPT_END(),
|
||||
@ -49,19 +46,9 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
|
||||
git_symbolic_ref_usage, 0);
|
||||
if (msg &&!*msg)
|
||||
die("Refusing to perform update with empty message");
|
||||
|
||||
if (delete) {
|
||||
if (argc != 1)
|
||||
usage_with_options(git_symbolic_ref_usage, options);
|
||||
ret = check_symref(argv[0], 1, 0, 0);
|
||||
if (ret)
|
||||
die("Cannot delete %s, not a symbolic ref", argv[0]);
|
||||
return delete_ref(argv[0], NULL, REF_NODEREF);
|
||||
}
|
||||
|
||||
switch (argc) {
|
||||
case 1:
|
||||
ret = check_symref(argv[0], quiet, shorten, 1);
|
||||
check_symref(argv[0], quiet);
|
||||
break;
|
||||
case 2:
|
||||
if (!strcmp(argv[0], "HEAD") &&
|
||||
@ -72,5 +59,5 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
|
||||
default:
|
||||
usage_with_options(git_symbolic_ref_usage, options);
|
||||
}
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
@ -593,7 +593,6 @@ struct refresh_params {
|
||||
static int refresh(struct refresh_params *o, unsigned int flag)
|
||||
{
|
||||
setup_work_tree();
|
||||
read_cache_preload(NULL);
|
||||
*o->has_errors |= refresh_cache(o->flags | flag);
|
||||
return 0;
|
||||
}
|
||||
|
19
cache.h
19
cache.h
@ -1149,8 +1149,11 @@ struct config_include_data {
|
||||
#define CONFIG_INCLUDE_INIT { 0 }
|
||||
extern int git_config_include(const char *name, const char *value, void *data);
|
||||
|
||||
extern int committer_ident_sufficiently_given(void);
|
||||
extern int author_ident_sufficiently_given(void);
|
||||
#define IDENT_NAME_GIVEN 01
|
||||
#define IDENT_MAIL_GIVEN 02
|
||||
#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
|
||||
extern int user_ident_explicitly_given;
|
||||
extern int user_ident_sufficiently_given(void);
|
||||
|
||||
extern const char *git_commit_encoding;
|
||||
extern const char *git_log_output_encoding;
|
||||
@ -1180,7 +1183,6 @@ extern int pager_in_use(void);
|
||||
extern int pager_use_color;
|
||||
extern int term_columns(void);
|
||||
extern int decimal_width(int);
|
||||
extern int check_pager_config(const char *cmd);
|
||||
|
||||
extern const char *editor_program;
|
||||
extern const char *askpass_program;
|
||||
@ -1263,15 +1265,8 @@ struct startup_info {
|
||||
};
|
||||
extern struct startup_info *startup_info;
|
||||
|
||||
/* merge.c */
|
||||
struct commit_list;
|
||||
int try_merge_command(const char *strategy, size_t xopts_nr,
|
||||
const char **xopts, struct commit_list *common,
|
||||
const char *head_arg, struct commit_list *remotes);
|
||||
int checkout_fast_forward(const unsigned char *from,
|
||||
const unsigned char *to,
|
||||
int overwrite_ignore);
|
||||
|
||||
/* builtin/merge.c */
|
||||
int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
|
||||
|
||||
int sane_execvp(const char *file, char *const argv[]);
|
||||
|
||||
|
10
commit.c
10
commit.c
@ -1347,13 +1347,3 @@ struct commit_list **commit_list_append(struct commit *commit,
|
||||
new->next = NULL;
|
||||
return &new->next;
|
||||
}
|
||||
|
||||
void print_commit_list(struct commit_list *list,
|
||||
const char *format_cur,
|
||||
const char *format_last)
|
||||
{
|
||||
for ( ; list; list = list->next) {
|
||||
const char *format = list->next ? format_cur : format_last;
|
||||
printf(format, sha1_to_hex(list->item->object.sha1));
|
||||
}
|
||||
}
|
||||
|
8
commit.h
8
commit.h
@ -86,7 +86,7 @@ struct pretty_print_context {
|
||||
enum date_mode date_mode;
|
||||
unsigned date_mode_explicit:1;
|
||||
int need_8bit_cte;
|
||||
char *notes_message;
|
||||
int show_notes;
|
||||
struct reflog_walk_info *reflog_info;
|
||||
const char *output_encoding;
|
||||
};
|
||||
@ -99,6 +99,8 @@ extern int has_non_ascii(const char *text);
|
||||
struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
|
||||
extern char *logmsg_reencode(const struct commit *commit,
|
||||
const char *output_encoding);
|
||||
extern char *reencode_commit_message(const struct commit *commit,
|
||||
const char **encoding_p);
|
||||
extern void get_commit_format(const char *arg, struct rev_info *);
|
||||
extern const char *format_subject(struct strbuf *sb, const char *msg,
|
||||
const char *line_separator);
|
||||
@ -220,8 +222,4 @@ struct commit *get_merge_parent(const char *name);
|
||||
|
||||
extern int parse_signed_commit(const unsigned char *sha1,
|
||||
struct strbuf *message, struct strbuf *signature);
|
||||
extern void print_commit_list(struct commit_list *list,
|
||||
const char *format_cur,
|
||||
const char *format_last);
|
||||
|
||||
#endif /* COMMIT_H */
|
||||
|
@ -1,13 +1,6 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifdef CYGWIN_V15_WIN32API
|
||||
#include "../git-compat-util.h"
|
||||
#include "win32.h"
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <sys/errno.h>
|
||||
#include "win32.h"
|
||||
#include "../git-compat-util.h"
|
||||
#endif
|
||||
#include "../cache.h" /* to read configuration */
|
||||
|
||||
static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
|
||||
|
114
compat/mingw.c
114
compat/mingw.c
@ -256,8 +256,6 @@ int mingw_rmdir(const char *pathname)
|
||||
|
||||
while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
||||
if (!is_file_in_use_error(GetLastError()))
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
if (errno != EACCES)
|
||||
break;
|
||||
if (!is_dir_empty(pathname)) {
|
||||
errno = ENOTEMPTY;
|
||||
@ -273,7 +271,7 @@ int mingw_rmdir(const char *pathname)
|
||||
Sleep(delay[tries]);
|
||||
tries++;
|
||||
}
|
||||
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
|
||||
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
|
||||
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
|
||||
"Should I try again?", pathname))
|
||||
ret = rmdir(pathname);
|
||||
@ -321,31 +319,6 @@ ssize_t mingw_write(int fd, const void *buf, size_t count)
|
||||
return write(fd, buf, min(count, 31 * 1024 * 1024));
|
||||
}
|
||||
|
||||
static BOOL WINAPI ctrl_ignore(DWORD type)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef fgetc
|
||||
int mingw_fgetc(FILE *stream)
|
||||
{
|
||||
int ch;
|
||||
if (!isatty(_fileno(stream)))
|
||||
return fgetc(stream);
|
||||
|
||||
SetConsoleCtrlHandler(ctrl_ignore, TRUE);
|
||||
while (1) {
|
||||
ch = fgetc(stream);
|
||||
if (ch != EOF || GetLastError() != ERROR_OPERATION_ABORTED)
|
||||
break;
|
||||
|
||||
/* Ctrl+C was pressed, simulate SIGINT and retry */
|
||||
mingw_raise(SIGINT);
|
||||
}
|
||||
SetConsoleCtrlHandler(ctrl_ignore, FALSE);
|
||||
return ch;
|
||||
}
|
||||
|
||||
#undef fopen
|
||||
FILE *mingw_fopen (const char *filename, const char *otype)
|
||||
{
|
||||
@ -362,28 +335,6 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
||||
return freopen(filename, otype, stream);
|
||||
}
|
||||
|
||||
#undef fflush
|
||||
int mingw_fflush(FILE *stream)
|
||||
{
|
||||
int ret = fflush(stream);
|
||||
|
||||
/*
|
||||
* write() is used behind the scenes of stdio output functions.
|
||||
* Since git code does not check for errors after each stdio write
|
||||
* operation, it can happen that write() is called by a later
|
||||
* stdio function even if an earlier write() call failed. In the
|
||||
* case of a pipe whose readable end was closed, only the first
|
||||
* call to write() reports EPIPE on Windows. Subsequent write()
|
||||
* calls report EINVAL. It is impossible to notice whether this
|
||||
* fflush invocation triggered such a case, therefore, we have to
|
||||
* catch all EINVAL errors whole-sale.
|
||||
*/
|
||||
if (ret && errno == EINVAL)
|
||||
errno = EPIPE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
|
||||
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
|
||||
@ -1573,7 +1524,7 @@ static HANDLE timer_event;
|
||||
static HANDLE timer_thread;
|
||||
static int timer_interval;
|
||||
static int one_shot;
|
||||
static sig_handler_t timer_fn = SIG_DFL, sigint_fn = SIG_DFL;
|
||||
static sig_handler_t timer_fn = SIG_DFL;
|
||||
|
||||
/* The timer works like this:
|
||||
* The thread, ticktack(), is a trivial routine that most of the time
|
||||
@ -1587,7 +1538,10 @@ static sig_handler_t timer_fn = SIG_DFL, sigint_fn = SIG_DFL;
|
||||
static unsigned __stdcall ticktack(void *dummy)
|
||||
{
|
||||
while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
|
||||
mingw_raise(SIGALRM);
|
||||
if (timer_fn == SIG_DFL)
|
||||
die("Alarm");
|
||||
if (timer_fn != SIG_IGN)
|
||||
timer_fn(SIGALRM);
|
||||
if (one_shot)
|
||||
break;
|
||||
}
|
||||
@ -1678,49 +1632,12 @@ int sigaction(int sig, struct sigaction *in, struct sigaction *out)
|
||||
sig_handler_t mingw_signal(int sig, sig_handler_t handler)
|
||||
{
|
||||
sig_handler_t old = timer_fn;
|
||||
|
||||
switch (sig) {
|
||||
case SIGALRM:
|
||||
timer_fn = handler;
|
||||
break;
|
||||
|
||||
case SIGINT:
|
||||
sigint_fn = handler;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (sig != SIGALRM)
|
||||
return signal(sig, handler);
|
||||
}
|
||||
|
||||
timer_fn = handler;
|
||||
return old;
|
||||
}
|
||||
|
||||
#undef raise
|
||||
int mingw_raise(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGALRM:
|
||||
if (timer_fn == SIG_DFL) {
|
||||
if (isatty(STDERR_FILENO))
|
||||
fputs("Alarm clock\n", stderr);
|
||||
exit(128 + SIGALRM);
|
||||
} else if (timer_fn != SIG_IGN)
|
||||
timer_fn(SIGALRM);
|
||||
return 0;
|
||||
|
||||
case SIGINT:
|
||||
if (sigint_fn == SIG_DFL)
|
||||
exit(128 + SIGINT);
|
||||
else if (sigint_fn != SIG_IGN)
|
||||
sigint_fn(SIGINT);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return raise(sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *make_backslash_path(const char *path)
|
||||
{
|
||||
static char buf[PATH_MAX + 1];
|
||||
@ -1782,6 +1699,21 @@ int link(const char *oldpath, const char *newpath)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *getpass(const char *prompt)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
fputs(prompt, stderr);
|
||||
for (;;) {
|
||||
char c = _getch();
|
||||
if (c == '\r' || c == '\n')
|
||||
break;
|
||||
strbuf_addch(&buf, c);
|
||||
}
|
||||
fputs("\n", stderr);
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
pid_t waitpid(pid_t pid, int *status, int options)
|
||||
{
|
||||
HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
|
||||
|
@ -55,6 +55,8 @@ struct passwd {
|
||||
char *pw_dir;
|
||||
};
|
||||
|
||||
extern char *getpass(const char *prompt);
|
||||
|
||||
typedef void (__cdecl *sig_handler_t)(int);
|
||||
struct sigaction {
|
||||
sig_handler_t sa_handler;
|
||||
@ -177,18 +179,12 @@ int mingw_open (const char *filename, int oflags, ...);
|
||||
ssize_t mingw_write(int fd, const void *buf, size_t count);
|
||||
#define write mingw_write
|
||||
|
||||
int mingw_fgetc(FILE *stream);
|
||||
#define fgetc mingw_fgetc
|
||||
|
||||
FILE *mingw_fopen (const char *filename, const char *otype);
|
||||
#define fopen mingw_fopen
|
||||
|
||||
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
|
||||
#define freopen mingw_freopen
|
||||
|
||||
int mingw_fflush(FILE *stream);
|
||||
#define fflush mingw_fflush
|
||||
|
||||
char *mingw_getcwd(char *pointer, int len);
|
||||
#define getcwd mingw_getcwd
|
||||
|
||||
@ -291,9 +287,6 @@ static inline unsigned int git_ntohl(unsigned int x)
|
||||
sig_handler_t mingw_signal(int sig, sig_handler_t handler);
|
||||
#define signal mingw_signal
|
||||
|
||||
int mingw_raise(int sig);
|
||||
#define raise mingw_raise
|
||||
|
||||
/*
|
||||
* ANSI emulation wrappers
|
||||
*/
|
||||
|
@ -3,22 +3,8 @@
|
||||
#include "sigchain.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
#if defined(HAVE_DEV_TTY) || defined(WIN32)
|
||||
|
||||
static void restore_term(void);
|
||||
|
||||
static void restore_term_on_signal(int sig)
|
||||
{
|
||||
restore_term();
|
||||
sigchain_pop(sig);
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DEV_TTY
|
||||
|
||||
#define INPUT_PATH "/dev/tty"
|
||||
#define OUTPUT_PATH "/dev/tty"
|
||||
|
||||
static int term_fd = -1;
|
||||
static struct termios old_term;
|
||||
|
||||
@ -28,109 +14,58 @@ static void restore_term(void)
|
||||
return;
|
||||
|
||||
tcsetattr(term_fd, TCSAFLUSH, &old_term);
|
||||
close(term_fd);
|
||||
term_fd = -1;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
static void restore_term_on_signal(int sig)
|
||||
{
|
||||
struct termios t;
|
||||
|
||||
term_fd = open("/dev/tty", O_RDWR);
|
||||
if (tcgetattr(term_fd, &t) < 0)
|
||||
goto error;
|
||||
|
||||
old_term = t;
|
||||
sigchain_push_common(restore_term_on_signal);
|
||||
|
||||
t.c_lflag &= ~ECHO;
|
||||
if (!tcsetattr(term_fd, TCSAFLUSH, &t))
|
||||
return 0;
|
||||
|
||||
error:
|
||||
close(term_fd);
|
||||
term_fd = -1;
|
||||
return -1;
|
||||
restore_term();
|
||||
sigchain_pop(sig);
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
#elif defined(WIN32)
|
||||
|
||||
#define INPUT_PATH "CONIN$"
|
||||
#define OUTPUT_PATH "CONOUT$"
|
||||
#define FORCE_TEXT "t"
|
||||
|
||||
static HANDLE hconin = INVALID_HANDLE_VALUE;
|
||||
static DWORD cmode;
|
||||
|
||||
static void restore_term(void)
|
||||
{
|
||||
if (hconin == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
SetConsoleMode(hconin, cmode);
|
||||
CloseHandle(hconin);
|
||||
hconin = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
{
|
||||
hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hconin == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
GetConsoleMode(hconin, &cmode);
|
||||
sigchain_push_common(restore_term_on_signal);
|
||||
if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
|
||||
CloseHandle(hconin);
|
||||
hconin = INVALID_HANDLE_VALUE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef FORCE_TEXT
|
||||
#define FORCE_TEXT
|
||||
#endif
|
||||
|
||||
char *git_terminal_prompt(const char *prompt, int echo)
|
||||
{
|
||||
static struct strbuf buf = STRBUF_INIT;
|
||||
int r;
|
||||
FILE *input_fh, *output_fh;
|
||||
FILE *fh;
|
||||
|
||||
input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT);
|
||||
if (!input_fh)
|
||||
fh = fopen("/dev/tty", "w+");
|
||||
if (!fh)
|
||||
return NULL;
|
||||
|
||||
output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT);
|
||||
if (!output_fh) {
|
||||
fclose(input_fh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!echo && disable_echo()) {
|
||||
fclose(input_fh);
|
||||
fclose(output_fh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fputs(prompt, output_fh);
|
||||
fflush(output_fh);
|
||||
|
||||
r = strbuf_getline(&buf, input_fh, '\n');
|
||||
if (!echo) {
|
||||
putc('\n', output_fh);
|
||||
fflush(output_fh);
|
||||
struct termios t;
|
||||
|
||||
if (tcgetattr(fileno(fh), &t) < 0) {
|
||||
fclose(fh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
old_term = t;
|
||||
term_fd = fileno(fh);
|
||||
sigchain_push_common(restore_term_on_signal);
|
||||
|
||||
t.c_lflag &= ~ECHO;
|
||||
if (tcsetattr(fileno(fh), TCSAFLUSH, &t) < 0) {
|
||||
term_fd = -1;
|
||||
fclose(fh);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
fputs(prompt, fh);
|
||||
fflush(fh);
|
||||
|
||||
r = strbuf_getline(&buf, fh, '\n');
|
||||
if (!echo) {
|
||||
fseek(fh, SEEK_CUR, 0);
|
||||
putc('\n', fh);
|
||||
fflush(fh);
|
||||
}
|
||||
|
||||
restore_term();
|
||||
fclose(input_fh);
|
||||
fclose(output_fh);
|
||||
fclose(fh);
|
||||
|
||||
if (r == EOF)
|
||||
return NULL;
|
||||
|
1
config.c
1
config.c
@ -1279,7 +1279,6 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
|
||||
|
||||
out_free_ret_1:
|
||||
free(*store_key);
|
||||
*store_key = NULL;
|
||||
return -CONFIG_INVALID_KEY;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ datarootdir = @datarootdir@
|
||||
template_dir = @datadir@/git-core/templates
|
||||
sysconfdir = @sysconfdir@
|
||||
|
||||
mandir = @mandir@
|
||||
mandir=@mandir@
|
||||
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
@ -1024,7 +1024,7 @@ elif test -z "$PTHREAD_CFLAGS"; then
|
||||
for opt in -mt -pthread -lpthread; do
|
||||
old_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$opt $CFLAGS"
|
||||
AC_MSG_CHECKING([for POSIX Threads with '$opt'])
|
||||
AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
|
||||
AC_LINK_IFELSE([PTHREADTEST_SRC],
|
||||
[AC_MSG_RESULT([yes])
|
||||
NO_PTHREADS=
|
||||
@ -1044,7 +1044,7 @@ elif test -z "$PTHREAD_CFLAGS"; then
|
||||
else
|
||||
old_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
|
||||
AC_MSG_CHECKING([for POSIX Threads with '$PTHREAD_CFLAGS'])
|
||||
AC_MSG_CHECKING([Checking for POSIX Threads with '$PTHREAD_CFLAGS'])
|
||||
AC_LINK_IFELSE([PTHREADTEST_SRC],
|
||||
[AC_MSG_RESULT([yes])
|
||||
NO_PTHREADS=
|
||||
|
@ -23,6 +23,10 @@
|
||||
# 3) Consider changing your PS1 to also show the current branch,
|
||||
# see git-prompt.sh for details.
|
||||
|
||||
if [[ -n ${ZSH_VERSION-} ]]; then
|
||||
autoload -U +X bashcompinit && bashcompinit
|
||||
fi
|
||||
|
||||
case "$COMP_WORDBREAKS" in
|
||||
*:*) : great ;;
|
||||
*) COMP_WORDBREAKS="$COMP_WORDBREAKS:"
|
||||
@ -165,6 +169,7 @@ __git_reassemble_comp_words_by_ref()
|
||||
}
|
||||
|
||||
if ! type _get_comp_words_by_ref >/dev/null 2>&1; then
|
||||
if [[ -z ${ZSH_VERSION:+set} ]]; then
|
||||
_get_comp_words_by_ref ()
|
||||
{
|
||||
local exclude cur_ words_ cword_
|
||||
@ -192,6 +197,32 @@ _get_comp_words_by_ref ()
|
||||
shift
|
||||
done
|
||||
}
|
||||
else
|
||||
_get_comp_words_by_ref ()
|
||||
{
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
cur)
|
||||
cur=${COMP_WORDS[COMP_CWORD]}
|
||||
;;
|
||||
prev)
|
||||
prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||
;;
|
||||
words)
|
||||
words=("${COMP_WORDS[@]}")
|
||||
;;
|
||||
cword)
|
||||
cword=$COMP_CWORD
|
||||
;;
|
||||
-n)
|
||||
# assume COMP_WORDBREAKS is already set sanely
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
}
|
||||
fi
|
||||
fi
|
||||
|
||||
# Generates completion reply with compgen, appending a space to possible
|
||||
@ -554,7 +585,7 @@ __git_list_porcelain_commands ()
|
||||
{
|
||||
local i IFS=" "$'\n'
|
||||
__git_compute_all_commands
|
||||
for i in $__git_all_commands
|
||||
for i in "help" $__git_all_commands
|
||||
do
|
||||
case $i in
|
||||
*--*) : helper pattern;;
|
||||
@ -1087,14 +1118,6 @@ _git_fetch ()
|
||||
__git_complete_remote_or_refspec
|
||||
}
|
||||
|
||||
__git_format_patch_options="
|
||||
--stdout --attach --no-attach --thread --thread= --output-directory
|
||||
--numbered --start-number --numbered-files --keep-subject --signoff
|
||||
--signature --no-signature --in-reply-to= --cc= --full-index --binary
|
||||
--not --all --cover-letter --no-prefix --src-prefix= --dst-prefix=
|
||||
--inline --suffix= --ignore-if-in-upstream --subject-prefix=
|
||||
"
|
||||
|
||||
_git_format_patch ()
|
||||
{
|
||||
case "$cur" in
|
||||
@ -1105,7 +1128,21 @@ _git_format_patch ()
|
||||
return
|
||||
;;
|
||||
--*)
|
||||
__gitcomp "$__git_format_patch_options"
|
||||
__gitcomp "
|
||||
--stdout --attach --no-attach --thread --thread=
|
||||
--output-directory
|
||||
--numbered --start-number
|
||||
--numbered-files
|
||||
--keep-subject
|
||||
--signoff --signature --no-signature
|
||||
--in-reply-to= --cc=
|
||||
--full-index --binary
|
||||
--not --all
|
||||
--cover-letter
|
||||
--no-prefix --src-prefix= --dst-prefix=
|
||||
--inline --suffix= --ignore-if-in-upstream
|
||||
--subject-prefix=
|
||||
"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
@ -1519,12 +1556,6 @@ _git_send_email ()
|
||||
__gitcomp "ssl tls" "" "${cur##--smtp-encryption=}"
|
||||
return
|
||||
;;
|
||||
--thread=*)
|
||||
__gitcomp "
|
||||
deep shallow
|
||||
" "" "${cur##--thread=}"
|
||||
return
|
||||
;;
|
||||
--*)
|
||||
__gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
|
||||
--compose --confirm= --dry-run --envelope-sender
|
||||
@ -1534,12 +1565,11 @@ _git_send_email ()
|
||||
--signed-off-by-cc --smtp-pass --smtp-server
|
||||
--smtp-server-port --smtp-encryption= --smtp-user
|
||||
--subject --suppress-cc= --suppress-from --thread --to
|
||||
--validate --no-validate
|
||||
$__git_format_patch_options"
|
||||
--validate --no-validate"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
__git_complete_revlist
|
||||
COMPREPLY=()
|
||||
}
|
||||
|
||||
_git_stage ()
|
||||
@ -2401,71 +2431,20 @@ __gitk_main ()
|
||||
__git_complete_revlist
|
||||
}
|
||||
|
||||
if [[ -n ${ZSH_VERSION-} ]]; then
|
||||
echo "WARNING: this script is deprecated, please see git-completion.zsh" 1>&2
|
||||
|
||||
autoload -U +X compinit && compinit
|
||||
|
||||
__gitcomp ()
|
||||
{
|
||||
emulate -L zsh
|
||||
|
||||
local cur_="${3-$cur}"
|
||||
|
||||
case "$cur_" in
|
||||
--*=)
|
||||
;;
|
||||
*)
|
||||
local c IFS=$' \t\n'
|
||||
local -a array
|
||||
for c in ${=1}; do
|
||||
c="$c${4-}"
|
||||
case $c in
|
||||
--*=*|*.) ;;
|
||||
*) c="$c " ;;
|
||||
esac
|
||||
array+=("$c")
|
||||
done
|
||||
compset -P '*[=:]'
|
||||
compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
__gitcomp_nl ()
|
||||
{
|
||||
emulate -L zsh
|
||||
|
||||
local IFS=$'\n'
|
||||
compset -P '*[=:]'
|
||||
compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
|
||||
}
|
||||
|
||||
__git_zsh_helper ()
|
||||
{
|
||||
emulate -L ksh
|
||||
local cur cword prev
|
||||
cur=${words[CURRENT-1]}
|
||||
prev=${words[CURRENT-2]}
|
||||
let cword=CURRENT-1
|
||||
__${service}_main
|
||||
}
|
||||
|
||||
_git ()
|
||||
{
|
||||
emulate -L zsh
|
||||
local _ret=1
|
||||
__git_zsh_helper
|
||||
let _ret && _default -S '' && _ret=0
|
||||
return _ret
|
||||
}
|
||||
|
||||
compdef _git git gitk
|
||||
return
|
||||
fi
|
||||
|
||||
__git_func_wrap ()
|
||||
{
|
||||
if [[ -n ${ZSH_VERSION-} ]]; then
|
||||
emulate -L bash
|
||||
setopt KSH_TYPESET
|
||||
|
||||
# workaround zsh's bug that leaves 'words' as a special
|
||||
# variable in versions < 4.3.12
|
||||
typeset -h words
|
||||
|
||||
# workaround zsh's bug that quotes spaces in the COMPREPLY
|
||||
# array if IFS doesn't contain spaces.
|
||||
typeset -h IFS
|
||||
fi
|
||||
local cur words cword prev
|
||||
_get_comp_words_by_ref -n =: cur words cword prev
|
||||
$1
|
||||
|
@ -1,107 +0,0 @@
|
||||
#!tcsh
|
||||
#
|
||||
# tcsh completion support for core Git.
|
||||
#
|
||||
# Copyright (C) 2012 Marc Khouzam <marc.khouzam@gmail.com>
|
||||
# Distributed under the GNU General Public License, version 2.0.
|
||||
#
|
||||
# When sourced, this script will generate a new script that uses
|
||||
# the git-completion.bash script provided by core Git. This new
|
||||
# script can be used by tcsh to perform git completion.
|
||||
# The current script also issues the necessary tcsh 'complete'
|
||||
# commands.
|
||||
#
|
||||
# To use this completion script:
|
||||
#
|
||||
# 1) Copy both this file and the bash completion script to ${HOME}.
|
||||
# You _must_ use the name ${HOME}/.git-completion.bash for the
|
||||
# bash script.
|
||||
# (e.g. ~/.git-completion.tcsh and ~/.git-completion.bash).
|
||||
# 2) Add the following line to your .tcshrc/.cshrc:
|
||||
# source ~/.git-completion.tcsh
|
||||
# 3) For completion similar to bash, it is recommended to also
|
||||
# add the following line to your .tcshrc/.cshrc:
|
||||
# set autolist=ambiguous
|
||||
# It will tell tcsh to list the possible completion choices.
|
||||
|
||||
set __git_tcsh_completion_original_script = ${HOME}/.git-completion.bash
|
||||
set __git_tcsh_completion_script = ${HOME}/.git-completion.tcsh.bash
|
||||
|
||||
# Check that the user put the script in the right place
|
||||
if ( ! -e ${__git_tcsh_completion_original_script} ) then
|
||||
echo "git-completion.tcsh: Cannot find: ${__git_tcsh_completion_original_script}. Git completion will not work."
|
||||
exit
|
||||
endif
|
||||
|
||||
cat << EOF > ${__git_tcsh_completion_script}
|
||||
#!bash
|
||||
#
|
||||
# This script is GENERATED and will be overwritten automatically.
|
||||
# Do not modify it directly. Instead, modify git-completion.tcsh
|
||||
# and source it again.
|
||||
|
||||
source ${__git_tcsh_completion_original_script}
|
||||
|
||||
# Set COMP_WORDS in a way that can be handled by the bash script.
|
||||
COMP_WORDS=(\$2)
|
||||
|
||||
# The cursor is at the end of parameter #1.
|
||||
# We must check for a space as the last character which will
|
||||
# tell us that the previous word is complete and the cursor
|
||||
# is on the next word.
|
||||
if [ "\${2: -1}" == " " ]; then
|
||||
# The last character is a space, so our location is at the end
|
||||
# of the command-line array
|
||||
COMP_CWORD=\${#COMP_WORDS[@]}
|
||||
else
|
||||
# The last character is not a space, so our location is on the
|
||||
# last word of the command-line array, so we must decrement the
|
||||
# count by 1
|
||||
COMP_CWORD=\$((\${#COMP_WORDS[@]}-1))
|
||||
fi
|
||||
|
||||
# Call _git() or _gitk() of the bash script, based on the first argument
|
||||
_\${1}
|
||||
|
||||
IFS=\$'\n'
|
||||
if [ \${#COMPREPLY[*]} -gt 0 ]; then
|
||||
echo "\${COMPREPLY[*]}" | sort | uniq
|
||||
else
|
||||
# No completions suggested. In this case, we want tcsh to perform
|
||||
# standard file completion. However, there does not seem to be way
|
||||
# to tell tcsh to do that. To help the user, we try to simulate
|
||||
# file completion directly in this script.
|
||||
#
|
||||
# Known issues:
|
||||
# - Possible completions are shown with their directory prefix.
|
||||
# - Completions containing shell variables are not handled.
|
||||
# - Completions with ~ as the first character are not handled.
|
||||
|
||||
# No file completion should be done unless we are completing beyond
|
||||
# the git sub-command. An improvement on the bash completion :)
|
||||
if [ \${COMP_CWORD} -gt 1 ]; then
|
||||
TO_COMPLETE="\${COMP_WORDS[\${COMP_CWORD}]}"
|
||||
|
||||
# We don't support ~ expansion: too tricky.
|
||||
if [ "\${TO_COMPLETE:0:1}" != "~" ]; then
|
||||
# Use ls so as to add the '/' at the end of directories.
|
||||
RESULT=(\`ls -dp \${TO_COMPLETE}* 2> /dev/null\`)
|
||||
echo \${RESULT[*]}
|
||||
|
||||
# If there is a single completion and it is a directory,
|
||||
# we output it a second time to trick tcsh into not adding a space
|
||||
# after it.
|
||||
if [ \${#RESULT[*]} -eq 1 ] && [ "\${RESULT[0]: -1}" == "/" ]; then
|
||||
echo \${RESULT[*]}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
EOF
|
||||
|
||||
# Don't need this variable anymore, so don't pollute the users environment
|
||||
unset __git_tcsh_completion_original_script
|
||||
|
||||
complete git 'p,*,`bash ${__git_tcsh_completion_script} git "${COMMAND_LINE}"`,'
|
||||
complete gitk 'p,*,`bash ${__git_tcsh_completion_script} gitk "${COMMAND_LINE}"`,'
|
@ -1,78 +0,0 @@
|
||||
#compdef git gitk
|
||||
|
||||
# zsh completion wrapper for git
|
||||
#
|
||||
# You need git's bash completion script installed somewhere, by default on the
|
||||
# same directory as this script.
|
||||
#
|
||||
# If your script is on ~/.git-completion.sh instead, you can configure it on
|
||||
# your ~/.zshrc:
|
||||
#
|
||||
# zstyle ':completion:*:*:git:*' script ~/.git-completion.sh
|
||||
#
|
||||
# The recommended way to install this script is to copy to
|
||||
# '~/.zsh/completion/_git', and then add the following to your ~/.zshrc file:
|
||||
#
|
||||
# fpath=(~/.zsh/completion $fpath)
|
||||
|
||||
complete ()
|
||||
{
|
||||
# do nothing
|
||||
return 0
|
||||
}
|
||||
|
||||
zstyle -s ":completion:*:*:git:*" script script
|
||||
test -z "$script" && script="$(dirname ${funcsourcetrace[1]%:*})"/git-completion.bash
|
||||
ZSH_VERSION='' . "$script"
|
||||
|
||||
__gitcomp ()
|
||||
{
|
||||
emulate -L zsh
|
||||
|
||||
local cur_="${3-$cur}"
|
||||
|
||||
case "$cur_" in
|
||||
--*=)
|
||||
;;
|
||||
*)
|
||||
local c IFS=$' \t\n'
|
||||
local -a array
|
||||
for c in ${=1}; do
|
||||
c="$c${4-}"
|
||||
case $c in
|
||||
--*=*|*.) ;;
|
||||
*) c="$c " ;;
|
||||
esac
|
||||
array+=("$c")
|
||||
done
|
||||
compset -P '*[=:]'
|
||||
compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
__gitcomp_nl ()
|
||||
{
|
||||
emulate -L zsh
|
||||
|
||||
local IFS=$'\n'
|
||||
compset -P '*[=:]'
|
||||
compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
|
||||
}
|
||||
|
||||
_git ()
|
||||
{
|
||||
local _ret=1
|
||||
() {
|
||||
emulate -L ksh
|
||||
local cur cword prev
|
||||
cur=${words[CURRENT-1]}
|
||||
prev=${words[CURRENT-2]}
|
||||
let cword=CURRENT-1
|
||||
__${service}_main
|
||||
}
|
||||
let _ret && _default -S '' && _ret=0
|
||||
return _ret
|
||||
}
|
||||
|
||||
_git
|
@ -10,20 +10,9 @@
|
||||
# 1) Copy this file to somewhere (e.g. ~/.git-prompt.sh).
|
||||
# 2) Add the following line to your .bashrc/.zshrc:
|
||||
# source ~/.git-prompt.sh
|
||||
# 3a) Change your PS1 to call __git_ps1 as
|
||||
# command-substitution:
|
||||
# Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
|
||||
# ZSH: PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
|
||||
# the optional argument will be used as format string.
|
||||
# 3b) Alternatively, if you are using bash, __git_ps1 can be
|
||||
# used for PROMPT_COMMAND with two parameters, <pre> and
|
||||
# <post>, which are strings you would put in $PS1 before
|
||||
# and after the status string generated by the git-prompt
|
||||
# machinery. e.g.
|
||||
# PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
|
||||
# will show username, at-sign, host, colon, cwd, then
|
||||
# various status string, followed by dollar and SP, as
|
||||
# your prompt.
|
||||
# 3) Change your PS1 to also show the current branch:
|
||||
# Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
|
||||
# ZSH: PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
|
||||
#
|
||||
# The argument to __git_ps1 will be displayed only if you are currently
|
||||
# in a git repository. The %s token will be the name of the current
|
||||
@ -69,10 +58,6 @@
|
||||
# branch relative to newer tag or branch (master~4)
|
||||
# describe relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
|
||||
# default exactly matching tag
|
||||
#
|
||||
# If you would like a colored hint about the current dirty state, set
|
||||
# GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
|
||||
# the colored output of "git status -sb".
|
||||
|
||||
# __gitdir accepts 0 or 1 arguments (i.e., location)
|
||||
# returns location of .git repo
|
||||
@ -219,40 +204,11 @@ __git_ps1_show_upstream ()
|
||||
|
||||
|
||||
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
|
||||
# when called from PS1 using command substitution
|
||||
# in this mode it prints text to add to bash PS1 prompt (includes branch name)
|
||||
#
|
||||
# __git_ps1 requires 2 arguments when called from PROMPT_COMMAND (pc)
|
||||
# in that case it _sets_ PS1. The arguments are parts of a PS1 string.
|
||||
# when both arguments are given, the first is prepended and the second appended
|
||||
# to the state string when assigned to PS1.
|
||||
# In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true
|
||||
# returns text to add to bash PS1 prompt (includes branch name)
|
||||
__git_ps1 ()
|
||||
{
|
||||
local pcmode=no
|
||||
local detached=no
|
||||
local ps1pc_start='\u@\h:\w '
|
||||
local ps1pc_end='\$ '
|
||||
local printf_format=' (%s)'
|
||||
|
||||
case "$#" in
|
||||
2) pcmode=yes
|
||||
ps1pc_start="$1"
|
||||
ps1pc_end="$2"
|
||||
;;
|
||||
0|1) printf_format="${1:-$printf_format}"
|
||||
;;
|
||||
*) return
|
||||
;;
|
||||
esac
|
||||
|
||||
local g="$(__gitdir)"
|
||||
if [ -z "$g" ]; then
|
||||
if [ $pcmode = yes ]; then
|
||||
#In PC mode PS1 always needs to be set
|
||||
PS1="$ps1pc_start$ps1pc_end"
|
||||
fi
|
||||
else
|
||||
if [ -n "$g" ]; then
|
||||
local r=""
|
||||
local b=""
|
||||
if [ -f "$g/rebase-merge/interactive" ]; then
|
||||
@ -279,7 +235,7 @@ __git_ps1 ()
|
||||
fi
|
||||
|
||||
b="$(git symbolic-ref HEAD 2>/dev/null)" || {
|
||||
detached=yes
|
||||
|
||||
b="$(
|
||||
case "${GIT_PS1_DESCRIBE_STYLE-}" in
|
||||
(contains)
|
||||
@ -338,50 +294,6 @@ __git_ps1 ()
|
||||
fi
|
||||
|
||||
local f="$w$i$s$u"
|
||||
if [ $pcmode = yes ]; then
|
||||
if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
|
||||
local c_red='\e[31m'
|
||||
local c_green='\e[32m'
|
||||
local c_lblue='\e[1;34m'
|
||||
local c_clear='\e[0m'
|
||||
local bad_color=$c_red
|
||||
local ok_color=$c_green
|
||||
local branch_color="$c_clear"
|
||||
local flags_color="$c_lblue"
|
||||
local branchstring="$c${b##refs/heads/}"
|
||||
|
||||
if [ $detached = no ]; then
|
||||
branch_color="$ok_color"
|
||||
else
|
||||
branch_color="$bad_color"
|
||||
fi
|
||||
|
||||
# Setting PS1 directly with \[ and \] around colors
|
||||
# is necessary to prevent wrapping issues!
|
||||
PS1="$ps1pc_start (\[$branch_color\]$branchstring\[$c_clear\]"
|
||||
|
||||
if [ -n "$w$i$s$u$r$p" ]; then
|
||||
PS1="$PS1 "
|
||||
fi
|
||||
if [ "$w" = "*" ]; then
|
||||
PS1="$PS1\[$bad_color\]$w"
|
||||
fi
|
||||
if [ -n "$i" ]; then
|
||||
PS1="$PS1\[$ok_color\]$i"
|
||||
fi
|
||||
if [ -n "$s" ]; then
|
||||
PS1="$PS1\[$flags_color\]$s"
|
||||
fi
|
||||
if [ -n "$u" ]; then
|
||||
PS1="$PS1\[$bad_color\]$u"
|
||||
fi
|
||||
PS1="$PS1\[$c_clear\]$r$p)$ps1pc_end"
|
||||
else
|
||||
PS1="$ps1pc_start ($c${b##refs/heads/}${f:+ $f}$r$p)$ps1pc_end"
|
||||
fi
|
||||
else
|
||||
# NO color option unless in PROMPT_COMMAND mode
|
||||
printf -- "$printf_format" "$c${b##refs/heads/}${f:+ $f}$r$p"
|
||||
fi
|
||||
printf -- "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
|
||||
fi
|
||||
}
|
||||
|
@ -1671,7 +1671,7 @@ Commands:
|
||||
"Entry point into git-status mode."
|
||||
(interactive "DSelect directory: ")
|
||||
(setq dir (git-get-top-dir dir))
|
||||
(if (file-exists-p (concat (file-name-as-directory dir) ".git"))
|
||||
(if (file-directory-p (concat (file-name-as-directory dir) ".git"))
|
||||
(let ((buffer (or (and git-reuse-status-buffer (git-find-status-buffer dir))
|
||||
(create-file-buffer (expand-file-name "*git-status*" dir)))))
|
||||
(switch-to-buffer buffer)
|
||||
|
@ -1,13 +0,0 @@
|
||||
TESTS := $(wildcard test*.sh)
|
||||
|
||||
export T := $(addprefix $(CURDIR)/,$(TESTS))
|
||||
export MAKE := $(MAKE) -e
|
||||
export PATH := $(CURDIR):$(PATH)
|
||||
|
||||
test:
|
||||
$(MAKE) -C ../../t $@
|
||||
|
||||
$(TESTS):
|
||||
$(MAKE) -C ../../t $(CURDIR)/$@
|
||||
|
||||
.PHONY: $(TESTS)
|
@ -1,794 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2012 Felipe Contreras
|
||||
#
|
||||
|
||||
# Inspired by Rocco Rutte's hg-fast-export
|
||||
|
||||
# Just copy to your ~/bin, or anywhere in your $PATH.
|
||||
# Then you can clone with:
|
||||
# git clone hg::/path/to/mercurial/repo/
|
||||
|
||||
from mercurial import hg, ui, bookmarks, context, util, encoding
|
||||
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import urllib
|
||||
|
||||
#
|
||||
# If you want to switch to hg-git compatibility mode:
|
||||
# git config --global remote-hg.hg-git-compat true
|
||||
#
|
||||
# git:
|
||||
# Sensible defaults for git.
|
||||
# hg bookmarks are exported as git branches, hg branches are prefixed
|
||||
# with 'branches/', HEAD is a special case.
|
||||
#
|
||||
# hg:
|
||||
# Emulate hg-git.
|
||||
# Only hg bookmarks are exported as git branches.
|
||||
# Commits are modified to preserve hg information and allow biridectionality.
|
||||
#
|
||||
|
||||
NAME_RE = re.compile('^([^<>]+)')
|
||||
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
|
||||
AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
|
||||
RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
|
||||
|
||||
def die(msg, *args):
|
||||
sys.stderr.write('ERROR: %s\n' % (msg % args))
|
||||
sys.exit(1)
|
||||
|
||||
def warn(msg, *args):
|
||||
sys.stderr.write('WARNING: %s\n' % (msg % args))
|
||||
|
||||
def gitmode(flags):
|
||||
return 'l' in flags and '120000' or 'x' in flags and '100755' or '100644'
|
||||
|
||||
def gittz(tz):
|
||||
return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)
|
||||
|
||||
def hgmode(mode):
|
||||
m = { '0100755': 'x', '0120000': 'l' }
|
||||
return m.get(mode, '')
|
||||
|
||||
def get_config(config):
|
||||
cmd = ['git', 'config', '--get', config]
|
||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
output, _ = process.communicate()
|
||||
return output
|
||||
|
||||
class Marks:
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.tips = {}
|
||||
self.marks = {}
|
||||
self.rev_marks = {}
|
||||
self.last_mark = 0
|
||||
|
||||
self.load()
|
||||
|
||||
def load(self):
|
||||
if not os.path.exists(self.path):
|
||||
return
|
||||
|
||||
tmp = json.load(open(self.path))
|
||||
|
||||
self.tips = tmp['tips']
|
||||
self.marks = tmp['marks']
|
||||
self.last_mark = tmp['last-mark']
|
||||
|
||||
for rev, mark in self.marks.iteritems():
|
||||
self.rev_marks[mark] = int(rev)
|
||||
|
||||
def dict(self):
|
||||
return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
|
||||
|
||||
def store(self):
|
||||
json.dump(self.dict(), open(self.path, 'w'))
|
||||
|
||||
def __str__(self):
|
||||
return str(self.dict())
|
||||
|
||||
def from_rev(self, rev):
|
||||
return self.marks[str(rev)]
|
||||
|
||||
def to_rev(self, mark):
|
||||
return self.rev_marks[mark]
|
||||
|
||||
def get_mark(self, rev):
|
||||
self.last_mark += 1
|
||||
self.marks[str(rev)] = self.last_mark
|
||||
return self.last_mark
|
||||
|
||||
def new_mark(self, rev, mark):
|
||||
self.marks[str(rev)] = mark
|
||||
self.rev_marks[mark] = rev
|
||||
self.last_mark = mark
|
||||
|
||||
def is_marked(self, rev):
|
||||
return self.marks.has_key(str(rev))
|
||||
|
||||
def get_tip(self, branch):
|
||||
return self.tips.get(branch, 0)
|
||||
|
||||
def set_tip(self, branch, tip):
|
||||
self.tips[branch] = tip
|
||||
|
||||
class Parser:
|
||||
|
||||
def __init__(self, repo):
|
||||
self.repo = repo
|
||||
self.line = self.get_line()
|
||||
|
||||
def get_line(self):
|
||||
return sys.stdin.readline().strip()
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.line.split()[i]
|
||||
|
||||
def check(self, word):
|
||||
return self.line.startswith(word)
|
||||
|
||||
def each_block(self, separator):
|
||||
while self.line != separator:
|
||||
yield self.line
|
||||
self.line = self.get_line()
|
||||
|
||||
def __iter__(self):
|
||||
return self.each_block('')
|
||||
|
||||
def next(self):
|
||||
self.line = self.get_line()
|
||||
if self.line == 'done':
|
||||
self.line = None
|
||||
|
||||
def get_mark(self):
|
||||
i = self.line.index(':') + 1
|
||||
return int(self.line[i:])
|
||||
|
||||
def get_data(self):
|
||||
if not self.check('data'):
|
||||
return None
|
||||
i = self.line.index(' ') + 1
|
||||
size = int(self.line[i:])
|
||||
return sys.stdin.read(size)
|
||||
|
||||
def get_author(self):
|
||||
global bad_mail
|
||||
|
||||
ex = None
|
||||
m = RAW_AUTHOR_RE.match(self.line)
|
||||
if not m:
|
||||
return None
|
||||
_, name, email, date, tz = m.groups()
|
||||
if name and 'ext:' in name:
|
||||
m = re.match('^(.+?) ext:\((.+)\)$', name)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
ex = urllib.unquote(m.group(2))
|
||||
|
||||
if email != bad_mail:
|
||||
if name:
|
||||
user = '%s <%s>' % (name, email)
|
||||
else:
|
||||
user = '<%s>' % (email)
|
||||
else:
|
||||
user = name
|
||||
|
||||
if ex:
|
||||
user += ex
|
||||
|
||||
tz = int(tz)
|
||||
tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
|
||||
return (user, int(date), -tz)
|
||||
|
||||
def export_file(fc):
|
||||
d = fc.data()
|
||||
print "M %s inline %s" % (gitmode(fc.flags()), fc.path())
|
||||
print "data %d" % len(d)
|
||||
print d
|
||||
|
||||
def get_filechanges(repo, ctx, parent):
|
||||
modified = set()
|
||||
added = set()
|
||||
removed = set()
|
||||
|
||||
cur = ctx.manifest()
|
||||
prev = repo[parent].manifest().copy()
|
||||
|
||||
for fn in cur:
|
||||
if fn in prev:
|
||||
if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):
|
||||
modified.add(fn)
|
||||
del prev[fn]
|
||||
else:
|
||||
added.add(fn)
|
||||
removed |= set(prev.keys())
|
||||
|
||||
return added | modified, removed
|
||||
|
||||
def fixup_user_git(user):
|
||||
name = mail = None
|
||||
user = user.replace('"', '')
|
||||
m = AUTHOR_RE.match(user)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
mail = m.group(2).strip()
|
||||
else:
|
||||
m = NAME_RE.match(user)
|
||||
if m:
|
||||
name = m.group(1).strip()
|
||||
return (name, mail)
|
||||
|
||||
def fixup_user_hg(user):
|
||||
def sanitize(name):
|
||||
# stole this from hg-git
|
||||
return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
|
||||
|
||||
m = AUTHOR_HG_RE.match(user)
|
||||
if m:
|
||||
name = sanitize(m.group(1))
|
||||
mail = sanitize(m.group(2))
|
||||
ex = m.group(3)
|
||||
if ex:
|
||||
name += ' ext:(' + urllib.quote(ex) + ')'
|
||||
else:
|
||||
name = sanitize(user)
|
||||
if '@' in user:
|
||||
mail = name
|
||||
else:
|
||||
mail = None
|
||||
|
||||
return (name, mail)
|
||||
|
||||
def fixup_user(user):
|
||||
global mode, bad_mail
|
||||
|
||||
if mode == 'git':
|
||||
name, mail = fixup_user_git(user)
|
||||
else:
|
||||
name, mail = fixup_user_hg(user)
|
||||
|
||||
if not name:
|
||||
name = bad_name
|
||||
if not mail:
|
||||
mail = bad_mail
|
||||
|
||||
return '%s <%s>' % (name, mail)
|
||||
|
||||
def get_repo(url, alias):
|
||||
global dirname, peer
|
||||
|
||||
myui = ui.ui()
|
||||
myui.setconfig('ui', 'interactive', 'off')
|
||||
|
||||
if hg.islocal(url):
|
||||
repo = hg.repository(myui, url)
|
||||
else:
|
||||
local_path = os.path.join(dirname, 'clone')
|
||||
if not os.path.exists(local_path):
|
||||
peer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True)
|
||||
repo = dstpeer.local()
|
||||
else:
|
||||
repo = hg.repository(myui, local_path)
|
||||
peer = hg.peer(myui, {}, url)
|
||||
repo.pull(peer, heads=None, force=True)
|
||||
|
||||
return repo
|
||||
|
||||
def rev_to_mark(rev):
|
||||
global marks
|
||||
return marks.from_rev(rev)
|
||||
|
||||
def mark_to_rev(mark):
|
||||
global marks
|
||||
return marks.to_rev(mark)
|
||||
|
||||
def export_ref(repo, name, kind, head):
|
||||
global prefix, marks, mode
|
||||
|
||||
ename = '%s/%s' % (kind, name)
|
||||
tip = marks.get_tip(ename)
|
||||
|
||||
# mercurial takes too much time checking this
|
||||
if tip and tip == head.rev():
|
||||
# nothing to do
|
||||
return
|
||||
revs = xrange(tip, head.rev() + 1)
|
||||
count = 0
|
||||
|
||||
revs = [rev for rev in revs if not marks.is_marked(rev)]
|
||||
|
||||
for rev in revs:
|
||||
|
||||
c = repo[rev]
|
||||
(manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
|
||||
rev_branch = extra['branch']
|
||||
|
||||
author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
|
||||
if 'committer' in extra:
|
||||
user, time, tz = extra['committer'].rsplit(' ', 2)
|
||||
committer = "%s %s %s" % (user, time, gittz(int(tz)))
|
||||
else:
|
||||
committer = author
|
||||
|
||||
parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
|
||||
|
||||
if len(parents) == 0:
|
||||
modified = c.manifest().keys()
|
||||
removed = []
|
||||
else:
|
||||
modified, removed = get_filechanges(repo, c, parents[0])
|
||||
|
||||
if mode == 'hg':
|
||||
extra_msg = ''
|
||||
|
||||
if rev_branch != 'default':
|
||||
extra_msg += 'branch : %s\n' % rev_branch
|
||||
|
||||
renames = []
|
||||
for f in c.files():
|
||||
if f not in c.manifest():
|
||||
continue
|
||||
rename = c.filectx(f).renamed()
|
||||
if rename:
|
||||
renames.append((rename[0], f))
|
||||
|
||||
for e in renames:
|
||||
extra_msg += "rename : %s => %s\n" % e
|
||||
|
||||
for key, value in extra.iteritems():
|
||||
if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
|
||||
continue
|
||||
else:
|
||||
extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
|
||||
|
||||
desc += '\n'
|
||||
if extra_msg:
|
||||
desc += '\n--HG--\n' + extra_msg
|
||||
|
||||
if len(parents) == 0 and rev:
|
||||
print 'reset %s/%s' % (prefix, ename)
|
||||
|
||||
print "commit %s/%s" % (prefix, ename)
|
||||
print "mark :%d" % (marks.get_mark(rev))
|
||||
print "author %s" % (author)
|
||||
print "committer %s" % (committer)
|
||||
print "data %d" % (len(desc))
|
||||
print desc
|
||||
|
||||
if len(parents) > 0:
|
||||
print "from :%s" % (rev_to_mark(parents[0]))
|
||||
if len(parents) > 1:
|
||||
print "merge :%s" % (rev_to_mark(parents[1]))
|
||||
|
||||
for f in modified:
|
||||
export_file(c.filectx(f))
|
||||
for f in removed:
|
||||
print "D %s" % (f)
|
||||
print
|
||||
|
||||
count += 1
|
||||
if (count % 100 == 0):
|
||||
print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
|
||||
print "#############################################################"
|
||||
|
||||
# make sure the ref is updated
|
||||
print "reset %s/%s" % (prefix, ename)
|
||||
print "from :%u" % rev_to_mark(rev)
|
||||
print
|
||||
|
||||
marks.set_tip(ename, rev)
|
||||
|
||||
def export_tag(repo, tag):
|
||||
export_ref(repo, tag, 'tags', repo[tag])
|
||||
|
||||
def export_bookmark(repo, bmark):
|
||||
head = bmarks[bmark]
|
||||
export_ref(repo, bmark, 'bookmarks', head)
|
||||
|
||||
def export_branch(repo, branch):
|
||||
tip = get_branch_tip(repo, branch)
|
||||
head = repo[tip]
|
||||
export_ref(repo, branch, 'branches', head)
|
||||
|
||||
def export_head(repo):
|
||||
global g_head
|
||||
export_ref(repo, g_head[0], 'bookmarks', g_head[1])
|
||||
|
||||
def do_capabilities(parser):
|
||||
global prefix, dirname
|
||||
|
||||
print "import"
|
||||
print "export"
|
||||
print "refspec refs/heads/branches/*:%s/branches/*" % prefix
|
||||
print "refspec refs/heads/*:%s/bookmarks/*" % prefix
|
||||
print "refspec refs/tags/*:%s/tags/*" % prefix
|
||||
|
||||
path = os.path.join(dirname, 'marks-git')
|
||||
|
||||
if os.path.exists(path):
|
||||
print "*import-marks %s" % path
|
||||
print "*export-marks %s" % path
|
||||
|
||||
print
|
||||
|
||||
def get_branch_tip(repo, branch):
|
||||
global branches
|
||||
|
||||
heads = branches.get(branch, None)
|
||||
if not heads:
|
||||
return None
|
||||
|
||||
# verify there's only one head
|
||||
if (len(heads) > 1):
|
||||
warn("Branch '%s' has more than one head, consider merging" % branch)
|
||||
# older versions of mercurial don't have this
|
||||
if hasattr(repo, "branchtip"):
|
||||
return repo.branchtip(branch)
|
||||
|
||||
return heads[0]
|
||||
|
||||
def list_head(repo, cur):
|
||||
global g_head, bmarks
|
||||
|
||||
head = bookmarks.readcurrent(repo)
|
||||
if head:
|
||||
node = repo[head]
|
||||
else:
|
||||
# fake bookmark from current branch
|
||||
head = cur
|
||||
node = repo['.']
|
||||
if not node:
|
||||
node = repo['tip']
|
||||
if not node:
|
||||
return
|
||||
if head == 'default':
|
||||
head = 'master'
|
||||
bmarks[head] = node
|
||||
|
||||
print "@refs/heads/%s HEAD" % head
|
||||
g_head = (head, node)
|
||||
|
||||
def do_list(parser):
|
||||
global branches, bmarks, mode, track_branches
|
||||
|
||||
repo = parser.repo
|
||||
for bmark, node in bookmarks.listbookmarks(repo).iteritems():
|
||||
bmarks[bmark] = repo[node]
|
||||
|
||||
cur = repo.dirstate.branch()
|
||||
|
||||
list_head(repo, cur)
|
||||
|
||||
if track_branches:
|
||||
for branch in repo.branchmap():
|
||||
heads = repo.branchheads(branch)
|
||||
if len(heads):
|
||||
branches[branch] = heads
|
||||
|
||||
for branch in branches:
|
||||
print "? refs/heads/branches/%s" % branch
|
||||
|
||||
for bmark in bmarks:
|
||||
print "? refs/heads/%s" % bmark
|
||||
|
||||
for tag, node in repo.tagslist():
|
||||
if tag == 'tip':
|
||||
continue
|
||||
print "? refs/tags/%s" % tag
|
||||
|
||||
print
|
||||
|
||||
def do_import(parser):
|
||||
repo = parser.repo
|
||||
|
||||
path = os.path.join(dirname, 'marks-git')
|
||||
|
||||
print "feature done"
|
||||
if os.path.exists(path):
|
||||
print "feature import-marks=%s" % path
|
||||
print "feature export-marks=%s" % path
|
||||
sys.stdout.flush()
|
||||
|
||||
tmp = encoding.encoding
|
||||
encoding.encoding = 'utf-8'
|
||||
|
||||
# lets get all the import lines
|
||||
while parser.check('import'):
|
||||
ref = parser[1]
|
||||
|
||||
if (ref == 'HEAD'):
|
||||
export_head(repo)
|
||||
elif ref.startswith('refs/heads/branches/'):
|
||||
branch = ref[len('refs/heads/branches/'):]
|
||||
export_branch(repo, branch)
|
||||
elif ref.startswith('refs/heads/'):
|
||||
bmark = ref[len('refs/heads/'):]
|
||||
export_bookmark(repo, bmark)
|
||||
elif ref.startswith('refs/tags/'):
|
||||
tag = ref[len('refs/tags/'):]
|
||||
export_tag(repo, tag)
|
||||
|
||||
parser.next()
|
||||
|
||||
encoding.encoding = tmp
|
||||
|
||||
print 'done'
|
||||
|
||||
def parse_blob(parser):
|
||||
global blob_marks
|
||||
|
||||
parser.next()
|
||||
mark = parser.get_mark()
|
||||
parser.next()
|
||||
data = parser.get_data()
|
||||
blob_marks[mark] = data
|
||||
parser.next()
|
||||
return
|
||||
|
||||
def get_merge_files(repo, p1, p2, files):
|
||||
for e in repo[p1].files():
|
||||
if e not in files:
|
||||
if e not in repo[p1].manifest():
|
||||
continue
|
||||
f = { 'ctx' : repo[p1][e] }
|
||||
files[e] = f
|
||||
|
||||
def parse_commit(parser):
|
||||
global marks, blob_marks, bmarks, parsed_refs
|
||||
global mode
|
||||
|
||||
from_mark = merge_mark = None
|
||||
|
||||
ref = parser[1]
|
||||
parser.next()
|
||||
|
||||
commit_mark = parser.get_mark()
|
||||
parser.next()
|
||||
author = parser.get_author()
|
||||
parser.next()
|
||||
committer = parser.get_author()
|
||||
parser.next()
|
||||
data = parser.get_data()
|
||||
parser.next()
|
||||
if parser.check('from'):
|
||||
from_mark = parser.get_mark()
|
||||
parser.next()
|
||||
if parser.check('merge'):
|
||||
merge_mark = parser.get_mark()
|
||||
parser.next()
|
||||
if parser.check('merge'):
|
||||
die('octopus merges are not supported yet')
|
||||
|
||||
files = {}
|
||||
|
||||
for line in parser:
|
||||
if parser.check('M'):
|
||||
t, m, mark_ref, path = line.split(' ', 3)
|
||||
mark = int(mark_ref[1:])
|
||||
f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
|
||||
elif parser.check('D'):
|
||||
t, path = line.split(' ')
|
||||
f = { 'deleted' : True }
|
||||
else:
|
||||
die('Unknown file command: %s' % line)
|
||||
files[path] = f
|
||||
|
||||
def getfilectx(repo, memctx, f):
|
||||
of = files[f]
|
||||
if 'deleted' in of:
|
||||
raise IOError
|
||||
if 'ctx' in of:
|
||||
return of['ctx']
|
||||
is_exec = of['mode'] == 'x'
|
||||
is_link = of['mode'] == 'l'
|
||||
rename = of.get('rename', None)
|
||||
return context.memfilectx(f, of['data'],
|
||||
is_link, is_exec, rename)
|
||||
|
||||
repo = parser.repo
|
||||
|
||||
user, date, tz = author
|
||||
extra = {}
|
||||
|
||||
if committer != author:
|
||||
extra['committer'] = "%s %u %u" % committer
|
||||
|
||||
if from_mark:
|
||||
p1 = repo.changelog.node(mark_to_rev(from_mark))
|
||||
else:
|
||||
p1 = '\0' * 20
|
||||
|
||||
if merge_mark:
|
||||
p2 = repo.changelog.node(mark_to_rev(merge_mark))
|
||||
else:
|
||||
p2 = '\0' * 20
|
||||
|
||||
#
|
||||
# If files changed from any of the parents, hg wants to know, but in git if
|
||||
# nothing changed from the first parent, nothing changed.
|
||||
#
|
||||
if merge_mark:
|
||||
get_merge_files(repo, p1, p2, files)
|
||||
|
||||
if mode == 'hg':
|
||||
i = data.find('\n--HG--\n')
|
||||
if i >= 0:
|
||||
tmp = data[i + len('\n--HG--\n'):].strip()
|
||||
for k, v in [e.split(' : ') for e in tmp.split('\n')]:
|
||||
if k == 'rename':
|
||||
old, new = v.split(' => ', 1)
|
||||
files[new]['rename'] = old
|
||||
elif k == 'branch':
|
||||
extra[k] = v
|
||||
elif k == 'extra':
|
||||
ek, ev = v.split(' : ', 1)
|
||||
extra[ek] = urllib.unquote(ev)
|
||||
data = data[:i]
|
||||
|
||||
ctx = context.memctx(repo, (p1, p2), data,
|
||||
files.keys(), getfilectx,
|
||||
user, (date, tz), extra)
|
||||
|
||||
tmp = encoding.encoding
|
||||
encoding.encoding = 'utf-8'
|
||||
|
||||
node = repo.commitctx(ctx)
|
||||
|
||||
encoding.encoding = tmp
|
||||
|
||||
rev = repo[node].rev()
|
||||
|
||||
parsed_refs[ref] = node
|
||||
|
||||
marks.new_mark(rev, commit_mark)
|
||||
|
||||
def parse_reset(parser):
|
||||
ref = parser[1]
|
||||
parser.next()
|
||||
# ugh
|
||||
if parser.check('commit'):
|
||||
parse_commit(parser)
|
||||
return
|
||||
if not parser.check('from'):
|
||||
return
|
||||
from_mark = parser.get_mark()
|
||||
parser.next()
|
||||
|
||||
node = parser.repo.changelog.node(mark_to_rev(from_mark))
|
||||
parsed_refs[ref] = node
|
||||
|
||||
def parse_tag(parser):
|
||||
name = parser[1]
|
||||
parser.next()
|
||||
from_mark = parser.get_mark()
|
||||
parser.next()
|
||||
tagger = parser.get_author()
|
||||
parser.next()
|
||||
data = parser.get_data()
|
||||
parser.next()
|
||||
|
||||
# nothing to do
|
||||
|
||||
def do_export(parser):
|
||||
global parsed_refs, bmarks, peer
|
||||
|
||||
parser.next()
|
||||
|
||||
for line in parser.each_block('done'):
|
||||
if parser.check('blob'):
|
||||
parse_blob(parser)
|
||||
elif parser.check('commit'):
|
||||
parse_commit(parser)
|
||||
elif parser.check('reset'):
|
||||
parse_reset(parser)
|
||||
elif parser.check('tag'):
|
||||
parse_tag(parser)
|
||||
elif parser.check('feature'):
|
||||
pass
|
||||
else:
|
||||
die('unhandled export command: %s' % line)
|
||||
|
||||
for ref, node in parsed_refs.iteritems():
|
||||
if ref.startswith('refs/heads/branches'):
|
||||
pass
|
||||
elif ref.startswith('refs/heads/'):
|
||||
bmark = ref[len('refs/heads/'):]
|
||||
if bmark in bmarks:
|
||||
old = bmarks[bmark].hex()
|
||||
else:
|
||||
old = ''
|
||||
if not bookmarks.pushbookmark(parser.repo, bmark, old, node):
|
||||
continue
|
||||
elif ref.startswith('refs/tags/'):
|
||||
tag = ref[len('refs/tags/'):]
|
||||
parser.repo.tag([tag], node, None, True, None, {})
|
||||
else:
|
||||
# transport-helper/fast-export bugs
|
||||
continue
|
||||
print "ok %s" % ref
|
||||
|
||||
print
|
||||
|
||||
if peer:
|
||||
parser.repo.push(peer, force=False)
|
||||
|
||||
def main(args):
|
||||
global prefix, dirname, branches, bmarks
|
||||
global marks, blob_marks, parsed_refs
|
||||
global peer, mode, bad_mail, bad_name
|
||||
global track_branches
|
||||
|
||||
alias = args[1]
|
||||
url = args[2]
|
||||
peer = None
|
||||
|
||||
hg_git_compat = False
|
||||
track_branches = True
|
||||
try:
|
||||
if get_config('remote-hg.hg-git-compat') == 'true\n':
|
||||
hg_git_compat = True
|
||||
track_branches = False
|
||||
if get_config('remote-hg.track-branches') == 'false\n':
|
||||
track_branches = False
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
if hg_git_compat:
|
||||
mode = 'hg'
|
||||
bad_mail = 'none@none'
|
||||
bad_name = ''
|
||||
else:
|
||||
mode = 'git'
|
||||
bad_mail = 'unknown'
|
||||
bad_name = 'Unknown'
|
||||
|
||||
if alias[4:] == url:
|
||||
is_tmp = True
|
||||
alias = util.sha1(alias).hexdigest()
|
||||
else:
|
||||
is_tmp = False
|
||||
|
||||
gitdir = os.environ['GIT_DIR']
|
||||
dirname = os.path.join(gitdir, 'hg', alias)
|
||||
branches = {}
|
||||
bmarks = {}
|
||||
blob_marks = {}
|
||||
parsed_refs = {}
|
||||
|
||||
repo = get_repo(url, alias)
|
||||
prefix = 'refs/hg/%s' % alias
|
||||
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
||||
marks_path = os.path.join(dirname, 'marks-hg')
|
||||
marks = Marks(marks_path)
|
||||
|
||||
parser = Parser(repo)
|
||||
for line in parser:
|
||||
if parser.check('capabilities'):
|
||||
do_capabilities(parser)
|
||||
elif parser.check('list'):
|
||||
do_list(parser)
|
||||
elif parser.check('import'):
|
||||
do_import(parser)
|
||||
elif parser.check('export'):
|
||||
do_export(parser)
|
||||
else:
|
||||
die('unhandled command: %s' % line)
|
||||
sys.stdout.flush()
|
||||
|
||||
if not is_tmp:
|
||||
marks.store()
|
||||
else:
|
||||
shutil.rmtree(dirname)
|
||||
|
||||
sys.exit(main(sys.argv))
|
@ -1,243 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2012 Felipe Contreras
|
||||
#
|
||||
# Base commands from hg-git tests:
|
||||
# https://bitbucket.org/durin42/hg-git/src
|
||||
#
|
||||
|
||||
test_description='Test biridectionality of remote-hg'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PYTHON; then
|
||||
skip_all='skipping remote-hg tests; python not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
if ! "$PYTHON_PATH" -c 'import mercurial'; then
|
||||
skip_all='skipping remote-hg tests; mercurial not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
# clone to a git repo
|
||||
git_clone () {
|
||||
hg -R $1 bookmark -f -r tip master &&
|
||||
git clone -q "hg::$PWD/$1" $2
|
||||
}
|
||||
|
||||
# clone to an hg repo
|
||||
hg_clone () {
|
||||
(
|
||||
hg init $2 &&
|
||||
cd $1 &&
|
||||
git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
|
||||
) &&
|
||||
|
||||
(cd $2 && hg -q update)
|
||||
}
|
||||
|
||||
# push an hg repo
|
||||
hg_push () {
|
||||
(
|
||||
cd $2
|
||||
old=$(git symbolic-ref --short HEAD)
|
||||
git checkout -q -b tmp &&
|
||||
git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
|
||||
git checkout -q $old &&
|
||||
git branch -q -D tmp 2> /dev/null || true
|
||||
)
|
||||
}
|
||||
|
||||
hg_log () {
|
||||
hg -R $1 log --graph --debug | grep -v 'tag: *default/'
|
||||
}
|
||||
|
||||
setup () {
|
||||
(
|
||||
echo "[ui]"
|
||||
echo "username = A U Thor <author@example.com>"
|
||||
echo "[defaults]"
|
||||
echo "backout = -d \"0 0\""
|
||||
echo "commit = -d \"0 0\""
|
||||
echo "debugrawcommit = -d \"0 0\""
|
||||
echo "tag = -d \"0 0\""
|
||||
) >> "$HOME"/.hgrc &&
|
||||
git config --global remote-hg.hg-git-compat true
|
||||
|
||||
export HGEDITOR=/usr/bin/true
|
||||
|
||||
export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
|
||||
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
||||
}
|
||||
|
||||
setup
|
||||
|
||||
test_expect_success 'encoding' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add älphà" &&
|
||||
|
||||
export GIT_AUTHOR_NAME="tést èncödîng" &&
|
||||
echo beta > beta &&
|
||||
git add beta &&
|
||||
git commit -m "add beta" &&
|
||||
|
||||
echo gamma > gamma &&
|
||||
git add gamma &&
|
||||
git commit -m "add gämmâ" &&
|
||||
|
||||
: TODO git config i18n.commitencoding latin-1 &&
|
||||
echo delta > delta &&
|
||||
git add delta &&
|
||||
git commit -m "add déltà"
|
||||
) &&
|
||||
|
||||
hg_clone gitrepo hgrepo &&
|
||||
git_clone hgrepo gitrepo2 &&
|
||||
hg_clone gitrepo2 hgrepo2 &&
|
||||
|
||||
HGENCODING=utf-8 hg_log hgrepo > expected &&
|
||||
HGENCODING=utf-8 hg_log hgrepo2 > actual &&
|
||||
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'file removal' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add alpha" &&
|
||||
echo beta > beta &&
|
||||
git add beta &&
|
||||
git commit -m "add beta"
|
||||
mkdir foo &&
|
||||
echo blah > foo/bar &&
|
||||
git add foo &&
|
||||
git commit -m "add foo" &&
|
||||
git rm alpha &&
|
||||
git commit -m "remove alpha" &&
|
||||
git rm foo/bar &&
|
||||
git commit -m "remove foo/bar"
|
||||
) &&
|
||||
|
||||
hg_clone gitrepo hgrepo &&
|
||||
git_clone hgrepo gitrepo2 &&
|
||||
hg_clone gitrepo2 hgrepo2 &&
|
||||
|
||||
hg_log hgrepo > expected &&
|
||||
hg_log hgrepo2 > actual &&
|
||||
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'git tags' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
git config receive.denyCurrentBranch ignore &&
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add alpha" &&
|
||||
git tag alpha &&
|
||||
|
||||
echo beta > beta &&
|
||||
git add beta &&
|
||||
git commit -m "add beta" &&
|
||||
git tag -a -m "added tag beta" beta
|
||||
) &&
|
||||
|
||||
hg_clone gitrepo hgrepo &&
|
||||
git_clone hgrepo gitrepo2 &&
|
||||
hg_clone gitrepo2 hgrepo2 &&
|
||||
|
||||
hg_log hgrepo > expected &&
|
||||
hg_log hgrepo2 > actual &&
|
||||
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'hg branch' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -q -m "add alpha" &&
|
||||
git checkout -q -b not-master
|
||||
) &&
|
||||
|
||||
(
|
||||
hg_clone gitrepo hgrepo &&
|
||||
|
||||
cd hgrepo &&
|
||||
hg -q co master &&
|
||||
hg mv alpha beta &&
|
||||
hg -q commit -m "rename alpha to beta" &&
|
||||
hg branch gamma | grep -v "permanent and global" &&
|
||||
hg -q commit -m "started branch gamma"
|
||||
) &&
|
||||
|
||||
hg_push hgrepo gitrepo &&
|
||||
hg_clone gitrepo hgrepo2 &&
|
||||
|
||||
: TODO, avoid "master" bookmark &&
|
||||
(cd hgrepo2 && hg checkout gamma) &&
|
||||
|
||||
hg_log hgrepo > expected &&
|
||||
hg_log hgrepo2 > actual &&
|
||||
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'hg tags' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add alpha" &&
|
||||
git checkout -q -b not-master
|
||||
) &&
|
||||
|
||||
(
|
||||
hg_clone gitrepo hgrepo &&
|
||||
|
||||
cd hgrepo &&
|
||||
hg co master &&
|
||||
hg tag alpha
|
||||
) &&
|
||||
|
||||
hg_push hgrepo gitrepo &&
|
||||
hg_clone gitrepo hgrepo2 &&
|
||||
|
||||
hg_log hgrepo > expected &&
|
||||
hg_log hgrepo2 > actual &&
|
||||
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
@ -1,466 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2012 Felipe Contreras
|
||||
#
|
||||
# Base commands from hg-git tests:
|
||||
# https://bitbucket.org/durin42/hg-git/src
|
||||
#
|
||||
|
||||
test_description='Test remote-hg output compared to hg-git'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PYTHON; then
|
||||
skip_all='skipping remote-hg tests; python not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
if ! "$PYTHON_PATH" -c 'import mercurial'; then
|
||||
skip_all='skipping remote-hg tests; mercurial not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
if ! "$PYTHON_PATH" -c 'import hggit'; then
|
||||
skip_all='skipping remote-hg tests; hg-git not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
# clone to a git repo with git
|
||||
git_clone_git () {
|
||||
hg -R $1 bookmark -f -r tip master &&
|
||||
git clone -q "hg::$PWD/$1" $2
|
||||
}
|
||||
|
||||
# clone to an hg repo with git
|
||||
hg_clone_git () {
|
||||
(
|
||||
hg init $2 &&
|
||||
cd $1 &&
|
||||
git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
|
||||
) &&
|
||||
|
||||
(cd $2 && hg -q update)
|
||||
}
|
||||
|
||||
# clone to a git repo with hg
|
||||
git_clone_hg () {
|
||||
(
|
||||
git init -q $2 &&
|
||||
cd $1 &&
|
||||
hg bookmark -f -r tip master &&
|
||||
hg -q push -r master ../$2 || true
|
||||
)
|
||||
}
|
||||
|
||||
# clone to an hg repo with hg
|
||||
hg_clone_hg () {
|
||||
hg -q clone $1 $2
|
||||
}
|
||||
|
||||
# push an hg repo with git
|
||||
hg_push_git () {
|
||||
(
|
||||
cd $2
|
||||
old=$(git symbolic-ref --short HEAD)
|
||||
git checkout -q -b tmp &&
|
||||
git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
|
||||
git checkout -q $old &&
|
||||
git branch -q -D tmp 2> /dev/null || true
|
||||
)
|
||||
}
|
||||
|
||||
# push an hg git repo with hg
|
||||
hg_push_hg () {
|
||||
(
|
||||
cd $1 &&
|
||||
hg -q push ../$2 || true
|
||||
)
|
||||
}
|
||||
|
||||
hg_log () {
|
||||
hg -R $1 log --graph --debug | grep -v 'tag: *default/'
|
||||
}
|
||||
|
||||
git_log () {
|
||||
git --git-dir=$1/.git fast-export --branches
|
||||
}
|
||||
|
||||
setup () {
|
||||
(
|
||||
echo "[ui]"
|
||||
echo "username = A U Thor <author@example.com>"
|
||||
echo "[defaults]"
|
||||
echo "backout = -d \"0 0\""
|
||||
echo "commit = -d \"0 0\""
|
||||
echo "debugrawcommit = -d \"0 0\""
|
||||
echo "tag = -d \"0 0\""
|
||||
echo "[extensions]"
|
||||
echo "hgext.bookmarks ="
|
||||
echo "hggit ="
|
||||
) >> "$HOME"/.hgrc &&
|
||||
git config --global receive.denycurrentbranch warn
|
||||
git config --global remote-hg.hg-git-compat true
|
||||
|
||||
export HGEDITOR=/usr/bin/true
|
||||
|
||||
export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
|
||||
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
||||
}
|
||||
|
||||
setup
|
||||
|
||||
test_expect_success 'merge conflict 1' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
hg init hgrepo1 &&
|
||||
cd hgrepo1 &&
|
||||
echo A > afile &&
|
||||
hg add afile &&
|
||||
hg ci -m "origin" &&
|
||||
|
||||
echo B > afile &&
|
||||
hg ci -m "A->B" &&
|
||||
|
||||
hg up -r0 &&
|
||||
echo C > afile &&
|
||||
hg ci -m "A->C" &&
|
||||
|
||||
hg merge -r1 || true &&
|
||||
echo C > afile &&
|
||||
hg resolve -m afile &&
|
||||
hg ci -m "merge to C"
|
||||
) &&
|
||||
|
||||
for x in hg git; do
|
||||
git_clone_$x hgrepo1 gitrepo-$x &&
|
||||
hg_clone_$x gitrepo-$x hgrepo2-$x &&
|
||||
hg_log hgrepo2-$x > hg-log-$x &&
|
||||
git_log gitrepo-$x > git-log-$x
|
||||
done &&
|
||||
|
||||
test_cmp hg-log-hg hg-log-git &&
|
||||
test_cmp git-log-hg git-log-git
|
||||
'
|
||||
|
||||
test_expect_success 'merge conflict 2' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
hg init hgrepo1 &&
|
||||
cd hgrepo1 &&
|
||||
echo A > afile &&
|
||||
hg add afile &&
|
||||
hg ci -m "origin" &&
|
||||
|
||||
echo B > afile &&
|
||||
hg ci -m "A->B" &&
|
||||
|
||||
hg up -r0 &&
|
||||
echo C > afile &&
|
||||
hg ci -m "A->C" &&
|
||||
|
||||
hg merge -r1 || true &&
|
||||
echo B > afile &&
|
||||
hg resolve -m afile &&
|
||||
hg ci -m "merge to B"
|
||||
) &&
|
||||
|
||||
for x in hg git; do
|
||||
git_clone_$x hgrepo1 gitrepo-$x &&
|
||||
hg_clone_$x gitrepo-$x hgrepo2-$x &&
|
||||
hg_log hgrepo2-$x > hg-log-$x &&
|
||||
git_log gitrepo-$x > git-log-$x
|
||||
done &&
|
||||
|
||||
test_cmp hg-log-hg hg-log-git &&
|
||||
test_cmp git-log-hg git-log-git
|
||||
'
|
||||
|
||||
test_expect_success 'converged merge' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
hg init hgrepo1 &&
|
||||
cd hgrepo1 &&
|
||||
echo A > afile &&
|
||||
hg add afile &&
|
||||
hg ci -m "origin" &&
|
||||
|
||||
echo B > afile &&
|
||||
hg ci -m "A->B" &&
|
||||
|
||||
echo C > afile &&
|
||||
hg ci -m "B->C" &&
|
||||
|
||||
hg up -r0 &&
|
||||
echo C > afile &&
|
||||
hg ci -m "A->C" &&
|
||||
|
||||
hg merge -r2 || true &&
|
||||
hg ci -m "merge"
|
||||
) &&
|
||||
|
||||
for x in hg git; do
|
||||
git_clone_$x hgrepo1 gitrepo-$x &&
|
||||
hg_clone_$x gitrepo-$x hgrepo2-$x &&
|
||||
hg_log hgrepo2-$x > hg-log-$x &&
|
||||
git_log gitrepo-$x > git-log-$x
|
||||
done &&
|
||||
|
||||
test_cmp hg-log-hg hg-log-git &&
|
||||
test_cmp git-log-hg git-log-git
|
||||
'
|
||||
|
||||
test_expect_success 'encoding' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add älphà" &&
|
||||
|
||||
export GIT_AUTHOR_NAME="tést èncödîng" &&
|
||||
echo beta > beta &&
|
||||
git add beta &&
|
||||
git commit -m "add beta" &&
|
||||
|
||||
echo gamma > gamma &&
|
||||
git add gamma &&
|
||||
git commit -m "add gämmâ" &&
|
||||
|
||||
: TODO git config i18n.commitencoding latin-1 &&
|
||||
echo delta > delta &&
|
||||
git add delta &&
|
||||
git commit -m "add déltà"
|
||||
) &&
|
||||
|
||||
for x in hg git; do
|
||||
hg_clone_$x gitrepo hgrepo-$x &&
|
||||
git_clone_$x hgrepo-$x gitrepo2-$x &&
|
||||
|
||||
HGENCODING=utf-8 hg_log hgrepo-$x > hg-log-$x &&
|
||||
git_log gitrepo2-$x > git-log-$x
|
||||
done &&
|
||||
|
||||
test_cmp hg-log-hg hg-log-git &&
|
||||
test_cmp git-log-hg git-log-git
|
||||
'
|
||||
|
||||
test_expect_success 'file removal' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add alpha" &&
|
||||
echo beta > beta &&
|
||||
git add beta &&
|
||||
git commit -m "add beta"
|
||||
mkdir foo &&
|
||||
echo blah > foo/bar &&
|
||||
git add foo &&
|
||||
git commit -m "add foo" &&
|
||||
git rm alpha &&
|
||||
git commit -m "remove alpha" &&
|
||||
git rm foo/bar &&
|
||||
git commit -m "remove foo/bar"
|
||||
) &&
|
||||
|
||||
for x in hg git; do
|
||||
(
|
||||
hg_clone_$x gitrepo hgrepo-$x &&
|
||||
cd hgrepo-$x &&
|
||||
hg_log . &&
|
||||
hg manifest -r 3 &&
|
||||
hg manifest
|
||||
) > output-$x &&
|
||||
|
||||
git_clone_$x hgrepo-$x gitrepo2-$x &&
|
||||
git_log gitrepo2-$x > log-$x
|
||||
done &&
|
||||
|
||||
test_cmp output-hg output-git &&
|
||||
test_cmp log-hg log-git
|
||||
'
|
||||
|
||||
test_expect_success 'git tags' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
git init -q gitrepo &&
|
||||
cd gitrepo &&
|
||||
git config receive.denyCurrentBranch ignore &&
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add alpha" &&
|
||||
git tag alpha &&
|
||||
|
||||
echo beta > beta &&
|
||||
git add beta &&
|
||||
git commit -m "add beta" &&
|
||||
git tag -a -m "added tag beta" beta
|
||||
) &&
|
||||
|
||||
for x in hg git; do
|
||||
hg_clone_$x gitrepo hgrepo-$x &&
|
||||
hg_log hgrepo-$x > log-$x
|
||||
done &&
|
||||
|
||||
test_cmp log-hg log-git
|
||||
'
|
||||
|
||||
test_expect_success 'hg author' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
for x in hg git; do
|
||||
(
|
||||
git init -q gitrepo-$x &&
|
||||
cd gitrepo-$x &&
|
||||
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add alpha" &&
|
||||
git checkout -q -b not-master
|
||||
) &&
|
||||
|
||||
(
|
||||
hg_clone_$x gitrepo-$x hgrepo-$x &&
|
||||
cd hgrepo-$x &&
|
||||
|
||||
hg co master &&
|
||||
echo beta > beta &&
|
||||
hg add beta &&
|
||||
hg commit -u "test" -m "add beta" &&
|
||||
|
||||
echo gamma >> beta &&
|
||||
hg commit -u "test <test@example.com> (comment)" -m "modify beta" &&
|
||||
|
||||
echo gamma > gamma &&
|
||||
hg add gamma &&
|
||||
hg commit -u "<test@example.com>" -m "add gamma" &&
|
||||
|
||||
echo delta > delta &&
|
||||
hg add delta &&
|
||||
hg commit -u "name<test@example.com>" -m "add delta" &&
|
||||
|
||||
echo epsilon > epsilon &&
|
||||
hg add epsilon &&
|
||||
hg commit -u "name <test@example.com" -m "add epsilon" &&
|
||||
|
||||
echo zeta > zeta &&
|
||||
hg add zeta &&
|
||||
hg commit -u " test " -m "add zeta" &&
|
||||
|
||||
echo eta > eta &&
|
||||
hg add eta &&
|
||||
hg commit -u "test < test@example.com >" -m "add eta" &&
|
||||
|
||||
echo theta > theta &&
|
||||
hg add theta &&
|
||||
hg commit -u "test >test@example.com>" -m "add theta" &&
|
||||
|
||||
echo iota > iota &&
|
||||
hg add iota &&
|
||||
hg commit -u "test <test <at> example <dot> com>" -m "add iota"
|
||||
) &&
|
||||
|
||||
hg_push_$x hgrepo-$x gitrepo-$x &&
|
||||
hg_clone_$x gitrepo-$x hgrepo2-$x &&
|
||||
|
||||
hg_log hgrepo2-$x > hg-log-$x &&
|
||||
git_log gitrepo-$x > git-log-$x
|
||||
done &&
|
||||
|
||||
test_cmp git-log-hg git-log-git &&
|
||||
|
||||
test_cmp hg-log-hg hg-log-git &&
|
||||
test_cmp git-log-hg git-log-git
|
||||
'
|
||||
|
||||
test_expect_success 'hg branch' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
for x in hg git; do
|
||||
(
|
||||
git init -q gitrepo-$x &&
|
||||
cd gitrepo-$x &&
|
||||
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -q -m "add alpha" &&
|
||||
git checkout -q -b not-master
|
||||
) &&
|
||||
|
||||
(
|
||||
hg_clone_$x gitrepo-$x hgrepo-$x &&
|
||||
|
||||
cd hgrepo-$x &&
|
||||
hg -q co master &&
|
||||
hg mv alpha beta &&
|
||||
hg -q commit -m "rename alpha to beta" &&
|
||||
hg branch gamma | grep -v "permanent and global" &&
|
||||
hg -q commit -m "started branch gamma"
|
||||
) &&
|
||||
|
||||
hg_push_$x hgrepo-$x gitrepo-$x &&
|
||||
hg_clone_$x gitrepo-$x hgrepo2-$x &&
|
||||
|
||||
hg_log hgrepo2-$x > hg-log-$x &&
|
||||
git_log gitrepo-$x > git-log-$x
|
||||
done &&
|
||||
|
||||
test_cmp hg-log-hg hg-log-git &&
|
||||
test_cmp git-log-hg git-log-git
|
||||
'
|
||||
|
||||
test_expect_success 'hg tags' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
for x in hg git; do
|
||||
(
|
||||
git init -q gitrepo-$x &&
|
||||
cd gitrepo-$x &&
|
||||
|
||||
echo alpha > alpha &&
|
||||
git add alpha &&
|
||||
git commit -m "add alpha" &&
|
||||
git checkout -q -b not-master
|
||||
) &&
|
||||
|
||||
(
|
||||
hg_clone_$x gitrepo-$x hgrepo-$x &&
|
||||
|
||||
cd hgrepo-$x &&
|
||||
hg co master &&
|
||||
hg tag alpha
|
||||
) &&
|
||||
|
||||
hg_push_$x hgrepo-$x gitrepo-$x &&
|
||||
hg_clone_$x gitrepo-$x hgrepo2-$x &&
|
||||
|
||||
(
|
||||
git --git-dir=gitrepo-$x/.git tag -l &&
|
||||
hg_log hgrepo2-$x &&
|
||||
cat hgrepo2-$x/.hgtags
|
||||
) > output-$x
|
||||
done &&
|
||||
|
||||
test_cmp output-hg output-git
|
||||
'
|
||||
|
||||
test_done
|
@ -1,121 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2012 Felipe Contreras
|
||||
#
|
||||
# Base commands from hg-git tests:
|
||||
# https://bitbucket.org/durin42/hg-git/src
|
||||
#
|
||||
|
||||
test_description='Test remote-hg'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq PYTHON; then
|
||||
skip_all='skipping remote-hg tests; python not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
if ! "$PYTHON_PATH" -c 'import mercurial'; then
|
||||
skip_all='skipping remote-hg tests; mercurial not available'
|
||||
test_done
|
||||
fi
|
||||
|
||||
check () {
|
||||
(cd $1 &&
|
||||
git log --format='%s' -1 &&
|
||||
git symbolic-ref HEAD) > actual &&
|
||||
(echo $2 &&
|
||||
echo "refs/heads/$3") > expected &&
|
||||
test_cmp expected actual
|
||||
}
|
||||
|
||||
setup () {
|
||||
(
|
||||
echo "[ui]"
|
||||
echo "username = H G Wells <wells@example.com>"
|
||||
) >> "$HOME"/.hgrc
|
||||
}
|
||||
|
||||
setup
|
||||
|
||||
test_expect_success 'cloning' '
|
||||
test_when_finished "rm -rf gitrepo*" &&
|
||||
|
||||
(
|
||||
hg init hgrepo &&
|
||||
cd hgrepo &&
|
||||
echo zero > content &&
|
||||
hg add content &&
|
||||
hg commit -m zero
|
||||
) &&
|
||||
|
||||
git clone "hg::$PWD/hgrepo" gitrepo &&
|
||||
check gitrepo zero master
|
||||
'
|
||||
|
||||
test_expect_success 'cloning with branches' '
|
||||
test_when_finished "rm -rf gitrepo*" &&
|
||||
|
||||
(
|
||||
cd hgrepo &&
|
||||
hg branch next &&
|
||||
echo next > content &&
|
||||
hg commit -m next
|
||||
) &&
|
||||
|
||||
git clone "hg::$PWD/hgrepo" gitrepo &&
|
||||
check gitrepo next next &&
|
||||
|
||||
(cd hgrepo && hg checkout default) &&
|
||||
|
||||
git clone "hg::$PWD/hgrepo" gitrepo2 &&
|
||||
check gitrepo2 zero master
|
||||
'
|
||||
|
||||
test_expect_success 'cloning with bookmarks' '
|
||||
test_when_finished "rm -rf gitrepo*" &&
|
||||
|
||||
(
|
||||
cd hgrepo &&
|
||||
hg bookmark feature-a &&
|
||||
echo feature-a > content &&
|
||||
hg commit -m feature-a
|
||||
) &&
|
||||
|
||||
git clone "hg::$PWD/hgrepo" gitrepo &&
|
||||
check gitrepo feature-a feature-a
|
||||
'
|
||||
|
||||
test_expect_success 'cloning with detached head' '
|
||||
test_when_finished "rm -rf gitrepo*" &&
|
||||
|
||||
(
|
||||
cd hgrepo &&
|
||||
hg update -r 0
|
||||
) &&
|
||||
|
||||
git clone "hg::$PWD/hgrepo" gitrepo &&
|
||||
check gitrepo zero master
|
||||
'
|
||||
|
||||
test_expect_success 'update bookmark' '
|
||||
test_when_finished "rm -rf gitrepo*" &&
|
||||
|
||||
(
|
||||
cd hgrepo &&
|
||||
hg bookmark devel
|
||||
) &&
|
||||
|
||||
(
|
||||
git clone "hg::$PWD/hgrepo" gitrepo &&
|
||||
cd gitrepo &&
|
||||
git checkout devel &&
|
||||
echo devel > content &&
|
||||
git commit -a -m devel &&
|
||||
git push
|
||||
) &&
|
||||
|
||||
hg -R hgrepo bookmarks | grep "devel\s\+3:"
|
||||
'
|
||||
|
||||
test_done
|
@ -10,8 +10,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
if (svndump_init(NULL))
|
||||
return 1;
|
||||
svndump_read((argc > 1) ? argv[1] : NULL, "refs/heads/master",
|
||||
"refs/notes/svn/revs");
|
||||
svndump_read((argc > 1) ? argv[1] : NULL);
|
||||
svndump_deinit();
|
||||
svndump_reset();
|
||||
return 0;
|
||||
|
@ -1,53 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
Simulates svnrdump by replaying an existing dump from a file, taking care
|
||||
of the specified revision range.
|
||||
To simulate incremental imports the environment variable SVNRMAX can be set
|
||||
to the highest revision that should be available.
|
||||
"""
|
||||
import sys, os
|
||||
|
||||
|
||||
def getrevlimit():
|
||||
var = 'SVNRMAX'
|
||||
if os.environ.has_key(var):
|
||||
return os.environ[var]
|
||||
return None
|
||||
|
||||
def writedump(url, lower, upper):
|
||||
if url.startswith('sim://'):
|
||||
filename = url[6:]
|
||||
if filename[-1] == '/': filename = filename[:-1] #remove terminating slash
|
||||
else:
|
||||
raise ValueError('sim:// url required')
|
||||
f = open(filename, 'r');
|
||||
state = 'header'
|
||||
wroterev = False
|
||||
while(True):
|
||||
l = f.readline()
|
||||
if l == '': break
|
||||
if state == 'header' and l.startswith('Revision-number: '):
|
||||
state = 'prefix'
|
||||
if state == 'prefix' and l == 'Revision-number: %s\n' % lower:
|
||||
state = 'selection'
|
||||
if not upper == 'HEAD' and state == 'selection' and l == 'Revision-number: %s\n' % upper:
|
||||
break;
|
||||
|
||||
if state == 'header' or state == 'selection':
|
||||
if state == 'selection': wroterev = True
|
||||
sys.stdout.write(l)
|
||||
return wroterev
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not (len(sys.argv) in (3, 4, 5)):
|
||||
print "usage: %s dump URL -rLOWER:UPPER"
|
||||
sys.exit(1)
|
||||
if not sys.argv[1] == 'dump': raise NotImplementedError('only "dump" is suppported.')
|
||||
url = sys.argv[2]
|
||||
r = ('0', 'HEAD')
|
||||
if len(sys.argv) == 4 and sys.argv[3][0:2] == '-r':
|
||||
r = sys.argv[3][2:].lstrip().split(':')
|
||||
if not getrevlimit() is None: r[1] = getrevlimit()
|
||||
if writedump(url, r[0], r[1]): ret = 0
|
||||
else: ret = 1
|
||||
sys.exit(ret)
|
113
diff.c
113
diff.c
@ -15,7 +15,6 @@
|
||||
#include "sigchain.h"
|
||||
#include "submodule.h"
|
||||
#include "ll-merge.h"
|
||||
#include "string-list.h"
|
||||
|
||||
#ifdef NO_FAST_WORKING_DIRECTORY
|
||||
#define FAST_WORKING_DIRECTORY 0
|
||||
@ -27,7 +26,6 @@ static int diff_detect_rename_default;
|
||||
static int diff_rename_limit_default = 400;
|
||||
static int diff_suppress_blank_empty;
|
||||
static int diff_use_color_default = -1;
|
||||
static int diff_context_default = 3;
|
||||
static const char *diff_word_regex_cfg;
|
||||
static const char *external_diff_cmd_cfg;
|
||||
int diff_auto_refresh_index = 1;
|
||||
@ -70,30 +68,26 @@ static int parse_diff_color_slot(const char *var, int ofs)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_dirstat_params(struct diff_options *options, const char *params_string,
|
||||
static int parse_dirstat_params(struct diff_options *options, const char *params,
|
||||
struct strbuf *errmsg)
|
||||
{
|
||||
char *params_copy = xstrdup(params_string);
|
||||
struct string_list params = STRING_LIST_INIT_NODUP;
|
||||
int ret = 0;
|
||||
int i;
|
||||
const char *p = params;
|
||||
int p_len, ret = 0;
|
||||
|
||||
if (*params_copy)
|
||||
string_list_split_in_place(¶ms, params_copy, ',', -1);
|
||||
for (i = 0; i < params.nr; i++) {
|
||||
const char *p = params.items[i].string;
|
||||
if (!strcmp(p, "changes")) {
|
||||
while (*p) {
|
||||
p_len = strchrnul(p, ',') - p;
|
||||
if (!memcmp(p, "changes", p_len)) {
|
||||
DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
|
||||
DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
|
||||
} else if (!strcmp(p, "lines")) {
|
||||
} else if (!memcmp(p, "lines", p_len)) {
|
||||
DIFF_OPT_SET(options, DIRSTAT_BY_LINE);
|
||||
DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
|
||||
} else if (!strcmp(p, "files")) {
|
||||
} else if (!memcmp(p, "files", p_len)) {
|
||||
DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
|
||||
DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
|
||||
} else if (!strcmp(p, "noncumulative")) {
|
||||
} else if (!memcmp(p, "noncumulative", p_len)) {
|
||||
DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
|
||||
} else if (!strcmp(p, "cumulative")) {
|
||||
} else if (!memcmp(p, "cumulative", p_len)) {
|
||||
DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
|
||||
} else if (isdigit(*p)) {
|
||||
char *end;
|
||||
@ -105,33 +99,25 @@ static int parse_dirstat_params(struct diff_options *options, const char *params
|
||||
while (isdigit(*++end))
|
||||
; /* nothing */
|
||||
}
|
||||
if (!*end)
|
||||
if (end - p == p_len)
|
||||
options->dirstat_permille = permille;
|
||||
else {
|
||||
strbuf_addf(errmsg, _(" Failed to parse dirstat cut-off percentage '%s'\n"),
|
||||
p);
|
||||
strbuf_addf(errmsg, _(" Failed to parse dirstat cut-off percentage '%.*s'\n"),
|
||||
p_len, p);
|
||||
ret++;
|
||||
}
|
||||
} else {
|
||||
strbuf_addf(errmsg, _(" Unknown dirstat parameter '%s'\n"), p);
|
||||
strbuf_addf(errmsg, _(" Unknown dirstat parameter '%.*s'\n"),
|
||||
p_len, p);
|
||||
ret++;
|
||||
}
|
||||
|
||||
}
|
||||
string_list_clear(¶ms, 0);
|
||||
free(params_copy);
|
||||
return ret;
|
||||
}
|
||||
p += p_len;
|
||||
|
||||
static int parse_submodule_params(struct diff_options *options, const char *value)
|
||||
{
|
||||
if (!strcmp(value, "log"))
|
||||
DIFF_OPT_SET(options, SUBMODULE_LOG);
|
||||
else if (!strcmp(value, "short"))
|
||||
DIFF_OPT_CLR(options, SUBMODULE_LOG);
|
||||
else
|
||||
return -1;
|
||||
return 0;
|
||||
if (*p)
|
||||
p++; /* more parameters, swallow separator */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int git_config_rename(const char *var, const char *value)
|
||||
@ -155,12 +141,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
|
||||
diff_use_color_default = git_config_colorbool(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "diff.context")) {
|
||||
diff_context_default = git_config_int(var, value);
|
||||
if (diff_context_default < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "diff.renames")) {
|
||||
diff_detect_rename_default = git_config_rename(var, value);
|
||||
return 0;
|
||||
@ -189,13 +169,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
|
||||
if (!strcmp(var, "diff.ignoresubmodules"))
|
||||
handle_ignore_submodules_arg(&default_diff_options, value);
|
||||
|
||||
if (!strcmp(var, "diff.submodule")) {
|
||||
if (parse_submodule_params(&default_diff_options, value))
|
||||
warning(_("Unknown value for 'diff.submodule' config variable: '%s'"),
|
||||
value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (git_color_config(var, value, cb) < 0)
|
||||
return -1;
|
||||
|
||||
@ -2245,7 +2218,7 @@ static void builtin_diff(const char *name_a,
|
||||
mmfile_t mf1, mf2;
|
||||
const char *lbl[2];
|
||||
char *a_one, *b_two;
|
||||
const char *meta = diff_get_color_opt(o, DIFF_METAINFO);
|
||||
const char *set = diff_get_color_opt(o, DIFF_METAINFO);
|
||||
const char *reset = diff_get_color_opt(o, DIFF_RESET);
|
||||
const char *a_prefix, *b_prefix;
|
||||
struct userdiff_driver *textconv_one = NULL;
|
||||
@ -2266,7 +2239,7 @@ static void builtin_diff(const char *name_a,
|
||||
const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
|
||||
show_submodule_summary(o->file, one ? one->path : two->path,
|
||||
one->sha1, two->sha1, two->dirty_submodule,
|
||||
meta, del, add, reset);
|
||||
del, add, reset);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2292,24 +2265,24 @@ static void builtin_diff(const char *name_a,
|
||||
b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
|
||||
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
|
||||
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
|
||||
strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset);
|
||||
strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, set, a_one, b_two, reset);
|
||||
if (lbl[0][0] == '/') {
|
||||
/* /dev/null */
|
||||
strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset);
|
||||
strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, set, two->mode, reset);
|
||||
if (xfrm_msg)
|
||||
strbuf_addstr(&header, xfrm_msg);
|
||||
must_show_header = 1;
|
||||
}
|
||||
else if (lbl[1][0] == '/') {
|
||||
strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset);
|
||||
strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, set, one->mode, reset);
|
||||
if (xfrm_msg)
|
||||
strbuf_addstr(&header, xfrm_msg);
|
||||
must_show_header = 1;
|
||||
}
|
||||
else {
|
||||
if (one->mode != two->mode) {
|
||||
strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset);
|
||||
strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset);
|
||||
strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, set, one->mode, reset);
|
||||
strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, set, two->mode, reset);
|
||||
must_show_header = 1;
|
||||
}
|
||||
if (xfrm_msg)
|
||||
@ -3206,7 +3179,7 @@ void diff_setup(struct diff_options *options)
|
||||
options->break_opt = -1;
|
||||
options->rename_limit = -1;
|
||||
options->dirstat_permille = diff_dirstat_permille_default;
|
||||
options->context = diff_context_default;
|
||||
options->context = 3;
|
||||
DIFF_OPT_SET(options, RENAME_EMPTY);
|
||||
|
||||
options->change = diff_change;
|
||||
@ -3502,14 +3475,6 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_submodule_opt(struct diff_options *options, const char *value)
|
||||
{
|
||||
if (parse_submodule_params(options, value))
|
||||
die(_("Failed to parse --submodule option parameter: '%s'"),
|
||||
value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
||||
{
|
||||
const char *arg = av[0];
|
||||
@ -3690,8 +3655,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
||||
handle_ignore_submodules_arg(options, arg + 20);
|
||||
} else if (!strcmp(arg, "--submodule"))
|
||||
DIFF_OPT_SET(options, SUBMODULE_LOG);
|
||||
else if (!prefixcmp(arg, "--submodule="))
|
||||
return parse_submodule_opt(options, arg + 12);
|
||||
else if (!prefixcmp(arg, "--submodule=")) {
|
||||
if (!strcmp(arg + 12, "log"))
|
||||
DIFF_OPT_SET(options, SUBMODULE_LOG);
|
||||
}
|
||||
|
||||
/* misc options */
|
||||
else if (!strcmp(arg, "-z"))
|
||||
@ -4913,19 +4880,3 @@ size_t fill_textconv(struct userdiff_driver *driver,
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void setup_diff_pager(struct diff_options *opt)
|
||||
{
|
||||
/*
|
||||
* If the user asked for our exit code, then either they want --quiet
|
||||
* or --exit-code. We should definitely not bother with a pager in the
|
||||
* former case, as we will generate no output. Since we still properly
|
||||
* report our exit code even when a pager is run, we _could_ run a
|
||||
* pager with --exit-code. But since we have not done so historically,
|
||||
* and because it is easy to find people oneline advising "git diff
|
||||
* --exit-code" in hooks and other scripts, we do not do so.
|
||||
*/
|
||||
if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
|
||||
check_pager_config("diff") != 0)
|
||||
setup_pager();
|
||||
}
|
||||
|
1
diff.h
1
diff.h
@ -335,6 +335,5 @@ extern int parse_rename_score(const char **cp_p);
|
||||
|
||||
extern int print_stat_summary(FILE *fp, int files,
|
||||
int insertions, int deletions);
|
||||
extern void setup_diff_pager(struct diff_options *);
|
||||
|
||||
#endif /* DIFF_H */
|
||||
|
192
dir.c
192
dir.c
@ -308,69 +308,42 @@ static int no_wildcard(const char *string)
|
||||
return string[simple_length(string)] == '\0';
|
||||
}
|
||||
|
||||
void parse_exclude_pattern(const char **pattern,
|
||||
int *patternlen,
|
||||
int *flags,
|
||||
int *nowildcardlen)
|
||||
{
|
||||
const char *p = *pattern;
|
||||
size_t i, len;
|
||||
|
||||
*flags = 0;
|
||||
if (*p == '!') {
|
||||
*flags |= EXC_FLAG_NEGATIVE;
|
||||
p++;
|
||||
}
|
||||
len = strlen(p);
|
||||
if (len && p[len - 1] == '/') {
|
||||
len--;
|
||||
*flags |= EXC_FLAG_MUSTBEDIR;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p[i] == '/')
|
||||
break;
|
||||
}
|
||||
if (i == len)
|
||||
*flags |= EXC_FLAG_NODIR;
|
||||
*nowildcardlen = simple_length(p);
|
||||
/*
|
||||
* we should have excluded the trailing slash from 'p' too,
|
||||
* but that's one more allocation. Instead just make sure
|
||||
* nowildcardlen does not exceed real patternlen
|
||||
*/
|
||||
if (*nowildcardlen > len)
|
||||
*nowildcardlen = len;
|
||||
if (*p == '*' && no_wildcard(p + 1))
|
||||
*flags |= EXC_FLAG_ENDSWITH;
|
||||
*pattern = p;
|
||||
*patternlen = len;
|
||||
}
|
||||
|
||||
void add_exclude(const char *string, const char *base,
|
||||
int baselen, struct exclude_list *which)
|
||||
{
|
||||
struct exclude *x;
|
||||
int patternlen;
|
||||
int flags;
|
||||
int nowildcardlen;
|
||||
size_t len;
|
||||
int to_exclude = 1;
|
||||
int flags = 0;
|
||||
|
||||
parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);
|
||||
if (flags & EXC_FLAG_MUSTBEDIR) {
|
||||
if (*string == '!') {
|
||||
to_exclude = 0;
|
||||
string++;
|
||||
}
|
||||
len = strlen(string);
|
||||
if (len && string[len - 1] == '/') {
|
||||
char *s;
|
||||
x = xmalloc(sizeof(*x) + patternlen + 1);
|
||||
x = xmalloc(sizeof(*x) + len);
|
||||
s = (char *)(x+1);
|
||||
memcpy(s, string, patternlen);
|
||||
s[patternlen] = '\0';
|
||||
memcpy(s, string, len - 1);
|
||||
s[len - 1] = '\0';
|
||||
string = s;
|
||||
x->pattern = s;
|
||||
flags = EXC_FLAG_MUSTBEDIR;
|
||||
} else {
|
||||
x = xmalloc(sizeof(*x));
|
||||
x->pattern = string;
|
||||
}
|
||||
x->patternlen = patternlen;
|
||||
x->nowildcardlen = nowildcardlen;
|
||||
x->to_exclude = to_exclude;
|
||||
x->patternlen = strlen(string);
|
||||
x->base = base;
|
||||
x->baselen = baselen;
|
||||
x->flags = flags;
|
||||
if (!strchr(string, '/'))
|
||||
x->flags |= EXC_FLAG_NODIR;
|
||||
x->nowildcardlen = simple_length(string);
|
||||
if (*string == '*' && no_wildcard(string+1))
|
||||
x->flags |= EXC_FLAG_ENDSWITH;
|
||||
ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
|
||||
which->excludes[which->nr++] = x;
|
||||
}
|
||||
@ -532,72 +505,6 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
||||
dir->basebuf[baselen] = '\0';
|
||||
}
|
||||
|
||||
int match_basename(const char *basename, int basenamelen,
|
||||
const char *pattern, int prefix, int patternlen,
|
||||
int flags)
|
||||
{
|
||||
if (prefix == patternlen) {
|
||||
if (!strcmp_icase(pattern, basename))
|
||||
return 1;
|
||||
} else if (flags & EXC_FLAG_ENDSWITH) {
|
||||
if (patternlen - 1 <= basenamelen &&
|
||||
!strcmp_icase(pattern + 1,
|
||||
basename + basenamelen - patternlen + 1))
|
||||
return 1;
|
||||
} else {
|
||||
if (fnmatch_icase(pattern, basename, 0) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int match_pathname(const char *pathname, int pathlen,
|
||||
const char *base, int baselen,
|
||||
const char *pattern, int prefix, int patternlen,
|
||||
int flags)
|
||||
{
|
||||
const char *name;
|
||||
int namelen;
|
||||
|
||||
/*
|
||||
* match with FNM_PATHNAME; the pattern has base implicitly
|
||||
* in front of it.
|
||||
*/
|
||||
if (*pattern == '/') {
|
||||
pattern++;
|
||||
prefix--;
|
||||
}
|
||||
|
||||
/*
|
||||
* baselen does not count the trailing slash. base[] may or
|
||||
* may not end with a trailing slash though.
|
||||
*/
|
||||
if (pathlen < baselen + 1 ||
|
||||
(baselen && pathname[baselen] != '/') ||
|
||||
strncmp_icase(pathname, base, baselen))
|
||||
return 0;
|
||||
|
||||
namelen = baselen ? pathlen - baselen - 1 : pathlen;
|
||||
name = pathname + pathlen - namelen;
|
||||
|
||||
if (prefix) {
|
||||
/*
|
||||
* if the non-wildcard part is longer than the
|
||||
* remaining pathname, surely it cannot match.
|
||||
*/
|
||||
if (prefix > namelen)
|
||||
return 0;
|
||||
|
||||
if (strncmp_icase(pattern, name, prefix))
|
||||
return 0;
|
||||
pattern += prefix;
|
||||
name += prefix;
|
||||
namelen -= prefix;
|
||||
}
|
||||
|
||||
return fnmatch_icase(pattern, name, FNM_PATHNAME) == 0;
|
||||
}
|
||||
|
||||
/* Scan the list and let the last match determine the fate.
|
||||
* Return 1 for exclude, 0 for include and -1 for undecided.
|
||||
*/
|
||||
@ -612,9 +519,9 @@ int excluded_from_list(const char *pathname,
|
||||
|
||||
for (i = el->nr - 1; 0 <= i; i--) {
|
||||
struct exclude *x = el->excludes[i];
|
||||
const char *exclude = x->pattern;
|
||||
int to_exclude = x->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
|
||||
int prefix = x->nowildcardlen;
|
||||
const char *name, *exclude = x->pattern;
|
||||
int to_exclude = x->to_exclude;
|
||||
int namelen, prefix = x->nowildcardlen;
|
||||
|
||||
if (x->flags & EXC_FLAG_MUSTBEDIR) {
|
||||
if (*dtype == DT_UNKNOWN)
|
||||
@ -624,18 +531,51 @@ int excluded_from_list(const char *pathname,
|
||||
}
|
||||
|
||||
if (x->flags & EXC_FLAG_NODIR) {
|
||||
if (match_basename(basename,
|
||||
pathlen - (basename - pathname),
|
||||
exclude, prefix, x->patternlen,
|
||||
x->flags))
|
||||
return to_exclude;
|
||||
/* match basename */
|
||||
if (prefix == x->patternlen) {
|
||||
if (!strcmp_icase(exclude, basename))
|
||||
return to_exclude;
|
||||
} else if (x->flags & EXC_FLAG_ENDSWITH) {
|
||||
if (x->patternlen - 1 <= pathlen &&
|
||||
!strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
|
||||
return to_exclude;
|
||||
} else {
|
||||
if (fnmatch_icase(exclude, basename, 0) == 0)
|
||||
return to_exclude;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(x->baselen == 0 || x->base[x->baselen - 1] == '/');
|
||||
if (match_pathname(pathname, pathlen,
|
||||
x->base, x->baselen ? x->baselen - 1 : 0,
|
||||
exclude, prefix, x->patternlen, x->flags))
|
||||
/* match with FNM_PATHNAME:
|
||||
* exclude has base (baselen long) implicitly in front of it.
|
||||
*/
|
||||
if (*exclude == '/') {
|
||||
exclude++;
|
||||
prefix--;
|
||||
}
|
||||
|
||||
if (pathlen < x->baselen ||
|
||||
(x->baselen && pathname[x->baselen-1] != '/') ||
|
||||
strncmp_icase(pathname, x->base, x->baselen))
|
||||
continue;
|
||||
|
||||
namelen = x->baselen ? pathlen - x->baselen : pathlen;
|
||||
name = pathname + pathlen - namelen;
|
||||
|
||||
/* if the non-wildcard part is longer than the
|
||||
remaining pathname, surely it cannot match */
|
||||
if (prefix > namelen)
|
||||
continue;
|
||||
|
||||
if (prefix) {
|
||||
if (strncmp_icase(exclude, name, prefix))
|
||||
continue;
|
||||
exclude += prefix;
|
||||
name += prefix;
|
||||
namelen -= prefix;
|
||||
}
|
||||
|
||||
if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
|
||||
return to_exclude;
|
||||
}
|
||||
return -1; /* undecided */
|
||||
|
13
dir.h
13
dir.h
@ -11,7 +11,6 @@ struct dir_entry {
|
||||
#define EXC_FLAG_NODIR 1
|
||||
#define EXC_FLAG_ENDSWITH 4
|
||||
#define EXC_FLAG_MUSTBEDIR 8
|
||||
#define EXC_FLAG_NEGATIVE 16
|
||||
|
||||
struct exclude_list {
|
||||
int nr;
|
||||
@ -22,6 +21,7 @@ struct exclude_list {
|
||||
int nowildcardlen;
|
||||
const char *base;
|
||||
int baselen;
|
||||
int to_exclude;
|
||||
int flags;
|
||||
} **excludes;
|
||||
};
|
||||
@ -80,16 +80,6 @@ extern int excluded_from_list(const char *pathname, int pathlen, const char *bas
|
||||
int *dtype, struct exclude_list *el);
|
||||
struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);
|
||||
|
||||
/*
|
||||
* these implement the matching logic for dir.c:excluded_from_list and
|
||||
* attr.c:path_matches()
|
||||
*/
|
||||
extern int match_basename(const char *, int,
|
||||
const char *, int, int, int);
|
||||
extern int match_pathname(const char *, int,
|
||||
const char *, int,
|
||||
const char *, int, int, int);
|
||||
|
||||
/*
|
||||
* The excluded() API is meant for callers that check each level of leading
|
||||
* directory hierarchies with excluded() to avoid recursing into excluded
|
||||
@ -107,7 +97,6 @@ extern int path_excluded(struct path_exclude_check *, const char *, int namelen,
|
||||
extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
|
||||
char **buf_p, struct exclude_list *which, int check_index);
|
||||
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
|
||||
extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);
|
||||
extern void add_exclude(const char *string, const char *base,
|
||||
int baselen, struct exclude_list *which);
|
||||
extern void free_excludes(struct exclude_list *el);
|
||||
|
951
fetch-pack.c
951
fetch-pack.c
@ -1,951 +0,0 @@
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
#include "commit.h"
|
||||
#include "tag.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "pack.h"
|
||||
#include "sideband.h"
|
||||
#include "fetch-pack.h"
|
||||
#include "remote.h"
|
||||
#include "run-command.h"
|
||||
#include "transport.h"
|
||||
#include "version.h"
|
||||
|
||||
static int transfer_unpack_limit = -1;
|
||||
static int fetch_unpack_limit = -1;
|
||||
static int unpack_limit = 100;
|
||||
static int prefer_ofs_delta = 1;
|
||||
static int no_done;
|
||||
static int fetch_fsck_objects = -1;
|
||||
static int transfer_fsck_objects = -1;
|
||||
static int agent_supported;
|
||||
|
||||
#define COMPLETE (1U << 0)
|
||||
#define COMMON (1U << 1)
|
||||
#define COMMON_REF (1U << 2)
|
||||
#define SEEN (1U << 3)
|
||||
#define POPPED (1U << 4)
|
||||
|
||||
static int marked;
|
||||
|
||||
/*
|
||||
* After sending this many "have"s if we do not get any new ACK , we
|
||||
* give up traversing our history.
|
||||
*/
|
||||
#define MAX_IN_VAIN 256
|
||||
|
||||
static struct commit_list *rev_list;
|
||||
static int non_common_revs, multi_ack, use_sideband;
|
||||
|
||||
static void rev_list_push(struct commit *commit, int mark)
|
||||
{
|
||||
if (!(commit->object.flags & mark)) {
|
||||
commit->object.flags |= mark;
|
||||
|
||||
if (!(commit->object.parsed))
|
||||
if (parse_commit(commit))
|
||||
return;
|
||||
|
||||
commit_list_insert_by_date(commit, &rev_list);
|
||||
|
||||
if (!(commit->object.flags & COMMON))
|
||||
non_common_revs++;
|
||||
}
|
||||
}
|
||||
|
||||
static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct object *o = deref_tag(parse_object(sha1), refname, 0);
|
||||
|
||||
if (o && o->type == OBJ_COMMIT)
|
||||
rev_list_push((struct commit *)o, SEEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct object *o = deref_tag(parse_object(sha1), refname, 0);
|
||||
|
||||
if (o && o->type == OBJ_COMMIT)
|
||||
clear_commit_marks((struct commit *)o,
|
||||
COMMON | COMMON_REF | SEEN | POPPED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
This function marks a rev and its ancestors as common.
|
||||
In some cases, it is desirable to mark only the ancestors (for example
|
||||
when only the server does not yet know that they are common).
|
||||
*/
|
||||
|
||||
static void mark_common(struct commit *commit,
|
||||
int ancestors_only, int dont_parse)
|
||||
{
|
||||
if (commit != NULL && !(commit->object.flags & COMMON)) {
|
||||
struct object *o = (struct object *)commit;
|
||||
|
||||
if (!ancestors_only)
|
||||
o->flags |= COMMON;
|
||||
|
||||
if (!(o->flags & SEEN))
|
||||
rev_list_push(commit, SEEN);
|
||||
else {
|
||||
struct commit_list *parents;
|
||||
|
||||
if (!ancestors_only && !(o->flags & POPPED))
|
||||
non_common_revs--;
|
||||
if (!o->parsed && !dont_parse)
|
||||
if (parse_commit(commit))
|
||||
return;
|
||||
|
||||
for (parents = commit->parents;
|
||||
parents;
|
||||
parents = parents->next)
|
||||
mark_common(parents->item, 0, dont_parse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Get the next rev to send, ignoring the common.
|
||||
*/
|
||||
|
||||
static const unsigned char *get_rev(void)
|
||||
{
|
||||
struct commit *commit = NULL;
|
||||
|
||||
while (commit == NULL) {
|
||||
unsigned int mark;
|
||||
struct commit_list *parents;
|
||||
|
||||
if (rev_list == NULL || non_common_revs == 0)
|
||||
return NULL;
|
||||
|
||||
commit = rev_list->item;
|
||||
if (!commit->object.parsed)
|
||||
parse_commit(commit);
|
||||
parents = commit->parents;
|
||||
|
||||
commit->object.flags |= POPPED;
|
||||
if (!(commit->object.flags & COMMON))
|
||||
non_common_revs--;
|
||||
|
||||
if (commit->object.flags & COMMON) {
|
||||
/* do not send "have", and ignore ancestors */
|
||||
commit = NULL;
|
||||
mark = COMMON | SEEN;
|
||||
} else if (commit->object.flags & COMMON_REF)
|
||||
/* send "have", and ignore ancestors */
|
||||
mark = COMMON | SEEN;
|
||||
else
|
||||
/* send "have", also for its ancestors */
|
||||
mark = SEEN;
|
||||
|
||||
while (parents) {
|
||||
if (!(parents->item->object.flags & SEEN))
|
||||
rev_list_push(parents->item, mark);
|
||||
if (mark & COMMON)
|
||||
mark_common(parents->item, 1, 0);
|
||||
parents = parents->next;
|
||||
}
|
||||
|
||||
rev_list = rev_list->next;
|
||||
}
|
||||
|
||||
return commit->object.sha1;
|
||||
}
|
||||
|
||||
enum ack_type {
|
||||
NAK = 0,
|
||||
ACK,
|
||||
ACK_continue,
|
||||
ACK_common,
|
||||
ACK_ready
|
||||
};
|
||||
|
||||
static void consume_shallow_list(struct fetch_pack_args *args, int fd)
|
||||
{
|
||||
if (args->stateless_rpc && args->depth > 0) {
|
||||
/* If we sent a depth we will get back "duplicate"
|
||||
* shallow and unshallow commands every time there
|
||||
* is a block of have lines exchanged.
|
||||
*/
|
||||
char line[1000];
|
||||
while (packet_read_line(fd, line, sizeof(line))) {
|
||||
if (!prefixcmp(line, "shallow "))
|
||||
continue;
|
||||
if (!prefixcmp(line, "unshallow "))
|
||||
continue;
|
||||
die("git fetch-pack: expected shallow list");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct write_shallow_data {
|
||||
struct strbuf *out;
|
||||
int use_pack_protocol;
|
||||
int count;
|
||||
};
|
||||
|
||||
static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
|
||||
{
|
||||
struct write_shallow_data *data = cb_data;
|
||||
const char *hex = sha1_to_hex(graft->sha1);
|
||||
data->count++;
|
||||
if (data->use_pack_protocol)
|
||||
packet_buf_write(data->out, "shallow %s", hex);
|
||||
else {
|
||||
strbuf_addstr(data->out, hex);
|
||||
strbuf_addch(data->out, '\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
|
||||
{
|
||||
struct write_shallow_data data;
|
||||
data.out = out;
|
||||
data.use_pack_protocol = use_pack_protocol;
|
||||
data.count = 0;
|
||||
for_each_commit_graft(write_one_shallow, &data);
|
||||
return data.count;
|
||||
}
|
||||
|
||||
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
|
||||
{
|
||||
static char line[1000];
|
||||
int len = packet_read_line(fd, line, sizeof(line));
|
||||
|
||||
if (!len)
|
||||
die("git fetch-pack: expected ACK/NAK, got EOF");
|
||||
if (line[len-1] == '\n')
|
||||
line[--len] = 0;
|
||||
if (!strcmp(line, "NAK"))
|
||||
return NAK;
|
||||
if (!prefixcmp(line, "ACK ")) {
|
||||
if (!get_sha1_hex(line+4, result_sha1)) {
|
||||
if (strstr(line+45, "continue"))
|
||||
return ACK_continue;
|
||||
if (strstr(line+45, "common"))
|
||||
return ACK_common;
|
||||
if (strstr(line+45, "ready"))
|
||||
return ACK_ready;
|
||||
return ACK;
|
||||
}
|
||||
}
|
||||
die("git fetch_pack: expected ACK/NAK, got '%s'", line);
|
||||
}
|
||||
|
||||
static void send_request(struct fetch_pack_args *args,
|
||||
int fd, struct strbuf *buf)
|
||||
{
|
||||
if (args->stateless_rpc) {
|
||||
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
|
||||
packet_flush(fd);
|
||||
} else
|
||||
safe_write(fd, buf->buf, buf->len);
|
||||
}
|
||||
|
||||
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
|
||||
{
|
||||
rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
|
||||
}
|
||||
|
||||
#define INITIAL_FLUSH 16
|
||||
#define PIPESAFE_FLUSH 32
|
||||
#define LARGE_FLUSH 1024
|
||||
|
||||
static int next_flush(struct fetch_pack_args *args, int count)
|
||||
{
|
||||
int flush_limit = args->stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
|
||||
|
||||
if (count < flush_limit)
|
||||
count <<= 1;
|
||||
else
|
||||
count += flush_limit;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int find_common(struct fetch_pack_args *args,
|
||||
int fd[2], unsigned char *result_sha1,
|
||||
struct ref *refs)
|
||||
{
|
||||
int fetching;
|
||||
int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
|
||||
const unsigned char *sha1;
|
||||
unsigned in_vain = 0;
|
||||
int got_continue = 0;
|
||||
int got_ready = 0;
|
||||
struct strbuf req_buf = STRBUF_INIT;
|
||||
size_t state_len = 0;
|
||||
|
||||
if (args->stateless_rpc && multi_ack == 1)
|
||||
die("--stateless-rpc requires multi_ack_detailed");
|
||||
if (marked)
|
||||
for_each_ref(clear_marks, NULL);
|
||||
marked = 1;
|
||||
|
||||
for_each_ref(rev_list_insert_ref, NULL);
|
||||
for_each_alternate_ref(insert_one_alternate_ref, NULL);
|
||||
|
||||
fetching = 0;
|
||||
for ( ; refs ; refs = refs->next) {
|
||||
unsigned char *remote = refs->old_sha1;
|
||||
const char *remote_hex;
|
||||
struct object *o;
|
||||
|
||||
/*
|
||||
* If that object is complete (i.e. it is an ancestor of a
|
||||
* local ref), we tell them we have it but do not have to
|
||||
* tell them about its ancestors, which they already know
|
||||
* about.
|
||||
*
|
||||
* We use lookup_object here because we are only
|
||||
* interested in the case we *know* the object is
|
||||
* reachable and we have already scanned it.
|
||||
*/
|
||||
if (((o = lookup_object(remote)) != NULL) &&
|
||||
(o->flags & COMPLETE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
remote_hex = sha1_to_hex(remote);
|
||||
if (!fetching) {
|
||||
struct strbuf c = STRBUF_INIT;
|
||||
if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed");
|
||||
if (multi_ack == 1) strbuf_addstr(&c, " multi_ack");
|
||||
if (no_done) strbuf_addstr(&c, " no-done");
|
||||
if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k");
|
||||
if (use_sideband == 1) strbuf_addstr(&c, " side-band");
|
||||
if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
|
||||
if (args->no_progress) strbuf_addstr(&c, " no-progress");
|
||||
if (args->include_tag) strbuf_addstr(&c, " include-tag");
|
||||
if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta");
|
||||
if (agent_supported) strbuf_addf(&c, " agent=%s",
|
||||
git_user_agent_sanitized());
|
||||
packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
|
||||
strbuf_release(&c);
|
||||
} else
|
||||
packet_buf_write(&req_buf, "want %s\n", remote_hex);
|
||||
fetching++;
|
||||
}
|
||||
|
||||
if (!fetching) {
|
||||
strbuf_release(&req_buf);
|
||||
packet_flush(fd[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_repository_shallow())
|
||||
write_shallow_commits(&req_buf, 1);
|
||||
if (args->depth > 0)
|
||||
packet_buf_write(&req_buf, "deepen %d", args->depth);
|
||||
packet_buf_flush(&req_buf);
|
||||
state_len = req_buf.len;
|
||||
|
||||
if (args->depth > 0) {
|
||||
char line[1024];
|
||||
unsigned char sha1[20];
|
||||
|
||||
send_request(args, fd[1], &req_buf);
|
||||
while (packet_read_line(fd[0], line, sizeof(line))) {
|
||||
if (!prefixcmp(line, "shallow ")) {
|
||||
if (get_sha1_hex(line + 8, sha1))
|
||||
die("invalid shallow line: %s", line);
|
||||
register_shallow(sha1);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(line, "unshallow ")) {
|
||||
if (get_sha1_hex(line + 10, sha1))
|
||||
die("invalid unshallow line: %s", line);
|
||||
if (!lookup_object(sha1))
|
||||
die("object not found: %s", line);
|
||||
/* make sure that it is parsed as shallow */
|
||||
if (!parse_object(sha1))
|
||||
die("error in object: %s", line);
|
||||
if (unregister_shallow(sha1))
|
||||
die("no shallow found: %s", line);
|
||||
continue;
|
||||
}
|
||||
die("expected shallow/unshallow, got %s", line);
|
||||
}
|
||||
} else if (!args->stateless_rpc)
|
||||
send_request(args, fd[1], &req_buf);
|
||||
|
||||
if (!args->stateless_rpc) {
|
||||
/* If we aren't using the stateless-rpc interface
|
||||
* we don't need to retain the headers.
|
||||
*/
|
||||
strbuf_setlen(&req_buf, 0);
|
||||
state_len = 0;
|
||||
}
|
||||
|
||||
flushes = 0;
|
||||
retval = -1;
|
||||
while ((sha1 = get_rev())) {
|
||||
packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
|
||||
in_vain++;
|
||||
if (flush_at <= ++count) {
|
||||
int ack;
|
||||
|
||||
packet_buf_flush(&req_buf);
|
||||
send_request(args, fd[1], &req_buf);
|
||||
strbuf_setlen(&req_buf, state_len);
|
||||
flushes++;
|
||||
flush_at = next_flush(args, count);
|
||||
|
||||
/*
|
||||
* We keep one window "ahead" of the other side, and
|
||||
* will wait for an ACK only on the next one
|
||||
*/
|
||||
if (!args->stateless_rpc && count == INITIAL_FLUSH)
|
||||
continue;
|
||||
|
||||
consume_shallow_list(args, fd[0]);
|
||||
do {
|
||||
ack = get_ack(fd[0], result_sha1);
|
||||
if (args->verbose && ack)
|
||||
fprintf(stderr, "got ack %d %s\n", ack,
|
||||
sha1_to_hex(result_sha1));
|
||||
switch (ack) {
|
||||
case ACK:
|
||||
flushes = 0;
|
||||
multi_ack = 0;
|
||||
retval = 0;
|
||||
goto done;
|
||||
case ACK_common:
|
||||
case ACK_ready:
|
||||
case ACK_continue: {
|
||||
struct commit *commit =
|
||||
lookup_commit(result_sha1);
|
||||
if (!commit)
|
||||
die("invalid commit %s", sha1_to_hex(result_sha1));
|
||||
if (args->stateless_rpc
|
||||
&& ack == ACK_common
|
||||
&& !(commit->object.flags & COMMON)) {
|
||||
/* We need to replay the have for this object
|
||||
* on the next RPC request so the peer knows
|
||||
* it is in common with us.
|
||||
*/
|
||||
const char *hex = sha1_to_hex(result_sha1);
|
||||
packet_buf_write(&req_buf, "have %s\n", hex);
|
||||
state_len = req_buf.len;
|
||||
}
|
||||
mark_common(commit, 0, 1);
|
||||
retval = 0;
|
||||
in_vain = 0;
|
||||
got_continue = 1;
|
||||
if (ack == ACK_ready) {
|
||||
rev_list = NULL;
|
||||
got_ready = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (ack);
|
||||
flushes--;
|
||||
if (got_continue && MAX_IN_VAIN < in_vain) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "giving up\n");
|
||||
break; /* give up */
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (!got_ready || !no_done) {
|
||||
packet_buf_write(&req_buf, "done\n");
|
||||
send_request(args, fd[1], &req_buf);
|
||||
}
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "done\n");
|
||||
if (retval != 0) {
|
||||
multi_ack = 0;
|
||||
flushes++;
|
||||
}
|
||||
strbuf_release(&req_buf);
|
||||
|
||||
consume_shallow_list(args, fd[0]);
|
||||
while (flushes || multi_ack) {
|
||||
int ack = get_ack(fd[0], result_sha1);
|
||||
if (ack) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "got ack (%d) %s\n", ack,
|
||||
sha1_to_hex(result_sha1));
|
||||
if (ack == ACK)
|
||||
return 0;
|
||||
multi_ack = 1;
|
||||
continue;
|
||||
}
|
||||
flushes--;
|
||||
}
|
||||
/* it is no error to fetch into a completely empty repo */
|
||||
return count ? retval : 0;
|
||||
}
|
||||
|
||||
static struct commit_list *complete;
|
||||
|
||||
static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct object *o = parse_object(sha1);
|
||||
|
||||
while (o && o->type == OBJ_TAG) {
|
||||
struct tag *t = (struct tag *) o;
|
||||
if (!t->tagged)
|
||||
break; /* broken repository */
|
||||
o->flags |= COMPLETE;
|
||||
o = parse_object(t->tagged->sha1);
|
||||
}
|
||||
if (o && o->type == OBJ_COMMIT) {
|
||||
struct commit *commit = (struct commit *)o;
|
||||
if (!(commit->object.flags & COMPLETE)) {
|
||||
commit->object.flags |= COMPLETE;
|
||||
commit_list_insert_by_date(commit, &complete);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark_recent_complete_commits(struct fetch_pack_args *args,
|
||||
unsigned long cutoff)
|
||||
{
|
||||
while (complete && cutoff <= complete->item->date) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "Marking %s as complete\n",
|
||||
sha1_to_hex(complete->item->object.sha1));
|
||||
pop_most_recent_commit(&complete, COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
static int non_matching_ref(struct string_list_item *item, void *unused)
|
||||
{
|
||||
if (item->util) {
|
||||
item->util = NULL;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void filter_refs(struct fetch_pack_args *args,
|
||||
struct ref **refs, struct string_list *sought)
|
||||
{
|
||||
struct ref *newlist = NULL;
|
||||
struct ref **newtail = &newlist;
|
||||
struct ref *ref, *next;
|
||||
int sought_pos;
|
||||
|
||||
sought_pos = 0;
|
||||
for (ref = *refs; ref; ref = next) {
|
||||
int keep = 0;
|
||||
next = ref->next;
|
||||
if (!memcmp(ref->name, "refs/", 5) &&
|
||||
check_refname_format(ref->name + 5, 0))
|
||||
; /* trash */
|
||||
else {
|
||||
while (sought_pos < sought->nr) {
|
||||
int cmp = strcmp(ref->name, sought->items[sought_pos].string);
|
||||
if (cmp < 0)
|
||||
break; /* definitely do not have it */
|
||||
else if (cmp == 0) {
|
||||
keep = 1; /* definitely have it */
|
||||
sought->items[sought_pos++].util = "matched";
|
||||
break;
|
||||
}
|
||||
else
|
||||
sought_pos++; /* might have it; keep looking */
|
||||
}
|
||||
}
|
||||
|
||||
if (! keep && args->fetch_all &&
|
||||
(!args->depth || prefixcmp(ref->name, "refs/tags/")))
|
||||
keep = 1;
|
||||
|
||||
if (keep) {
|
||||
*newtail = ref;
|
||||
ref->next = NULL;
|
||||
newtail = &ref->next;
|
||||
} else {
|
||||
free(ref);
|
||||
}
|
||||
}
|
||||
|
||||
filter_string_list(sought, 0, non_matching_ref, NULL);
|
||||
*refs = newlist;
|
||||
}
|
||||
|
||||
static void mark_alternate_complete(const struct ref *ref, void *unused)
|
||||
{
|
||||
mark_complete(NULL, ref->old_sha1, 0, NULL);
|
||||
}
|
||||
|
||||
static int everything_local(struct fetch_pack_args *args,
|
||||
struct ref **refs, struct string_list *sought)
|
||||
{
|
||||
struct ref *ref;
|
||||
int retval;
|
||||
unsigned long cutoff = 0;
|
||||
|
||||
save_commit_buffer = 0;
|
||||
|
||||
for (ref = *refs; ref; ref = ref->next) {
|
||||
struct object *o;
|
||||
|
||||
o = parse_object(ref->old_sha1);
|
||||
if (!o)
|
||||
continue;
|
||||
|
||||
/* We already have it -- which may mean that we were
|
||||
* in sync with the other side at some time after
|
||||
* that (it is OK if we guess wrong here).
|
||||
*/
|
||||
if (o->type == OBJ_COMMIT) {
|
||||
struct commit *commit = (struct commit *)o;
|
||||
if (!cutoff || cutoff < commit->date)
|
||||
cutoff = commit->date;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args->depth) {
|
||||
for_each_ref(mark_complete, NULL);
|
||||
for_each_alternate_ref(mark_alternate_complete, NULL);
|
||||
if (cutoff)
|
||||
mark_recent_complete_commits(args, cutoff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark all complete remote refs as common refs.
|
||||
* Don't mark them common yet; the server has to be told so first.
|
||||
*/
|
||||
for (ref = *refs; ref; ref = ref->next) {
|
||||
struct object *o = deref_tag(lookup_object(ref->old_sha1),
|
||||
NULL, 0);
|
||||
|
||||
if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
|
||||
continue;
|
||||
|
||||
if (!(o->flags & SEEN)) {
|
||||
rev_list_push((struct commit *)o, COMMON_REF | SEEN);
|
||||
|
||||
mark_common((struct commit *)o, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
filter_refs(args, refs, sought);
|
||||
|
||||
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
|
||||
const unsigned char *remote = ref->old_sha1;
|
||||
unsigned char local[20];
|
||||
struct object *o;
|
||||
|
||||
o = lookup_object(remote);
|
||||
if (!o || !(o->flags & COMPLETE)) {
|
||||
retval = 0;
|
||||
if (!args->verbose)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"want %s (%s)\n", sha1_to_hex(remote),
|
||||
ref->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
hashcpy(ref->new_sha1, local);
|
||||
if (!args->verbose)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"already have %s (%s)\n", sha1_to_hex(remote),
|
||||
ref->name);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sideband_demux(int in, int out, void *data)
|
||||
{
|
||||
int *xd = data;
|
||||
|
||||
int ret = recv_sideband("fetch-pack", xd[0], out);
|
||||
close(out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_pack(struct fetch_pack_args *args,
|
||||
int xd[2], char **pack_lockfile)
|
||||
{
|
||||
struct async demux;
|
||||
const char *argv[20];
|
||||
char keep_arg[256];
|
||||
char hdr_arg[256];
|
||||
const char **av;
|
||||
int do_keep = args->keep_pack;
|
||||
struct child_process cmd;
|
||||
|
||||
memset(&demux, 0, sizeof(demux));
|
||||
if (use_sideband) {
|
||||
/* xd[] is talking with upload-pack; subprocess reads from
|
||||
* xd[0], spits out band#2 to stderr, and feeds us band#1
|
||||
* through demux->out.
|
||||
*/
|
||||
demux.proc = sideband_demux;
|
||||
demux.data = xd;
|
||||
demux.out = -1;
|
||||
if (start_async(&demux))
|
||||
die("fetch-pack: unable to fork off sideband"
|
||||
" demultiplexer");
|
||||
}
|
||||
else
|
||||
demux.out = xd[0];
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.argv = argv;
|
||||
av = argv;
|
||||
*hdr_arg = 0;
|
||||
if (!args->keep_pack && unpack_limit) {
|
||||
struct pack_header header;
|
||||
|
||||
if (read_pack_header(demux.out, &header))
|
||||
die("protocol error: bad pack header");
|
||||
snprintf(hdr_arg, sizeof(hdr_arg),
|
||||
"--pack_header=%"PRIu32",%"PRIu32,
|
||||
ntohl(header.hdr_version), ntohl(header.hdr_entries));
|
||||
if (ntohl(header.hdr_entries) < unpack_limit)
|
||||
do_keep = 0;
|
||||
else
|
||||
do_keep = 1;
|
||||
}
|
||||
|
||||
if (do_keep) {
|
||||
if (pack_lockfile)
|
||||
cmd.out = -1;
|
||||
*av++ = "index-pack";
|
||||
*av++ = "--stdin";
|
||||
if (!args->quiet && !args->no_progress)
|
||||
*av++ = "-v";
|
||||
if (args->use_thin_pack)
|
||||
*av++ = "--fix-thin";
|
||||
if (args->lock_pack || unpack_limit) {
|
||||
int s = sprintf(keep_arg,
|
||||
"--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
|
||||
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
|
||||
strcpy(keep_arg + s, "localhost");
|
||||
*av++ = keep_arg;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*av++ = "unpack-objects";
|
||||
if (args->quiet || args->no_progress)
|
||||
*av++ = "-q";
|
||||
}
|
||||
if (*hdr_arg)
|
||||
*av++ = hdr_arg;
|
||||
if (fetch_fsck_objects >= 0
|
||||
? fetch_fsck_objects
|
||||
: transfer_fsck_objects >= 0
|
||||
? transfer_fsck_objects
|
||||
: 0)
|
||||
*av++ = "--strict";
|
||||
*av++ = NULL;
|
||||
|
||||
cmd.in = demux.out;
|
||||
cmd.git_cmd = 1;
|
||||
if (start_command(&cmd))
|
||||
die("fetch-pack: unable to fork off %s", argv[0]);
|
||||
if (do_keep && pack_lockfile) {
|
||||
*pack_lockfile = index_pack_lockfile(cmd.out);
|
||||
close(cmd.out);
|
||||
}
|
||||
|
||||
if (finish_command(&cmd))
|
||||
die("%s failed", argv[0]);
|
||||
if (use_sideband && finish_async(&demux))
|
||||
die("error in sideband demultiplexer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
||||
int fd[2],
|
||||
const struct ref *orig_ref,
|
||||
struct string_list *sought,
|
||||
char **pack_lockfile)
|
||||
{
|
||||
struct ref *ref = copy_ref_list(orig_ref);
|
||||
unsigned char sha1[20];
|
||||
const char *agent_feature;
|
||||
int agent_len;
|
||||
|
||||
sort_ref_list(&ref, ref_compare_name);
|
||||
|
||||
if (is_repository_shallow() && !server_supports("shallow"))
|
||||
die("Server does not support shallow clients");
|
||||
if (server_supports("multi_ack_detailed")) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "Server supports multi_ack_detailed\n");
|
||||
multi_ack = 2;
|
||||
if (server_supports("no-done")) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "Server supports no-done\n");
|
||||
if (args->stateless_rpc)
|
||||
no_done = 1;
|
||||
}
|
||||
}
|
||||
else if (server_supports("multi_ack")) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "Server supports multi_ack\n");
|
||||
multi_ack = 1;
|
||||
}
|
||||
if (server_supports("side-band-64k")) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "Server supports side-band-64k\n");
|
||||
use_sideband = 2;
|
||||
}
|
||||
else if (server_supports("side-band")) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "Server supports side-band\n");
|
||||
use_sideband = 1;
|
||||
}
|
||||
if (!server_supports("thin-pack"))
|
||||
args->use_thin_pack = 0;
|
||||
if (!server_supports("no-progress"))
|
||||
args->no_progress = 0;
|
||||
if (!server_supports("include-tag"))
|
||||
args->include_tag = 0;
|
||||
if (server_supports("ofs-delta")) {
|
||||
if (args->verbose)
|
||||
fprintf(stderr, "Server supports ofs-delta\n");
|
||||
} else
|
||||
prefer_ofs_delta = 0;
|
||||
|
||||
if ((agent_feature = server_feature_value("agent", &agent_len))) {
|
||||
agent_supported = 1;
|
||||
if (args->verbose && agent_len)
|
||||
fprintf(stderr, "Server version is %.*s\n",
|
||||
agent_len, agent_feature);
|
||||
}
|
||||
|
||||
if (everything_local(args, &ref, sought)) {
|
||||
packet_flush(fd[1]);
|
||||
goto all_done;
|
||||
}
|
||||
if (find_common(args, fd, sha1, ref) < 0)
|
||||
if (!args->keep_pack)
|
||||
/* When cloning, it is not unusual to have
|
||||
* no common commit.
|
||||
*/
|
||||
warning("no common commits");
|
||||
|
||||
if (args->stateless_rpc)
|
||||
packet_flush(fd[1]);
|
||||
if (get_pack(args, fd, pack_lockfile))
|
||||
die("git fetch-pack: fetch failed.");
|
||||
|
||||
all_done:
|
||||
return ref;
|
||||
}
|
||||
|
||||
static int fetch_pack_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (strcmp(var, "fetch.unpacklimit") == 0) {
|
||||
fetch_unpack_limit = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(var, "transfer.unpacklimit") == 0) {
|
||||
transfer_unpack_limit = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
|
||||
prefer_ofs_delta = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "fetch.fsckobjects")) {
|
||||
fetch_fsck_objects = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "transfer.fsckobjects")) {
|
||||
transfer_fsck_objects = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static struct lock_file lock;
|
||||
|
||||
static void fetch_pack_setup(void)
|
||||
{
|
||||
static int did_setup;
|
||||
if (did_setup)
|
||||
return;
|
||||
git_config(fetch_pack_config, NULL);
|
||||
if (0 <= transfer_unpack_limit)
|
||||
unpack_limit = transfer_unpack_limit;
|
||||
else if (0 <= fetch_unpack_limit)
|
||||
unpack_limit = fetch_unpack_limit;
|
||||
did_setup = 1;
|
||||
}
|
||||
|
||||
struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||
int fd[], struct child_process *conn,
|
||||
const struct ref *ref,
|
||||
const char *dest,
|
||||
struct string_list *sought,
|
||||
char **pack_lockfile)
|
||||
{
|
||||
struct stat st;
|
||||
struct ref *ref_cpy;
|
||||
|
||||
fetch_pack_setup();
|
||||
if (args->depth > 0) {
|
||||
if (stat(git_path("shallow"), &st))
|
||||
st.st_mtime = 0;
|
||||
}
|
||||
|
||||
if (sought->nr) {
|
||||
sort_string_list(sought);
|
||||
string_list_remove_duplicates(sought, 0);
|
||||
}
|
||||
|
||||
if (!ref) {
|
||||
packet_flush(fd[1]);
|
||||
die("no matching remote head");
|
||||
}
|
||||
ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile);
|
||||
|
||||
if (args->depth > 0) {
|
||||
struct cache_time mtime;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
char *shallow = git_path("shallow");
|
||||
int fd;
|
||||
|
||||
mtime.sec = st.st_mtime;
|
||||
mtime.nsec = ST_MTIME_NSEC(st);
|
||||
if (stat(shallow, &st)) {
|
||||
if (mtime.sec)
|
||||
die("shallow file was removed during fetch");
|
||||
} else if (st.st_mtime != mtime.sec
|
||||
#ifdef USE_NSEC
|
||||
|| ST_MTIME_NSEC(st) != mtime.nsec
|
||||
#endif
|
||||
)
|
||||
die("shallow file was changed during fetch");
|
||||
|
||||
fd = hold_lock_file_for_update(&lock, shallow,
|
||||
LOCK_DIE_ON_ERROR);
|
||||
if (!write_shallow_commits(&sb, 0)
|
||||
|| write_in_full(fd, sb.buf, sb.len) != sb.len) {
|
||||
unlink_or_warn(shallow);
|
||||
rollback_lock_file(&lock);
|
||||
} else {
|
||||
commit_lock_file(&lock);
|
||||
}
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
reprepare_packed_git();
|
||||
return ref_cpy;
|
||||
}
|
@ -24,14 +24,14 @@ use File::Basename qw(basename dirname);
|
||||
use Time::Local;
|
||||
use IO::Socket;
|
||||
use IO::Pipe;
|
||||
use POSIX qw(strftime tzset dup2 ENOENT);
|
||||
use POSIX qw(strftime dup2 ENOENT);
|
||||
use IPC::Open2;
|
||||
|
||||
$SIG{'PIPE'}="IGNORE";
|
||||
set_timezone('UTC');
|
||||
$ENV{'TZ'}="UTC";
|
||||
|
||||
our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
|
||||
my (%conv_author_name, %conv_author_email, %conv_author_tz);
|
||||
my (%conv_author_name, %conv_author_email);
|
||||
|
||||
sub usage(;$) {
|
||||
my $msg = shift;
|
||||
@ -59,14 +59,6 @@ sub read_author_info($) {
|
||||
$conv_author_name{$user} = $2;
|
||||
$conv_author_email{$user} = $3;
|
||||
}
|
||||
# or with an optional timezone:
|
||||
# spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
|
||||
elsif (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*(\S+?)\s*$/) {
|
||||
$user = $1;
|
||||
$conv_author_name{$user} = $2;
|
||||
$conv_author_email{$user} = $3;
|
||||
$conv_author_tz{$user} = $4;
|
||||
}
|
||||
# However, we also read from CVSROOT/users format
|
||||
# to ease migration.
|
||||
elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
|
||||
@ -92,22 +84,11 @@ sub write_author_info($) {
|
||||
die("Failed to open $file for writing: $!");
|
||||
|
||||
foreach (keys %conv_author_name) {
|
||||
print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>";
|
||||
print $f " $conv_author_tz{$_}" if ($conv_author_tz{$_});
|
||||
print $f "\n";
|
||||
print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>\n";
|
||||
}
|
||||
close ($f);
|
||||
}
|
||||
|
||||
# Versions of perl before 5.10.0 may not automatically check $TZ each
|
||||
# time localtime is run (most platforms will do so only the first time).
|
||||
# We can work around this by using tzset() to update the internal
|
||||
# variable whenever we change the environment.
|
||||
sub set_timezone {
|
||||
$ENV{TZ} = shift;
|
||||
tzset();
|
||||
}
|
||||
|
||||
# convert getopts specs for use by git config
|
||||
my %longmap = (
|
||||
'A:' => 'authors-file',
|
||||
@ -814,7 +795,7 @@ sub write_tree () {
|
||||
return $tree;
|
||||
}
|
||||
|
||||
my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);
|
||||
my ($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
|
||||
my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
|
||||
|
||||
# commits that cvsps cannot place anywhere...
|
||||
@ -863,9 +844,7 @@ sub commit {
|
||||
}
|
||||
}
|
||||
|
||||
set_timezone($author_tz);
|
||||
my $commit_date = strftime("%s %z", localtime($date));
|
||||
set_timezone('UTC');
|
||||
my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date));
|
||||
$ENV{GIT_AUTHOR_NAME} = $author_name;
|
||||
$ENV{GIT_AUTHOR_EMAIL} = $author_email;
|
||||
$ENV{GIT_AUTHOR_DATE} = $commit_date;
|
||||
@ -966,14 +945,12 @@ while (<CVS>) {
|
||||
}
|
||||
$state=3;
|
||||
} elsif ($state == 3 and s/^Author:\s+//) {
|
||||
$author_tz = "UTC";
|
||||
s/\s+$//;
|
||||
if (/^(.*?)\s+<(.*)>/) {
|
||||
($author_name, $author_email) = ($1, $2);
|
||||
} elsif ($conv_author_name{$_}) {
|
||||
$author_name = $conv_author_name{$_};
|
||||
$author_email = $conv_author_email{$_};
|
||||
$author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});
|
||||
} else {
|
||||
$author_name = $author_email = $_;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -64,19 +64,37 @@ EOF
|
||||
|
||||
eval "$functions"
|
||||
|
||||
finish_ident() {
|
||||
# Ensure non-empty id name.
|
||||
echo "case \"\$GIT_$1_NAME\" in \"\") GIT_$1_NAME=\"\${GIT_$1_EMAIL%%@*}\" && export GIT_$1_NAME;; esac"
|
||||
# And make sure everything is exported.
|
||||
echo "export GIT_$1_NAME"
|
||||
echo "export GIT_$1_EMAIL"
|
||||
echo "export GIT_$1_DATE"
|
||||
}
|
||||
# When piped a commit, output a script to set the ident of either
|
||||
# "author" or "committer
|
||||
|
||||
set_ident () {
|
||||
parse_ident_from_commit author AUTHOR committer COMMITTER
|
||||
finish_ident AUTHOR
|
||||
finish_ident COMMITTER
|
||||
lid="$(echo "$1" | tr "[A-Z]" "[a-z]")"
|
||||
uid="$(echo "$1" | tr "[a-z]" "[A-Z]")"
|
||||
pick_id_script='
|
||||
/^'$lid' /{
|
||||
s/'\''/'\''\\'\'\''/g
|
||||
h
|
||||
s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p
|
||||
|
||||
g
|
||||
s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
|
||||
|
||||
g
|
||||
s/^'$lid' [^<]* <[^>]*> \(.*\)$/@\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
|
||||
|
||||
q
|
||||
}
|
||||
'
|
||||
|
||||
LANG=C LC_ALL=C sed -ne "$pick_id_script"
|
||||
# Ensure non-empty id name.
|
||||
echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
|
||||
}
|
||||
|
||||
USAGE="[--env-filter <command>] [--tree-filter <command>]
|
||||
@ -302,8 +320,10 @@ while read commit parents; do
|
||||
git cat-file commit "$commit" >../commit ||
|
||||
die "Cannot read commit $commit"
|
||||
|
||||
eval "$(set_ident <../commit)" ||
|
||||
die "setting author/committer failed for commit $commit"
|
||||
eval "$(set_ident AUTHOR <../commit)" ||
|
||||
die "setting author failed for commit $commit"
|
||||
eval "$(set_ident COMMITTER <../commit)" ||
|
||||
die "setting committer failed for commit $commit"
|
||||
eval "$filter_env" < /dev/null ||
|
||||
die "env filter failed: $filter_env"
|
||||
|
||||
|
97
git-p4.py
97
git-p4.py
@ -129,25 +129,6 @@ def p4_has_command(cmd):
|
||||
p.communicate()
|
||||
return p.returncode == 0
|
||||
|
||||
def p4_has_move_command():
|
||||
"""See if the move command exists, that it supports -k, and that
|
||||
it has not been administratively disabled. The arguments
|
||||
must be correct, but the filenames do not have to exist. Use
|
||||
ones with wildcards so even if they exist, it will fail."""
|
||||
|
||||
if not p4_has_command("move"):
|
||||
return False
|
||||
cmd = p4_build_cmd(["move", "-k", "@from", "@to"])
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
# return code will be 1 in either case
|
||||
if err.find("Invalid option") >= 0:
|
||||
return False
|
||||
if err.find("disabled") >= 0:
|
||||
return False
|
||||
# assume it failed because @... was invalid changelist
|
||||
return True
|
||||
|
||||
def system(cmd):
|
||||
expand = isinstance(cmd,basestring)
|
||||
if verbose:
|
||||
@ -188,29 +169,6 @@ def p4_reopen(type, f):
|
||||
def p4_move(src, dest):
|
||||
p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
|
||||
|
||||
def p4_describe(change):
|
||||
"""Make sure it returns a valid result by checking for
|
||||
the presence of field "time". Return a dict of the
|
||||
results."""
|
||||
|
||||
ds = p4CmdList(["describe", "-s", str(change)])
|
||||
if len(ds) != 1:
|
||||
die("p4 describe -s %d did not return 1 result: %s" % (change, str(ds)))
|
||||
|
||||
d = ds[0]
|
||||
|
||||
if "p4ExitCode" in d:
|
||||
die("p4 describe -s %d exited with %d: %s" % (change, d["p4ExitCode"],
|
||||
str(d)))
|
||||
if "code" in d:
|
||||
if d["code"] == "error":
|
||||
die("p4 describe -s %d returned error code: %s" % (change, str(d)))
|
||||
|
||||
if "time" not in d:
|
||||
die("p4 describe -s %d returned no \"time\": %s" % (change, str(d)))
|
||||
|
||||
return d
|
||||
|
||||
#
|
||||
# Canonicalize the p4 type and return a tuple of the
|
||||
# base type, plus any modifiers. See "p4 help filetypes"
|
||||
@ -913,7 +871,7 @@ class P4Submit(Command, P4UserMap):
|
||||
self.conflict_behavior = None
|
||||
self.isWindows = (platform.system() == "Windows")
|
||||
self.exportLabels = False
|
||||
self.p4HasMoveCommand = p4_has_move_command()
|
||||
self.p4HasMoveCommand = p4_has_command("move")
|
||||
|
||||
def check(self):
|
||||
if len(p4CmdList("opened ...")) > 0:
|
||||
@ -2139,29 +2097,6 @@ class P4Sync(Command, P4UserMap):
|
||||
# handle another chunk of streaming data
|
||||
def streamP4FilesCb(self, marshalled):
|
||||
|
||||
# catch p4 errors and complain
|
||||
err = None
|
||||
if "code" in marshalled:
|
||||
if marshalled["code"] == "error":
|
||||
if "data" in marshalled:
|
||||
err = marshalled["data"].rstrip()
|
||||
if err:
|
||||
f = None
|
||||
if self.stream_have_file_info:
|
||||
if "depotFile" in self.stream_file:
|
||||
f = self.stream_file["depotFile"]
|
||||
# force a failure in fast-import, else an empty
|
||||
# commit will be made
|
||||
self.gitStream.write("\n")
|
||||
self.gitStream.write("die-now\n")
|
||||
self.gitStream.close()
|
||||
# ignore errors, but make sure it exits first
|
||||
self.importProcess.wait()
|
||||
if f:
|
||||
die("Error from p4 print for %s: %s" % (f, err))
|
||||
else:
|
||||
die("Error from p4 print: %s" % err)
|
||||
|
||||
if marshalled.has_key('depotFile') and self.stream_have_file_info:
|
||||
# start of a new file - output the old one first
|
||||
self.streamOneP4File(self.stream_file, self.stream_contents)
|
||||
@ -2406,7 +2341,7 @@ class P4Sync(Command, P4UserMap):
|
||||
try:
|
||||
tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
|
||||
except ValueError:
|
||||
print "Could not convert label time %s" % labelDetails['Update']
|
||||
print "Could not convert label time %s" % labelDetail['Update']
|
||||
tmwhen = 1
|
||||
|
||||
when = int(time.mktime(tmwhen))
|
||||
@ -2608,7 +2543,7 @@ class P4Sync(Command, P4UserMap):
|
||||
def importChanges(self, changes):
|
||||
cnt = 1
|
||||
for change in changes:
|
||||
description = p4_describe(change)
|
||||
description = p4Cmd(["describe", str(change)])
|
||||
self.updateOptionDict(description)
|
||||
|
||||
if not self.silent:
|
||||
@ -2732,8 +2667,14 @@ class P4Sync(Command, P4UserMap):
|
||||
|
||||
# Use time from top-most change so that all git p4 clones of
|
||||
# the same p4 repo have the same commit SHA1s.
|
||||
res = p4_describe(newestRevision)
|
||||
details["time"] = res["time"]
|
||||
res = p4CmdList("describe -s %d" % newestRevision)
|
||||
newestTime = None
|
||||
for r in res:
|
||||
if r.has_key('time'):
|
||||
newestTime = int(r['time'])
|
||||
if newestTime is None:
|
||||
die("\"describe -s\" on newest change %d did not give a time")
|
||||
details["time"] = newestTime
|
||||
|
||||
self.updateOptionDict(details)
|
||||
try:
|
||||
@ -2923,13 +2864,12 @@ class P4Sync(Command, P4UserMap):
|
||||
|
||||
self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
|
||||
|
||||
self.importProcess = subprocess.Popen(["git", "fast-import"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE);
|
||||
self.gitOutput = self.importProcess.stdout
|
||||
self.gitStream = self.importProcess.stdin
|
||||
self.gitError = self.importProcess.stderr
|
||||
importProcess = subprocess.Popen(["git", "fast-import"],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE);
|
||||
self.gitOutput = importProcess.stdout
|
||||
self.gitStream = importProcess.stdin
|
||||
self.gitError = importProcess.stderr
|
||||
|
||||
if revision:
|
||||
self.importHeadRevision(revision)
|
||||
@ -2989,7 +2929,7 @@ class P4Sync(Command, P4UserMap):
|
||||
self.importP4Labels(self.gitStream, missingP4Labels)
|
||||
|
||||
self.gitStream.close()
|
||||
if self.importProcess.wait() != 0:
|
||||
if importProcess.wait() != 0:
|
||||
die("fast-import failed: %s" % self.gitError.read())
|
||||
self.gitOutput.close()
|
||||
self.gitError.close()
|
||||
@ -3188,6 +3128,7 @@ def main():
|
||||
printUsage(commands.keys())
|
||||
sys.exit(2)
|
||||
|
||||
cmd = ""
|
||||
cmdName = sys.argv[1]
|
||||
try:
|
||||
klass = commands[cmdName]
|
||||
|
@ -56,7 +56,6 @@ git send-email [options] <file | directory | rev-list options >
|
||||
--in-reply-to <str> * Email "In-Reply-To:"
|
||||
--annotate * Review each patch that will be sent in an editor.
|
||||
--compose * Open an editor for introduction.
|
||||
--compose-encoding <str> * Encoding to assume for introduction.
|
||||
--8bit-encoding <str> * Encoding to assume 8bit mails if undeclared
|
||||
|
||||
Sending:
|
||||
@ -199,7 +198,6 @@ my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
|
||||
my ($validate, $confirm);
|
||||
my (@suppress_cc);
|
||||
my ($auto_8bit_encoding);
|
||||
my ($compose_encoding);
|
||||
|
||||
my ($debug_net_smtp) = 0; # Net::SMTP, see send_message()
|
||||
|
||||
@ -233,7 +231,6 @@ my %config_settings = (
|
||||
"confirm" => \$confirm,
|
||||
"from" => \$sender,
|
||||
"assume8bitencoding" => \$auto_8bit_encoding,
|
||||
"composeencoding" => \$compose_encoding,
|
||||
);
|
||||
|
||||
my %config_path_settings = (
|
||||
@ -318,7 +315,6 @@ my $rc = GetOptions("h" => \$help,
|
||||
"validate!" => \$validate,
|
||||
"format-patch!" => \$format_patch,
|
||||
"8bit-encoding=s" => \$auto_8bit_encoding,
|
||||
"compose-encoding=s" => \$compose_encoding,
|
||||
"force" => \$force,
|
||||
);
|
||||
|
||||
@ -636,9 +632,6 @@ EOT
|
||||
my $need_8bit_cte = file_has_nonascii($compose_filename);
|
||||
my $in_body = 0;
|
||||
my $summary_empty = 1;
|
||||
if (!defined $compose_encoding) {
|
||||
$compose_encoding = "UTF-8";
|
||||
}
|
||||
while(<$c>) {
|
||||
next if m/^GIT:/;
|
||||
if ($in_body) {
|
||||
@ -648,7 +641,7 @@ EOT
|
||||
if ($need_8bit_cte) {
|
||||
print $c2 "MIME-Version: 1.0\n",
|
||||
"Content-Type: text/plain; ",
|
||||
"charset=$compose_encoding\n",
|
||||
"charset=UTF-8\n",
|
||||
"Content-Transfer-Encoding: 8bit\n";
|
||||
}
|
||||
} elsif (/^MIME-Version:/i) {
|
||||
@ -657,7 +650,9 @@ EOT
|
||||
$initial_subject = $1;
|
||||
my $subject = $initial_subject;
|
||||
$_ = "Subject: " .
|
||||
quote_subject($subject, $compose_encoding) .
|
||||
($subject =~ /[^[:ascii:]]/ ?
|
||||
quote_rfc2047($subject) :
|
||||
$subject) .
|
||||
"\n";
|
||||
} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
|
||||
$initial_reply_to = $1;
|
||||
@ -753,11 +748,16 @@ if (!$force) {
|
||||
}
|
||||
}
|
||||
|
||||
my $prompting = 0;
|
||||
if (!defined $sender) {
|
||||
$sender = $repoauthor || $repocommitter || '';
|
||||
$sender = ask("Who should the emails appear to be from? [$sender] ",
|
||||
default => $sender,
|
||||
valid_re => qr/\@.*\./, confirm_only => 1);
|
||||
print "Emails will be sent from: ", $sender, "\n";
|
||||
$prompting++;
|
||||
}
|
||||
|
||||
my $prompting = 0;
|
||||
if (!@initial_to && !defined $to_cmd) {
|
||||
my $to = ask("Who should the emails be sent to (if any)? ",
|
||||
default => "",
|
||||
@ -781,11 +781,9 @@ sub expand_one_alias {
|
||||
}
|
||||
|
||||
@initial_to = expand_aliases(@initial_to);
|
||||
@initial_to = validate_address_list(sanitize_address_list(@initial_to));
|
||||
@initial_to = (map { sanitize_address($_) } @initial_to);
|
||||
@initial_cc = expand_aliases(@initial_cc);
|
||||
@initial_cc = validate_address_list(sanitize_address_list(@initial_cc));
|
||||
@bcclist = expand_aliases(@bcclist);
|
||||
@bcclist = validate_address_list(sanitize_address_list(@bcclist));
|
||||
|
||||
if ($thread && !defined $initial_reply_to && $prompting) {
|
||||
$initial_reply_to = ask(
|
||||
@ -828,45 +826,12 @@ sub extract_valid_address {
|
||||
$address =~ s/^\s*<(.*)>\s*$/$1/;
|
||||
if ($have_email_valid) {
|
||||
return scalar Email::Valid->address($address);
|
||||
} else {
|
||||
# less robust/correct than the monster regexp in Email::Valid,
|
||||
# but still does a 99% job, and one less dependency
|
||||
$address =~ /($local_part_regexp\@$domain_regexp)/;
|
||||
return $1;
|
||||
}
|
||||
|
||||
# less robust/correct than the monster regexp in Email::Valid,
|
||||
# but still does a 99% job, and one less dependency
|
||||
return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/;
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub extract_valid_address_or_die {
|
||||
my $address = shift;
|
||||
$address = extract_valid_address($address);
|
||||
die "error: unable to extract a valid address from: $address\n"
|
||||
if !$address;
|
||||
return $address;
|
||||
}
|
||||
|
||||
sub validate_address {
|
||||
my $address = shift;
|
||||
while (!extract_valid_address($address)) {
|
||||
print STDERR "error: unable to extract a valid address from: $address\n";
|
||||
$_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): ",
|
||||
valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
|
||||
default => 'q');
|
||||
if (/^d/i) {
|
||||
return undef;
|
||||
} elsif (/^q/i) {
|
||||
cleanup_compose_files();
|
||||
exit(0);
|
||||
}
|
||||
$address = ask("Who should the email be sent to (if any)? ",
|
||||
default => "",
|
||||
valid_re => qr/\@.*\./, confirm_only => 1);
|
||||
}
|
||||
return $address;
|
||||
}
|
||||
|
||||
sub validate_address_list {
|
||||
return (grep { defined $_ }
|
||||
map { validate_address($_) } @_);
|
||||
}
|
||||
|
||||
# Usually don't need to change anything below here.
|
||||
@ -935,29 +900,9 @@ sub is_rfc2047_quoted {
|
||||
$s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o;
|
||||
}
|
||||
|
||||
sub subject_needs_rfc2047_quoting {
|
||||
my $s = shift;
|
||||
|
||||
return ($s =~ /[^[:ascii:]]/) || ($s =~ /=\?/);
|
||||
}
|
||||
|
||||
sub quote_subject {
|
||||
local $subject = shift;
|
||||
my $encoding = shift || 'UTF-8';
|
||||
|
||||
if (subject_needs_rfc2047_quoting($subject)) {
|
||||
return quote_rfc2047($subject, $encoding);
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
|
||||
# use the simplest quoting being able to handle the recipient
|
||||
sub sanitize_address {
|
||||
my ($recipient) = @_;
|
||||
|
||||
# remove garbage after email address
|
||||
$recipient =~ s/(.*>).*$/$1/;
|
||||
|
||||
my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
|
||||
|
||||
if (not $recipient_name) {
|
||||
@ -985,10 +930,6 @@ sub sanitize_address {
|
||||
|
||||
}
|
||||
|
||||
sub sanitize_address_list {
|
||||
return (map { sanitize_address($_) } @_);
|
||||
}
|
||||
|
||||
# Returns the local Fully Qualified Domain Name (FQDN) if available.
|
||||
#
|
||||
# Tightly configured MTAa require that a caller sends a real DNS
|
||||
@ -1051,13 +992,14 @@ sub maildomain {
|
||||
|
||||
sub send_message {
|
||||
my @recipients = unique_email_list(@to);
|
||||
@cc = (grep { my $cc = extract_valid_address_or_die($_);
|
||||
@cc = (grep { my $cc = extract_valid_address($_);
|
||||
not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
|
||||
}
|
||||
map { sanitize_address($_) }
|
||||
@cc);
|
||||
my $to = join (",\n\t", @recipients);
|
||||
@recipients = unique_email_list(@recipients,@cc,@bcclist);
|
||||
@recipients = (map { extract_valid_address_or_die($_) } @recipients);
|
||||
@recipients = (map { extract_valid_address($_) } @recipients);
|
||||
my $date = format_2822_time($time++);
|
||||
my $gitversion = '@@GIT_VERSION@@';
|
||||
if ($gitversion =~ m/..GIT_VERSION../) {
|
||||
@ -1300,7 +1242,7 @@ foreach my $t (@files) {
|
||||
foreach my $addr (parse_address_line($1)) {
|
||||
printf("(mbox) Adding to: %s from line '%s'\n",
|
||||
$addr, $_) unless $quiet;
|
||||
push @to, $addr;
|
||||
push @to, sanitize_address($addr);
|
||||
}
|
||||
}
|
||||
elsif (/^Cc:\s+(.*)$/) {
|
||||
@ -1379,7 +1321,7 @@ foreach my $t (@files) {
|
||||
}
|
||||
|
||||
if ($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {
|
||||
$subject = quote_subject($subject, $auto_8bit_encoding);
|
||||
$subject = quote_rfc2047($subject, $auto_8bit_encoding);
|
||||
}
|
||||
|
||||
if (defined $author and $author ne $sender) {
|
||||
@ -1409,9 +1351,6 @@ foreach my $t (@files) {
|
||||
($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
|
||||
$needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
|
||||
|
||||
@to = validate_address_list(sanitize_address_list(@to));
|
||||
@cc = validate_address_list(sanitize_address_list(@cc));
|
||||
|
||||
@to = (@initial_to, @to);
|
||||
@cc = (@initial_cc, @cc);
|
||||
|
||||
@ -1467,10 +1406,14 @@ sub unique_email_list {
|
||||
my @emails;
|
||||
|
||||
foreach my $entry (@_) {
|
||||
my $clean = extract_valid_address_or_die($entry);
|
||||
$seen{$clean} ||= 0;
|
||||
next if $seen{$clean}++;
|
||||
push @emails, $entry;
|
||||
if (my $clean = extract_valid_address($entry)) {
|
||||
$seen{$clean} ||= 0;
|
||||
next if $seen{$clean}++;
|
||||
push @emails, $entry;
|
||||
} else {
|
||||
print STDERR "W: unable to extract a valid address",
|
||||
" from: $entry\n";
|
||||
}
|
||||
}
|
||||
return @emails;
|
||||
}
|
||||
|
@ -191,52 +191,28 @@ require_clean_work_tree () {
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate a sed script to parse identities from a commit.
|
||||
#
|
||||
# Reads the commit from stdin, which should be in raw format (e.g., from
|
||||
# cat-file or "--pretty=raw").
|
||||
#
|
||||
# The first argument specifies the ident line to parse (e.g., "author"), and
|
||||
# the second specifies the environment variable to put it in (e.g., "AUTHOR"
|
||||
# for "GIT_AUTHOR_*"). Multiple pairs can be given to parse author and
|
||||
# committer.
|
||||
pick_ident_script () {
|
||||
while test $# -gt 0
|
||||
do
|
||||
lid=$1; shift
|
||||
uid=$1; shift
|
||||
printf '%s' "
|
||||
/^$lid /{
|
||||
s/'/'\\\\''/g
|
||||
h
|
||||
s/^$lid "'\([^<]*\) <[^>]*> .*$/\1/'"
|
||||
s/.*/GIT_${uid}_NAME='&'/p
|
||||
|
||||
g
|
||||
s/^$lid "'[^<]* <\([^>]*\)> .*$/\1/'"
|
||||
s/.*/GIT_${uid}_EMAIL='&'/p
|
||||
|
||||
g
|
||||
s/^$lid "'[^<]* <[^>]*> \(.*\)$/@\1/'"
|
||||
s/.*/GIT_${uid}_DATE='&'/p
|
||||
}
|
||||
"
|
||||
done
|
||||
echo '/^$/q'
|
||||
}
|
||||
|
||||
# Create a pick-script as above and feed it to sed. Stdout is suitable for
|
||||
# feeding to eval.
|
||||
parse_ident_from_commit () {
|
||||
LANG=C LC_ALL=C sed -ne "$(pick_ident_script "$@")"
|
||||
}
|
||||
|
||||
# Parse the author from a commit given as an argument. Stdout is suitable for
|
||||
# feeding to eval to set the usual GIT_* ident variables.
|
||||
get_author_ident_from_commit () {
|
||||
pick_author_script='
|
||||
/^author /{
|
||||
s/'\''/'\''\\'\'\''/g
|
||||
h
|
||||
s/^author \([^<]*\) <[^>]*> .*$/\1/
|
||||
s/.*/GIT_AUTHOR_NAME='\''&'\''/p
|
||||
|
||||
g
|
||||
s/^author [^<]* <\([^>]*\)> .*$/\1/
|
||||
s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
|
||||
|
||||
g
|
||||
s/^author [^<]* <[^>]*> \(.*\)$/@\1/
|
||||
s/.*/GIT_AUTHOR_DATE='\''&'\''/p
|
||||
|
||||
q
|
||||
}
|
||||
'
|
||||
encoding=$(git config i18n.commitencoding || echo UTF-8)
|
||||
git show -s --pretty=raw --encoding="$encoding" "$1" -- |
|
||||
parse_ident_from_commit author AUTHOR
|
||||
LANG=C LC_ALL=C sed -ne "$pick_author_script"
|
||||
}
|
||||
|
||||
# Clear repo-local GIT_* environment variables. Useful when switching to
|
||||
|
@ -5,13 +5,13 @@
|
||||
# Copyright (c) 2007 Lars Hjemli
|
||||
|
||||
dashless=$(basename "$0" | sed -e 's/-/ /')
|
||||
USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
|
||||
USAGE="[--quiet] add [-b <branch>] [-f|--force] [--reference <repository>] [--] <repository> [<path>]
|
||||
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
|
||||
or: $dashless [--quiet] init [--] [<path>...]
|
||||
or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
|
||||
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
|
||||
or: $dashless [--quiet] foreach [--recursive] <command>
|
||||
or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
|
||||
or: $dashless [--quiet] sync [--] [<path>...]"
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
. git-sh-i18n
|
||||
@ -29,7 +29,6 @@ files=
|
||||
nofetch=
|
||||
update=
|
||||
prefix=
|
||||
custom_name=
|
||||
|
||||
# The function takes at most 2 arguments. The first argument is the
|
||||
# URL that navigates to the submodule origin repo. When relative, this URL
|
||||
@ -180,9 +179,8 @@ module_name()
|
||||
module_clone()
|
||||
{
|
||||
sm_path=$1
|
||||
name=$2
|
||||
url=$3
|
||||
reference="$4"
|
||||
url=$2
|
||||
reference="$3"
|
||||
quiet=
|
||||
if test -n "$GIT_QUIET"
|
||||
then
|
||||
@ -191,6 +189,8 @@ module_clone()
|
||||
|
||||
gitdir=
|
||||
gitdir_base=
|
||||
name=$(module_name "$sm_path" 2>/dev/null)
|
||||
test -n "$name" || name="$sm_path"
|
||||
base_name=$(dirname "$name")
|
||||
|
||||
gitdir=$(git rev-parse --git-dir)
|
||||
@ -270,10 +270,6 @@ cmd_add()
|
||||
;;
|
||||
--reference=*)
|
||||
reference="$1"
|
||||
;;
|
||||
--name)
|
||||
case "$2" in '') usage ;; esac
|
||||
custom_name=$2
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
@ -340,13 +336,6 @@ Use -f if you really want to add it." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -n "$custom_name"
|
||||
then
|
||||
sm_name="$custom_name"
|
||||
else
|
||||
sm_name="$sm_path"
|
||||
fi
|
||||
|
||||
# perhaps the path exists and is already a git repo, else clone it
|
||||
if test -e "$sm_path"
|
||||
then
|
||||
@ -358,21 +347,8 @@ Use -f if you really want to add it." >&2
|
||||
fi
|
||||
|
||||
else
|
||||
if test -d ".git/modules/$sm_name"
|
||||
then
|
||||
if test -z "$force"
|
||||
then
|
||||
echo >&2 "$(eval_gettext "A git directory for '\$sm_name' is found locally with remote(s):")"
|
||||
GIT_DIR=".git/modules/$sm_name" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^," ", -e s,' (fetch)',, >&2
|
||||
echo >&2 "$(eval_gettext "If you want to reuse this local git directory instead of cloning again from")"
|
||||
echo >&2 " $realrepo"
|
||||
echo >&2 "$(eval_gettext "use the '--force' option. If the local git directory is not the correct repo")"
|
||||
die "$(eval_gettext "or you are unsure what this means choose another name with the '--name' option.")"
|
||||
else
|
||||
echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
|
||||
fi
|
||||
fi
|
||||
module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit
|
||||
|
||||
module_clone "$sm_path" "$realrepo" "$reference" || exit
|
||||
(
|
||||
clear_local_git_env
|
||||
cd "$sm_path" &&
|
||||
@ -383,13 +359,13 @@ Use -f if you really want to add it." >&2
|
||||
esac
|
||||
) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
|
||||
fi
|
||||
git config submodule."$sm_name".url "$realrepo"
|
||||
git config submodule."$sm_path".url "$realrepo"
|
||||
|
||||
git add $force "$sm_path" ||
|
||||
die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
|
||||
|
||||
git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
|
||||
git config -f .gitmodules submodule."$sm_name".url "$repo" &&
|
||||
git config -f .gitmodules submodule."$sm_path".path "$sm_path" &&
|
||||
git config -f .gitmodules submodule."$sm_path".url "$repo" &&
|
||||
git add --force .gitmodules ||
|
||||
die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
|
||||
}
|
||||
@ -618,7 +594,7 @@ Maybe you want to use 'update --init'?")"
|
||||
|
||||
if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
|
||||
then
|
||||
module_clone "$sm_path" "$name" "$url" "$reference" || exit
|
||||
module_clone "$sm_path" "$url" "$reference"|| exit
|
||||
cloned_modules="$cloned_modules;$name"
|
||||
subsha1=
|
||||
else
|
||||
@ -950,6 +926,7 @@ cmd_summary() {
|
||||
cmd_status()
|
||||
{
|
||||
# parse $args after "submodule ... status".
|
||||
orig_flags=
|
||||
while test $# -ne 0
|
||||
do
|
||||
case "$1" in
|
||||
@ -973,6 +950,7 @@ cmd_status()
|
||||
break
|
||||
;;
|
||||
esac
|
||||
orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
|
||||
shift
|
||||
done
|
||||
|
||||
@ -1012,7 +990,7 @@ cmd_status()
|
||||
prefix="$displaypath/"
|
||||
clear_local_git_env
|
||||
cd "$sm_path" &&
|
||||
eval cmd_status
|
||||
eval cmd_status "$orig_args"
|
||||
) ||
|
||||
die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
|
||||
fi
|
||||
@ -1032,10 +1010,6 @@ cmd_sync()
|
||||
GIT_QUIET=1
|
||||
shift
|
||||
;;
|
||||
--recursive)
|
||||
recursive=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
@ -1077,7 +1051,7 @@ cmd_sync()
|
||||
|
||||
if git config "submodule.$name.url" >/dev/null 2>/dev/null
|
||||
then
|
||||
say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")"
|
||||
say "$(eval_gettext "Synchronizing submodule url for '\$name'")"
|
||||
git config submodule."$name".url "$super_config_url"
|
||||
|
||||
if test -e "$sm_path"/.git
|
||||
@ -1087,12 +1061,6 @@ cmd_sync()
|
||||
cd "$sm_path"
|
||||
remote=$(get_default_remote)
|
||||
git config remote."$remote".url "$sub_origin_url"
|
||||
|
||||
if test -n "$recursive"
|
||||
then
|
||||
prefix="$prefix$sm_path/"
|
||||
eval cmd_sync
|
||||
fi
|
||||
)
|
||||
fi
|
||||
fi
|
||||
|
33
git.c
33
git.c
@ -17,6 +17,39 @@ const char git_more_info_string[] =
|
||||
|
||||
static struct startup_info git_startup_info;
|
||||
static int use_pager = -1;
|
||||
struct pager_config {
|
||||
const char *cmd;
|
||||
int want;
|
||||
char *value;
|
||||
};
|
||||
|
||||
static int pager_command_config(const char *var, const char *value, void *data)
|
||||
{
|
||||
struct pager_config *c = data;
|
||||
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
|
||||
int b = git_config_maybe_bool(var, value);
|
||||
if (b >= 0)
|
||||
c->want = b;
|
||||
else {
|
||||
c->want = 1;
|
||||
c->value = xstrdup(value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
|
||||
int check_pager_config(const char *cmd)
|
||||
{
|
||||
struct pager_config c;
|
||||
c.cmd = cmd;
|
||||
c.want = -1;
|
||||
c.value = NULL;
|
||||
git_config(pager_command_config, &c);
|
||||
if (c.value)
|
||||
pager_program = c.value;
|
||||
return c.want;
|
||||
}
|
||||
|
||||
static void commit_pager_choice(void) {
|
||||
switch (use_pager) {
|
||||
|
@ -540,7 +540,7 @@ our %feature = (
|
||||
# $feature{'remote_heads'}{'default'} = [1];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'remote_heads'}{'override'} = 1;
|
||||
# and in project config gitweb.remoteheads = 0|1;
|
||||
# and in project config gitweb.remote_heads = 0|1;
|
||||
'remote_heads' => {
|
||||
'sub' => sub { feature_bool('remote_heads', @_) },
|
||||
'override' => 0,
|
||||
@ -2696,15 +2696,12 @@ sub git_get_project_config {
|
||||
# only subsection, if exists, is case sensitive,
|
||||
# and not lowercased by 'git config -z -l'
|
||||
if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
|
||||
$lo =~ s/_//g;
|
||||
$key = join(".", lc($hi), $mi, lc($lo));
|
||||
return if ($lo =~ /\W/ || $hi =~ /\W/);
|
||||
} else {
|
||||
$key = lc($key);
|
||||
$key =~ s/_//g;
|
||||
return if ($key =~ /\W/);
|
||||
}
|
||||
$key =~ s/^gitweb\.//;
|
||||
return if ($key =~ m/\W/);
|
||||
|
||||
# type sanity check
|
||||
if (defined $type) {
|
||||
|
6
http.c
6
http.c
@ -757,7 +757,8 @@ char *get_remote_object_url(const char *url, const char *hex,
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
int handle_curl_result(struct slot_results *results)
|
||||
int handle_curl_result(struct active_request_slot *slot,
|
||||
struct slot_results *results)
|
||||
{
|
||||
if (results->curl_result == CURLE_OK) {
|
||||
credential_approve(&http_auth);
|
||||
@ -770,6 +771,7 @@ int handle_curl_result(struct slot_results *results)
|
||||
return HTTP_NOAUTH;
|
||||
} else {
|
||||
credential_fill(&http_auth);
|
||||
init_curl_http_auth(slot->curl);
|
||||
return HTTP_REAUTH;
|
||||
}
|
||||
} else {
|
||||
@ -831,7 +833,7 @@ static int http_request(const char *url, void *result, int target, int options)
|
||||
|
||||
if (start_active_slot(slot)) {
|
||||
run_active_slot(slot);
|
||||
ret = handle_curl_result(&results);
|
||||
ret = handle_curl_result(slot, &results);
|
||||
} else {
|
||||
error("Unable to start HTTP request for %s", url);
|
||||
ret = HTTP_START_FAILED;
|
||||
|
3
http.h
3
http.h
@ -78,7 +78,8 @@ extern int start_active_slot(struct active_request_slot *slot);
|
||||
extern void run_active_slot(struct active_request_slot *slot);
|
||||
extern void finish_active_slot(struct active_request_slot *slot);
|
||||
extern void finish_all_active_slots(void);
|
||||
extern int handle_curl_result(struct slot_results *results);
|
||||
extern int handle_curl_result(struct active_request_slot *slot,
|
||||
struct slot_results *results);
|
||||
|
||||
#ifdef USE_CURL_MULTI
|
||||
extern void fill_active_slots(void);
|
||||
|
36
ident.c
36
ident.c
@ -10,12 +10,7 @@
|
||||
static struct strbuf git_default_name = STRBUF_INIT;
|
||||
static struct strbuf git_default_email = STRBUF_INIT;
|
||||
static char git_default_date[50];
|
||||
|
||||
#define IDENT_NAME_GIVEN 01
|
||||
#define IDENT_MAIL_GIVEN 02
|
||||
#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
|
||||
static int committer_ident_explicitly_given;
|
||||
static int author_ident_explicitly_given;
|
||||
int user_ident_explicitly_given;
|
||||
|
||||
#ifdef NO_GECOS_IN_PWENT
|
||||
#define get_gecos(ignored) "&"
|
||||
@ -114,8 +109,7 @@ const char *ident_default_email(void)
|
||||
|
||||
if (email && email[0]) {
|
||||
strbuf_addstr(&git_default_email, email);
|
||||
committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
} else
|
||||
copy_email(xgetpwuid_self(), &git_default_email);
|
||||
strbuf_trim(&git_default_email);
|
||||
@ -333,10 +327,6 @@ const char *fmt_name(const char *name, const char *email)
|
||||
|
||||
const char *git_author_info(int flag)
|
||||
{
|
||||
if (getenv("GIT_AUTHOR_NAME"))
|
||||
author_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
||||
if (getenv("GIT_AUTHOR_EMAIL"))
|
||||
author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
return fmt_ident(getenv("GIT_AUTHOR_NAME"),
|
||||
getenv("GIT_AUTHOR_EMAIL"),
|
||||
getenv("GIT_AUTHOR_DATE"),
|
||||
@ -346,16 +336,16 @@ const char *git_author_info(int flag)
|
||||
const char *git_committer_info(int flag)
|
||||
{
|
||||
if (getenv("GIT_COMMITTER_NAME"))
|
||||
committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
||||
user_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
||||
if (getenv("GIT_COMMITTER_EMAIL"))
|
||||
committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
return fmt_ident(getenv("GIT_COMMITTER_NAME"),
|
||||
getenv("GIT_COMMITTER_EMAIL"),
|
||||
getenv("GIT_COMMITTER_DATE"),
|
||||
flag);
|
||||
}
|
||||
|
||||
static int ident_is_sufficient(int user_ident_explicitly_given)
|
||||
int user_ident_sufficiently_given(void)
|
||||
{
|
||||
#ifndef WINDOWS
|
||||
return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
|
||||
@ -364,16 +354,6 @@ static int ident_is_sufficient(int user_ident_explicitly_given)
|
||||
#endif
|
||||
}
|
||||
|
||||
int committer_ident_sufficiently_given(void)
|
||||
{
|
||||
return ident_is_sufficient(committer_ident_explicitly_given);
|
||||
}
|
||||
|
||||
int author_ident_sufficiently_given(void)
|
||||
{
|
||||
return ident_is_sufficient(author_ident_explicitly_given);
|
||||
}
|
||||
|
||||
int git_ident_config(const char *var, const char *value, void *data)
|
||||
{
|
||||
if (!strcmp(var, "user.name")) {
|
||||
@ -381,8 +361,7 @@ int git_ident_config(const char *var, const char *value, void *data)
|
||||
return config_error_nonbool(var);
|
||||
strbuf_reset(&git_default_name);
|
||||
strbuf_addstr(&git_default_name, value);
|
||||
committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
||||
author_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
||||
user_ident_explicitly_given |= IDENT_NAME_GIVEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -391,8 +370,7 @@ int git_ident_config(const char *var, const char *value, void *data)
|
||||
return config_error_nonbool(var);
|
||||
strbuf_reset(&git_default_email);
|
||||
strbuf_addstr(&git_default_email, value);
|
||||
committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
51
log-tree.c
51
log-tree.c
@ -540,6 +540,7 @@ void show_log(struct rev_info *opt)
|
||||
struct pretty_print_context ctx = {0};
|
||||
|
||||
opt->loginfo = NULL;
|
||||
ctx.show_notes = opt->show_notes;
|
||||
if (!opt->verbose_header) {
|
||||
graph_show_commit(opt->graph);
|
||||
|
||||
@ -647,18 +648,6 @@ void show_log(struct rev_info *opt)
|
||||
if (!commit->buffer)
|
||||
return;
|
||||
|
||||
if (opt->show_notes) {
|
||||
int raw;
|
||||
struct strbuf notebuf = STRBUF_INIT;
|
||||
|
||||
raw = (opt->commit_format == CMIT_FMT_USERFORMAT);
|
||||
format_display_notes(commit->object.sha1, ¬ebuf,
|
||||
get_log_output_encoding(), raw);
|
||||
ctx.notes_message = notebuf.len
|
||||
? strbuf_detach(¬ebuf, NULL)
|
||||
: xcalloc(1, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* And then the pretty-printed message itself
|
||||
*/
|
||||
@ -675,16 +664,6 @@ void show_log(struct rev_info *opt)
|
||||
|
||||
if (opt->add_signoff)
|
||||
append_signoff(&msgbuf, opt->add_signoff);
|
||||
|
||||
if ((ctx.fmt != CMIT_FMT_USERFORMAT) &&
|
||||
ctx.notes_message && *ctx.notes_message) {
|
||||
if (ctx.fmt == CMIT_FMT_EMAIL) {
|
||||
strbuf_addstr(&msgbuf, "---\n");
|
||||
opt->shown_dashes = 1;
|
||||
}
|
||||
strbuf_addstr(&msgbuf, ctx.notes_message);
|
||||
}
|
||||
|
||||
if (opt->show_log_size) {
|
||||
printf("log size %i\n", (int)msgbuf.len);
|
||||
graph_show_oneline(opt->graph);
|
||||
@ -710,12 +689,10 @@ void show_log(struct rev_info *opt)
|
||||
}
|
||||
|
||||
strbuf_release(&msgbuf);
|
||||
free(ctx.notes_message);
|
||||
}
|
||||
|
||||
int log_tree_diff_flush(struct rev_info *opt)
|
||||
{
|
||||
opt->shown_dashes = 0;
|
||||
diffcore_std(&opt->diffopt);
|
||||
|
||||
if (diff_queue_is_empty()) {
|
||||
@ -727,16 +704,15 @@ int log_tree_diff_flush(struct rev_info *opt)
|
||||
}
|
||||
|
||||
if (opt->loginfo && !opt->no_commit_id) {
|
||||
/* When showing a verbose header (i.e. log message),
|
||||
* and not in --pretty=oneline format, we would want
|
||||
* an extra newline between the end of log and the
|
||||
* output for readability.
|
||||
*/
|
||||
show_log(opt);
|
||||
if ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&
|
||||
opt->verbose_header &&
|
||||
opt->commit_format != CMIT_FMT_ONELINE) {
|
||||
/*
|
||||
* When showing a verbose header (i.e. log message),
|
||||
* and not in --pretty=oneline format, we would want
|
||||
* an extra newline between the end of log and the
|
||||
* diff/diffstat output for readability.
|
||||
*/
|
||||
int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
|
||||
if (opt->diffopt.output_prefix) {
|
||||
struct strbuf *msg = NULL;
|
||||
@ -744,20 +720,9 @@ int log_tree_diff_flush(struct rev_info *opt)
|
||||
opt->diffopt.output_prefix_data);
|
||||
fwrite(msg->buf, msg->len, 1, stdout);
|
||||
}
|
||||
|
||||
/*
|
||||
* We may have shown three-dashes line early
|
||||
* between notes and the log message, in which
|
||||
* case we only want a blank line after the
|
||||
* notes without (an extra) three-dashes line.
|
||||
* Otherwise, we show the three-dashes line if
|
||||
* we are showing the patch with diffstat, but
|
||||
* in that case, there is no extra blank line
|
||||
* after the three-dashes line.
|
||||
*/
|
||||
if (!opt->shown_dashes &&
|
||||
(pch & opt->diffopt.output_format) == pch)
|
||||
if ((pch & opt->diffopt.output_format) == pch) {
|
||||
printf("---");
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
@ -59,4 +59,9 @@ struct tree *write_tree_from_memory(struct merge_options *o);
|
||||
|
||||
int parse_merge_opt(struct merge_options *out, const char *s);
|
||||
|
||||
/* builtin/merge.c */
|
||||
int try_merge_command(const char *strategy, size_t xopts_nr,
|
||||
const char **xopts, struct commit_list *common,
|
||||
const char *head_arg, struct commit_list *remotes);
|
||||
|
||||
#endif
|
||||
|
112
merge.c
112
merge.c
@ -1,112 +0,0 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "run-command.h"
|
||||
#include "resolve-undo.h"
|
||||
#include "tree-walk.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "dir.h"
|
||||
|
||||
static const char *merge_argument(struct commit *commit)
|
||||
{
|
||||
if (commit)
|
||||
return sha1_to_hex(commit->object.sha1);
|
||||
else
|
||||
return EMPTY_TREE_SHA1_HEX;
|
||||
}
|
||||
|
||||
int try_merge_command(const char *strategy, size_t xopts_nr,
|
||||
const char **xopts, struct commit_list *common,
|
||||
const char *head_arg, struct commit_list *remotes)
|
||||
{
|
||||
const char **args;
|
||||
int i = 0, x = 0, ret;
|
||||
struct commit_list *j;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
args = xmalloc((4 + xopts_nr + commit_list_count(common) +
|
||||
commit_list_count(remotes)) * sizeof(char *));
|
||||
strbuf_addf(&buf, "merge-%s", strategy);
|
||||
args[i++] = buf.buf;
|
||||
for (x = 0; x < xopts_nr; x++) {
|
||||
char *s = xmalloc(strlen(xopts[x])+2+1);
|
||||
strcpy(s, "--");
|
||||
strcpy(s+2, xopts[x]);
|
||||
args[i++] = s;
|
||||
}
|
||||
for (j = common; j; j = j->next)
|
||||
args[i++] = xstrdup(merge_argument(j->item));
|
||||
args[i++] = "--";
|
||||
args[i++] = head_arg;
|
||||
for (j = remotes; j; j = j->next)
|
||||
args[i++] = xstrdup(merge_argument(j->item));
|
||||
args[i] = NULL;
|
||||
ret = run_command_v_opt(args, RUN_GIT_CMD);
|
||||
strbuf_release(&buf);
|
||||
i = 1;
|
||||
for (x = 0; x < xopts_nr; x++)
|
||||
free((void *)args[i++]);
|
||||
for (j = common; j; j = j->next)
|
||||
free((void *)args[i++]);
|
||||
i += 2;
|
||||
for (j = remotes; j; j = j->next)
|
||||
free((void *)args[i++]);
|
||||
free(args);
|
||||
discard_cache();
|
||||
if (read_cache() < 0)
|
||||
die(_("failed to read the cache"));
|
||||
resolve_undo_clear();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int checkout_fast_forward(const unsigned char *head,
|
||||
const unsigned char *remote,
|
||||
int overwrite_ignore)
|
||||
{
|
||||
struct tree *trees[MAX_UNPACK_TREES];
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc t[MAX_UNPACK_TREES];
|
||||
int i, fd, nr_trees = 0;
|
||||
struct dir_struct dir;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
fd = hold_locked_index(lock_file, 1);
|
||||
|
||||
memset(&trees, 0, sizeof(trees));
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
memset(&t, 0, sizeof(t));
|
||||
if (overwrite_ignore) {
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
dir.flags |= DIR_SHOW_IGNORED;
|
||||
setup_standard_excludes(&dir);
|
||||
opts.dir = &dir;
|
||||
}
|
||||
|
||||
opts.head_idx = 1;
|
||||
opts.src_index = &the_index;
|
||||
opts.dst_index = &the_index;
|
||||
opts.update = 1;
|
||||
opts.verbose_update = 1;
|
||||
opts.merge = 1;
|
||||
opts.fn = twoway_merge;
|
||||
setup_unpack_trees_porcelain(&opts, "merge");
|
||||
|
||||
trees[nr_trees] = parse_tree_indirect(head);
|
||||
if (!trees[nr_trees++])
|
||||
return -1;
|
||||
trees[nr_trees] = parse_tree_indirect(remote);
|
||||
if (!trees[nr_trees++])
|
||||
return -1;
|
||||
for (i = 0; i < nr_trees; i++) {
|
||||
parse_tree(trees[i]);
|
||||
init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
|
||||
}
|
||||
if (unpack_trees(nr_trees, t, &opts))
|
||||
return -1;
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
die(_("unable to write new index file"));
|
||||
return 0;
|
||||
}
|
74
notes.c
74
notes.c
@ -848,16 +848,15 @@ int combine_notes_ignore(unsigned char *cur_sha1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the lines from the named object to list, with trailing
|
||||
* newlines removed.
|
||||
*/
|
||||
static int string_list_add_note_lines(struct string_list *list,
|
||||
static int string_list_add_note_lines(struct string_list *sort_uniq_list,
|
||||
const unsigned char *sha1)
|
||||
{
|
||||
char *data;
|
||||
unsigned long len;
|
||||
enum object_type t;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct strbuf **lines = NULL;
|
||||
int i, list_index;
|
||||
|
||||
if (is_null_sha1(sha1))
|
||||
return 0;
|
||||
@ -869,14 +868,24 @@ static int string_list_add_note_lines(struct string_list *list,
|
||||
return t != OBJ_BLOB || !data;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the last line of the file is EOL-terminated, this will
|
||||
* add an empty string to the list. But it will be removed
|
||||
* later, along with any empty strings that came from empty
|
||||
* lines within the file.
|
||||
*/
|
||||
string_list_split(list, data, '\n', -1);
|
||||
free(data);
|
||||
strbuf_attach(&buf, data, len, len + 1);
|
||||
lines = strbuf_split(&buf, '\n');
|
||||
|
||||
for (i = 0; lines[i]; i++) {
|
||||
if (lines[i]->buf[lines[i]->len - 1] == '\n')
|
||||
strbuf_setlen(lines[i], lines[i]->len - 1);
|
||||
if (!lines[i]->len)
|
||||
continue; /* skip empty lines */
|
||||
list_index = string_list_find_insert_index(sort_uniq_list,
|
||||
lines[i]->buf, 0);
|
||||
if (list_index < 0)
|
||||
continue; /* skip duplicate lines */
|
||||
string_list_insert_at_index(sort_uniq_list, list_index,
|
||||
lines[i]->buf);
|
||||
}
|
||||
|
||||
strbuf_list_free(lines);
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -892,7 +901,7 @@ static int string_list_join_lines_helper(struct string_list_item *item,
|
||||
int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
|
||||
const unsigned char *new_sha1)
|
||||
{
|
||||
struct string_list sort_uniq_list = STRING_LIST_INIT_DUP;
|
||||
struct string_list sort_uniq_list = { NULL, 0, 0, 1 };
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret = 1;
|
||||
|
||||
@ -901,9 +910,6 @@ int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
|
||||
goto out;
|
||||
if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
|
||||
goto out;
|
||||
string_list_remove_empty_items(&sort_uniq_list, 0);
|
||||
sort_string_list(&sort_uniq_list);
|
||||
string_list_remove_duplicates(&sort_uniq_list, 0);
|
||||
|
||||
/* create a new blob object from sort_uniq_list */
|
||||
if (for_each_string_list(&sort_uniq_list,
|
||||
@ -943,18 +949,23 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
|
||||
void string_list_add_refs_from_colon_sep(struct string_list *list,
|
||||
const char *globs)
|
||||
{
|
||||
struct string_list split = STRING_LIST_INIT_NODUP;
|
||||
char *globs_copy = xstrdup(globs);
|
||||
struct strbuf globbuf = STRBUF_INIT;
|
||||
struct strbuf **split;
|
||||
int i;
|
||||
|
||||
string_list_split_in_place(&split, globs_copy, ':', -1);
|
||||
string_list_remove_empty_items(&split, 0);
|
||||
strbuf_addstr(&globbuf, globs);
|
||||
split = strbuf_split(&globbuf, ':');
|
||||
|
||||
for (i = 0; i < split.nr; i++)
|
||||
string_list_add_refs_by_glob(list, split.items[i].string);
|
||||
for (i = 0; split[i]; i++) {
|
||||
if (!split[i]->len)
|
||||
continue;
|
||||
if (split[i]->buf[split[i]->len-1] == ':')
|
||||
strbuf_setlen(split[i], split[i]->len-1);
|
||||
string_list_add_refs_by_glob(list, split[i]->buf);
|
||||
}
|
||||
|
||||
string_list_clear(&split, 0);
|
||||
free(globs_copy);
|
||||
strbuf_list_free(split);
|
||||
strbuf_release(&globbuf);
|
||||
}
|
||||
|
||||
static int notes_display_config(const char *k, const char *v, void *cb)
|
||||
@ -1193,11 +1204,10 @@ void free_notes(struct notes_tree *t)
|
||||
* If the given notes_tree is NULL, the internal/default notes_tree will be
|
||||
* used instead.
|
||||
*
|
||||
* (raw != 0) gives the %N userformat; otherwise, the note message is given
|
||||
* for human consumption.
|
||||
* 'flags' is a bitwise combination of the flags for format_display_notes.
|
||||
*/
|
||||
static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
|
||||
struct strbuf *sb, const char *output_encoding, int raw)
|
||||
struct strbuf *sb, const char *output_encoding, int flags)
|
||||
{
|
||||
static const char utf8[] = "utf-8";
|
||||
const unsigned char *sha1;
|
||||
@ -1234,7 +1244,7 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
|
||||
if (msglen && msg[msglen - 1] == '\n')
|
||||
msglen--;
|
||||
|
||||
if (!raw) {
|
||||
if (flags & NOTES_SHOW_HEADER) {
|
||||
const char *ref = t->ref;
|
||||
if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
|
||||
strbuf_addstr(sb, "\nNotes:\n");
|
||||
@ -1250,7 +1260,7 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
|
||||
for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
|
||||
linelen = strchrnul(msg_p, '\n') - msg_p;
|
||||
|
||||
if (!raw)
|
||||
if (flags & NOTES_INDENT)
|
||||
strbuf_addstr(sb, " ");
|
||||
strbuf_add(sb, msg_p, linelen);
|
||||
strbuf_addch(sb, '\n');
|
||||
@ -1260,13 +1270,13 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
|
||||
}
|
||||
|
||||
void format_display_notes(const unsigned char *object_sha1,
|
||||
struct strbuf *sb, const char *output_encoding, int raw)
|
||||
struct strbuf *sb, const char *output_encoding, int flags)
|
||||
{
|
||||
int i;
|
||||
assert(display_notes_trees);
|
||||
for (i = 0; display_notes_trees[i]; i++)
|
||||
format_note(display_notes_trees[i], object_sha1, sb,
|
||||
output_encoding, raw);
|
||||
output_encoding, flags);
|
||||
}
|
||||
|
||||
int copy_note(struct notes_tree *t,
|
||||
|
6
notes.h
6
notes.h
@ -237,6 +237,10 @@ void prune_notes(struct notes_tree *t, int flags);
|
||||
*/
|
||||
void free_notes(struct notes_tree *t);
|
||||
|
||||
/* Flags controlling how notes are formatted */
|
||||
#define NOTES_SHOW_HEADER 1
|
||||
#define NOTES_INDENT 2
|
||||
|
||||
struct string_list;
|
||||
|
||||
struct display_notes_opt {
|
||||
@ -270,7 +274,7 @@ void init_display_notes(struct display_notes_opt *opt);
|
||||
* You *must* call init_display_notes() before using this function.
|
||||
*/
|
||||
void format_display_notes(const unsigned char *object_sha1,
|
||||
struct strbuf *sb, const char *output_encoding, int raw);
|
||||
struct strbuf *sb, const char *output_encoding, int flags);
|
||||
|
||||
/*
|
||||
* Load the notes tree from each ref listed in 'refs'. The output is
|
||||
|
52
pager.c
52
pager.c
@ -6,17 +6,26 @@
|
||||
#define DEFAULT_PAGER "less"
|
||||
#endif
|
||||
|
||||
struct pager_config {
|
||||
const char *cmd;
|
||||
int want;
|
||||
char *value;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is split up from the rest of git so that we can do
|
||||
* something different on Windows.
|
||||
*/
|
||||
|
||||
#ifndef WIN32
|
||||
static void pager_preexec(void)
|
||||
{
|
||||
/*
|
||||
* Work around bug in "less" by not starting it until we
|
||||
* have real input
|
||||
*/
|
||||
fd_set in;
|
||||
|
||||
FD_ZERO(&in);
|
||||
FD_SET(0, &in);
|
||||
select(1, &in, NULL, &in, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *pager_argv[] = { NULL, NULL };
|
||||
static struct child_process pager_process;
|
||||
|
||||
@ -84,6 +93,9 @@ void setup_pager(void)
|
||||
static const char *env[] = { "LESS=FRSX", NULL };
|
||||
pager_process.env = env;
|
||||
}
|
||||
#ifndef WIN32
|
||||
pager_process.preexec_cb = pager_preexec;
|
||||
#endif
|
||||
if (start_command(&pager_process))
|
||||
return;
|
||||
|
||||
@ -147,31 +159,3 @@ int decimal_width(int number)
|
||||
i *= 10;
|
||||
return width;
|
||||
}
|
||||
|
||||
static int pager_command_config(const char *var, const char *value, void *data)
|
||||
{
|
||||
struct pager_config *c = data;
|
||||
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
|
||||
int b = git_config_maybe_bool(var, value);
|
||||
if (b >= 0)
|
||||
c->want = b;
|
||||
else {
|
||||
c->want = 1;
|
||||
c->value = xstrdup(value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
|
||||
int check_pager_config(const char *cmd)
|
||||
{
|
||||
struct pager_config c;
|
||||
c.cmd = cmd;
|
||||
c.want = -1;
|
||||
c.value = NULL;
|
||||
git_config(pager_command_config, &c);
|
||||
if (c.value)
|
||||
pager_program = c.value;
|
||||
return c.want;
|
||||
}
|
||||
|
1272
po/git.pot
1272
po/git.pot
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user