mirror of
https://github.com/git/git.git
synced 2026-03-13 18:33:25 +01:00
Merge commit 'mingw/master' into devel
Conflicts: Makefile git-compat-util.h help.c t/t5505-remote.sh
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
GIT-BUILD-OPTIONS
|
||||
GIT-CFLAGS
|
||||
GIT-GUI-VARS
|
||||
GIT-VERSION-FILE
|
||||
|
||||
@@ -53,6 +53,18 @@ For shell scripts specifically (not exhaustive):
|
||||
- We do not write the noiseword "function" in front of shell
|
||||
functions.
|
||||
|
||||
- As to use of grep, stick to a subset of BRE (namely, no \{m,n\},
|
||||
[::], [==], nor [..]) for portability.
|
||||
|
||||
- We do not use \{m,n\};
|
||||
|
||||
- We do not use -E;
|
||||
|
||||
- We do not use ? nor + (which are \{0,1\} and \{1,\}
|
||||
respectively in BRE) but that goes without saying as these
|
||||
are ERE elements not BRE (note that \? and \+ are not even part
|
||||
of BRE -- making them accessible from BRE is a GNU extension).
|
||||
|
||||
For C programs:
|
||||
|
||||
- We use tabs to indent, and interpret tabs as taking up to
|
||||
|
||||
27
Documentation/RelNotes-1.5.4.3.txt
Normal file
27
Documentation/RelNotes-1.5.4.3.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
GIT v1.5.4.3 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.4.2
|
||||
--------------------
|
||||
|
||||
* RPM spec used to pull in everything with 'git'. This has been
|
||||
changed so that 'git' package contains just the core parts,
|
||||
and we now supply 'git-all' metapackage to slurp in everything.
|
||||
This should match end user's expectation better.
|
||||
|
||||
* When some refs failed to update, git-push reported "failure"
|
||||
which was unclear if some other refs were updated or all of
|
||||
them failed atomically (the answer is the former). Reworded
|
||||
the message to clarify this.
|
||||
|
||||
* "git clone" from a repository whose HEAD was misconfigured
|
||||
did not set up the remote properly. Now it tries to do
|
||||
better.
|
||||
|
||||
* Updated git-push documentation to clarify what "matching"
|
||||
means, in order to reduce user confusion.
|
||||
|
||||
* Updated git-add documentation to clarify "add -u" operates in
|
||||
the current subdirectory you are in, just like other commands.
|
||||
|
||||
* git-gui updates to work on OSX and Windows better.
|
||||
66
Documentation/RelNotes-1.5.4.4.txt
Normal file
66
Documentation/RelNotes-1.5.4.4.txt
Normal file
@@ -0,0 +1,66 @@
|
||||
GIT v1.5.4.4 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.4.3
|
||||
--------------------
|
||||
|
||||
* Building and installing with an overtight umask such as 077 made
|
||||
installed templates unreadable by others, while the rest of the install
|
||||
are done in a way that is friendly to umask 022.
|
||||
|
||||
* "git cvsexportcommit -w $cvsdir" misbehaved when GIT_DIR is set to a
|
||||
relative directory.
|
||||
|
||||
* "git http-push" had an invalid memory access that could lead it to
|
||||
segfault.
|
||||
|
||||
* When "git rebase -i" gave control back to the user for a commit that is
|
||||
marked to be edited, it just said "modify it with commit --amend",
|
||||
without saying what to do to continue after modifying it. Give an
|
||||
explicit instruction to run "rebase --continue" to be more helpful.
|
||||
|
||||
* "git send-email" in 1.5.4.3 issued a bogus empty In-Reply-To: header.
|
||||
|
||||
* "git bisect" showed mysterious "won't bisect on seeked tree" error message.
|
||||
This was leftover from Cogito days to prevent "bisect" starting from a
|
||||
cg-seeked state. We still keep the Cogito safety, but running "git bisect
|
||||
start" when another bisect was in effect will clean up and start over.
|
||||
|
||||
* "git push" with an explicit PATH to receive-pack did not quite work if
|
||||
receive-pack was not on usual PATH. We earlier fixed the same issue
|
||||
with "git fetch" and upload-pack, but somehow forgot to do so in the
|
||||
other direction.
|
||||
|
||||
* git-gui's info dialog was not displayed correctly when the user tries
|
||||
to commit nothing (i.e. without staging anything).
|
||||
|
||||
* "git revert" did not properly fail when attempting to run with a
|
||||
dirty index.
|
||||
|
||||
* "git merge --no-commit --no-ff <other>" incorrectly made commits.
|
||||
|
||||
* "git merge --squash --no-ff <other>", which is a nonsense combination
|
||||
of options, was not rejected.
|
||||
|
||||
* "git ls-remote" and "git remote show" against an empty repository
|
||||
failed, instead of just giving an empty result (regression).
|
||||
|
||||
* "git fast-import" did not handle a renamed path whose name needs to be
|
||||
quoted, due to a bug in unquote_c_style() function.
|
||||
|
||||
* "git cvsexportcommit" was confused when multiple files with the same
|
||||
basename needed to be pushed out in the same commit.
|
||||
|
||||
* "git daemon" did not send early errors to syslog.
|
||||
|
||||
* "git log --merge" did not work well with --left-right option.
|
||||
|
||||
* "git svn" promprted for client cert password every time it accessed the
|
||||
server.
|
||||
|
||||
* The reset command in "git fast-import" data stream was documented to
|
||||
end with an optional LF, but it actually required one.
|
||||
|
||||
* "git svn dcommit/rebase" did not honor --rewrite-root option.
|
||||
|
||||
Also included are a handful documentation updates.
|
||||
53
Documentation/RelNotes-1.5.4.5.txt
Normal file
53
Documentation/RelNotes-1.5.4.5.txt
Normal file
@@ -0,0 +1,53 @@
|
||||
GIT v1.5.4.5 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.4.4
|
||||
--------------------
|
||||
|
||||
* You couldn't specify a custom editor whose path contains a whitespace
|
||||
via GIT_EDITOR (and core.editor).
|
||||
|
||||
* The subdirectory filter to "git filter-branch" mishandled a history
|
||||
where the subdirectory becomes empty and then later becomes non-empty.
|
||||
|
||||
* "git shortlog" gave an empty line if the original commit message was
|
||||
malformed (e.g. a botched import from foreign SCM). Now it finds the
|
||||
first non-empty line and uses it for better information.
|
||||
|
||||
* When the user fails to give a revision parameter to "git svn", an error
|
||||
from the Perl interpreter was issued because the script lacked proper
|
||||
error checking.
|
||||
|
||||
* After "git rebase" stopped due to conflicts, if the user played with
|
||||
"git reset" and friends, "git rebase --abort" failed to go back to the
|
||||
correct commit.
|
||||
|
||||
* Additional work trees prepared with git-new-workdir (in contrib/) did
|
||||
not share git-svn metadata directory .git/svn with the original.
|
||||
|
||||
* "git-merge-recursive" did not mark addition of the same path with
|
||||
different filemodes correctly as a conflict.
|
||||
|
||||
* "gitweb" gave malformed URL when pathinfo stype paths are in use.
|
||||
|
||||
* "-n" stands for "--no-tags" again for "git fetch".
|
||||
|
||||
* "git format-patch" did not detect the need to add 8-bit MIME header
|
||||
when the user used format.header configuration.
|
||||
|
||||
* "rev~" revision specifier used to mean "rev", which was inconsistent
|
||||
with how "rev^" worked. Now "rev~" is the same as "rev~1" (hence it
|
||||
also is the same as "rev^1"), and "rev~0" is the same as "rev^0"
|
||||
(i.e. it has to be a commit).
|
||||
|
||||
* "git quiltimport" did not grok empty lines, lines in "file -pNNN"
|
||||
format to specify the prefix levels and lines with trailing comments.
|
||||
|
||||
* "git rebase -m" triggered pre-commit verification, which made
|
||||
"rebase --continue" impossible.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
echo O=$(git describe maint)
|
||||
O=v1.5.4.4-25-ga6f7728
|
||||
git shortlog --no-merges $O..maint
|
||||
@@ -4,6 +4,19 @@ GIT v1.5.5 Release Notes
|
||||
Updates since v1.5.4
|
||||
--------------------
|
||||
|
||||
(subsystems)
|
||||
|
||||
* Comes with git-gui 0.9.3.
|
||||
|
||||
(portability)
|
||||
|
||||
* We shouldn't ask for BSD group ownership semantics by setting g+s bit
|
||||
on directories on older BSD systems that refuses chmod() by non root
|
||||
users. BSD semantics is the default there anyway.
|
||||
|
||||
* Bunch of portability improvement patches coming from an effort to port
|
||||
to Solaris has been applied.
|
||||
|
||||
(performance)
|
||||
|
||||
* On platforms with suboptimal qsort(3) implementation, there
|
||||
@@ -23,33 +36,132 @@ Updates since v1.5.4
|
||||
|
||||
(usability, bells and whistles)
|
||||
|
||||
* Bash completion script (in contrib) are aware of more commands and
|
||||
options.
|
||||
|
||||
* You can be warned when core.autocrlf conversion is applied in
|
||||
such a way that results in an irreversible conversion.
|
||||
|
||||
* A catch-all "color.ui" configuration variable can be used to
|
||||
enable coloring of all color-capable commands, instead of
|
||||
individual ones such as "color.status" and "color.branch".
|
||||
|
||||
* The commands refused to take absolute pathnames where they
|
||||
require pathnames relative to the work tree or the current
|
||||
subdirectory. They now can take absolute pathnames in such a
|
||||
case as long as the pathnames do not refer outside of the
|
||||
work tree. E.g. "git add $(pwd)/foo" now works.
|
||||
|
||||
* Error messages used to be sent to stderr, only to get hidden,
|
||||
when $PAGER was in use. They now are sent to stdout along
|
||||
with the command output to be shown in the $PAGER.
|
||||
|
||||
* A pattern "foo/" in .gitignore file now matches a directory
|
||||
"foo". Pattern "foo" also matches as before.
|
||||
|
||||
* bash completion's prompt helper function can talk about
|
||||
operation in-progress (e.g. merge, rebase, etc.).
|
||||
|
||||
* Configuration variables "url.<usethis>.insteadof = <otherurl>" can be
|
||||
used to tell "git-fetch" and "git-push" to use different URL than what
|
||||
is given from the command line.
|
||||
|
||||
* "git add -i" behaves better even before you make an initial commit.
|
||||
|
||||
* "git am" refused to run from a subdirectory without a good reason.
|
||||
|
||||
* After "git apply --whitespace=fix" fixes whitespace errors in a patch,
|
||||
a line before the fix can appear as a context or preimage line in a
|
||||
later patch, causing the patch not to apply. The command now knows to
|
||||
see through whitespace fixes done to context lines to successfully
|
||||
apply such a patch series.
|
||||
|
||||
* "git branch" (and "git checkout -b") to branch from a local branch can
|
||||
optionally set "branch.<name>.merge" to mark the new branch to build on
|
||||
the other local branch, when "branch.autosetupmerge" is set to
|
||||
"always". By default, this does not happen when branching from a local
|
||||
branch.
|
||||
|
||||
* "git checkout" to switch to a branch that has "branch.<name>.merge" set
|
||||
(i.e. marked to build on another branch) reports how much the branch
|
||||
and the other branch diverged.
|
||||
|
||||
* When "git checkout" has to update a lot of paths, it used to be silent
|
||||
for 4 seconds before it showed any progress report. It is now a bit
|
||||
more impatient and starts showing progress report early.
|
||||
|
||||
* "git commit" learned a new hook "prepare-commit-msg" that can
|
||||
inspect what is going to be committed and prepare the commit
|
||||
log message template to be edited.
|
||||
|
||||
* "git cvsimport" can now take more than one -M options.
|
||||
|
||||
* "git describe" learned to limit the tags to be used for
|
||||
naming with --match option.
|
||||
|
||||
* "git describe --contains" now barfs when the named commit
|
||||
cannot be described.
|
||||
|
||||
* bash completion's prompt helper function can talk about
|
||||
operation in-progress (e.g. merge, rebase, etc.).
|
||||
* "git describe --exact-match" describes only commits that are tagged.
|
||||
|
||||
* "git commit" learned a new hook "prepare-commit-msg" that can
|
||||
inspect what is going to be committed and prepare the commit
|
||||
log message template to be edited.
|
||||
* "git describe --long" describes a tagged commit as $tag-0-$sha1,
|
||||
instead of just showing the exact tagname.
|
||||
|
||||
* "git describe" warns when using a tag whose name and path contradict
|
||||
with each other.
|
||||
|
||||
* "git diff" learned "--relative" option to limit and output paths
|
||||
relative to the current directory when working in a subdirectory.
|
||||
|
||||
* "git diff" learned "--dirstat" option to show birds-eye-summary of
|
||||
changes more concisely than "--diffstat".
|
||||
|
||||
* "git format-patch" learned --cover-letter option to generate a cover
|
||||
letter template.
|
||||
|
||||
* "git gc" learned --quiet option.
|
||||
|
||||
* "git gc" now automatically prunes unreachable objects that are two
|
||||
weeks old or older.
|
||||
|
||||
* "git grep" now knows "--name-only" is a synonym for the "-l" option.
|
||||
|
||||
* "git help <alias>" now reports "'git <alias>' is alias to <what>",
|
||||
instead of saying "No manual entry for git-<alias>".
|
||||
|
||||
* "git help" can use different backends to show manual pages and this can
|
||||
be configured using "man.viewer" configuration.
|
||||
|
||||
* "gitk" does not restore window position from $HOME/.gitk anymore (it
|
||||
still restores the size).
|
||||
|
||||
* "git log --grep=<what>" learned "--fixed-strings" option to look for
|
||||
<what> without treating it as a regular expression.
|
||||
|
||||
* "git gui" learned an auto-spell checking.
|
||||
|
||||
* "git push <somewhere> HEAD" and "git push <somewhere> +HEAD" works as
|
||||
expected; they push the current branch (and only the current branch).
|
||||
In addition, HEAD can be written as the value of "remote.<there>.push"
|
||||
configuration variable.
|
||||
|
||||
* When the configuration variable "pack.threads" is set to 0, "git
|
||||
repack" auto detects the number of CPUs and uses that many threads.
|
||||
|
||||
* "git send-email" learned to prompt for passwords
|
||||
interactively.
|
||||
|
||||
* "git send-email" learned an easier way to suppress CC
|
||||
recipients.
|
||||
|
||||
* "git stash" learned "pop" command, that applies the latest stash and
|
||||
removes it from the stash, and "drop" command to discard the named
|
||||
stash entry.
|
||||
|
||||
* "git submodule" learned a new subcommand "summary" to show the
|
||||
symmetric difference between the HEAD version and the work tree version
|
||||
of the submodule commits.
|
||||
|
||||
* Various "git cvsimport", "git cvsexportcommit", "git svn" and
|
||||
"git p4" improvements.
|
||||
|
||||
@@ -61,6 +173,19 @@ Updates since v1.5.4
|
||||
* It is now easier to write test scripts that records known
|
||||
breakages.
|
||||
|
||||
* "git checkout" is rewritten in C.
|
||||
|
||||
* "git remote" is rewritten in C.
|
||||
|
||||
* Two conflict hunks that are separated by a very short span of common
|
||||
lines are now coalesced into one larger hunk, to make the result easier
|
||||
to read.
|
||||
|
||||
* Run-command API's use of file descriptors is documented clearer and
|
||||
is more consistent now.
|
||||
|
||||
* diff output can be sent to FILE * that is different from stdout. This
|
||||
will help reimplementing more things in C.
|
||||
|
||||
Fixes since v1.5.4
|
||||
------------------
|
||||
@@ -68,11 +193,14 @@ Fixes since v1.5.4
|
||||
All of the fixes in v1.5.4 maintenance series are included in
|
||||
this release, unless otherwise noted.
|
||||
|
||||
* "git-http-push" did not allow deletion of remote ref with the usual
|
||||
"push <remote> :<branch>" syntax.
|
||||
|
||||
* "git-rebase --abort" did not go back to the right location if
|
||||
"git-reset" was run during the "git-rebase" session.
|
||||
|
||||
---
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.4
|
||||
O=v1.5.4.2-122-g7cb97da
|
||||
O=v1.5.4.4-620-gc817faa
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
|
||||
|
||||
@@ -353,6 +353,10 @@ core.whitespace::
|
||||
error (enabled by default).
|
||||
* `indent-with-non-tab` treats a line that is indented with 8 or more
|
||||
space characters as an error (not enabled by default).
|
||||
* `cr-at-eol` treats a carriage-return at the end of line as
|
||||
part of the line terminator, i.e. with it, `trailing-space`
|
||||
does not trigger if the character before such a carriage-return
|
||||
is not a whitespace (not enabled by default).
|
||||
|
||||
alias.*::
|
||||
Command aliases for the linkgit:git[1] command wrapper - e.g.
|
||||
@@ -375,10 +379,14 @@ apply.whitespace::
|
||||
|
||||
branch.autosetupmerge::
|
||||
Tells `git-branch` and `git-checkout` to setup new branches
|
||||
so that linkgit:git-pull[1] will appropriately merge from that
|
||||
remote branch. Note that even if this option is not set,
|
||||
so that linkgit:git-pull[1] will appropriately merge from the
|
||||
starting point branch. Note that even if this option is not set,
|
||||
this behavior can be chosen per-branch using the `--track`
|
||||
and `--no-track` options. This option defaults to true.
|
||||
and `--no-track` options. The valid settings are: `false` -- no
|
||||
automatic setup is done; `true` -- automatic setup is done when the
|
||||
starting point is a remote branch; `always` -- automatic setup is
|
||||
done when the starting point is either a local branch or remote
|
||||
branch. This option defaults to true.
|
||||
|
||||
branch.<name>.remote::
|
||||
When in branch <name>, it tells `git fetch` which remote to fetch.
|
||||
@@ -412,6 +420,11 @@ branch.<name>.rebase::
|
||||
it unless you understand the implications (see linkgit:git-rebase[1]
|
||||
for details).
|
||||
|
||||
browser.<tool>.cmd::
|
||||
Specify the command to invoke the specified browser. The
|
||||
specified command is evaluated in shell with the URLs passed
|
||||
as arguments. (See linkgit:git-web--browse[1].)
|
||||
|
||||
browser.<tool>.path::
|
||||
Override the path for the given tool that may be used to
|
||||
browse HTML help (see '-w' option in linkgit:git-help[1]) or a
|
||||
@@ -548,6 +561,11 @@ format.suffix::
|
||||
`.patch`. Use this variable to change that suffix (make sure to
|
||||
include the dot if you want it).
|
||||
|
||||
format.pretty::
|
||||
The default pretty format for log/show/whatchanged command,
|
||||
See linkgit:git-log[1], linkgit:git-show[1],
|
||||
linkgit:git-whatchanged[1].
|
||||
|
||||
gc.aggressiveWindow::
|
||||
The window size parameter used in the delta compression
|
||||
algorithm used by 'git gc --aggressive'. This defaults
|
||||
@@ -577,6 +595,10 @@ gc.packrefs::
|
||||
at some stage, and setting this to `false` will continue to
|
||||
prevent `git pack-refs` from being run from `git gc`.
|
||||
|
||||
gc.pruneexpire::
|
||||
When `git gc` is run, it will call `prune --expire 2.weeks.ago`.
|
||||
Override the grace period with this config variable.
|
||||
|
||||
gc.reflogexpire::
|
||||
`git reflog expire` removes reflog entries older than
|
||||
this time; defaults to 90 days.
|
||||
@@ -735,14 +757,20 @@ log.showroot::
|
||||
Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
|
||||
normally hide the root commit will now show it. True by default.
|
||||
|
||||
man.viewer::
|
||||
Specify the programs that may be used to display help in the
|
||||
'man' format. See linkgit:git-help[1].
|
||||
|
||||
merge.summary::
|
||||
Whether to include summaries of merged commits in newly created
|
||||
merge commit messages. False by default.
|
||||
|
||||
merge.tool::
|
||||
Controls which merge resolution program is used by
|
||||
linkgit:git-mergetool[1]. Valid values are: "kdiff3", "tkdiff",
|
||||
"meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
|
||||
linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3",
|
||||
"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
|
||||
"opendiff". Any other value is treated is custom merge tool
|
||||
and there must be a corresponing mergetool.<tool>.cmd option.
|
||||
|
||||
merge.verbosity::
|
||||
Controls the amount of output shown by the recursive merge
|
||||
@@ -769,6 +797,31 @@ mergetool.<tool>.path::
|
||||
Override the path for the given tool. This is useful in case
|
||||
your tool is not in the PATH.
|
||||
|
||||
mergetool.<tool>.cmd::
|
||||
Specify the command to invoke the specified merge tool. The
|
||||
specified command is evaluated in shell with the following
|
||||
variables available: 'BASE' is the name of a temporary file
|
||||
containing the common base of the files to be merged, if available;
|
||||
'LOCAL' is the name of a temporary file containing the contents of
|
||||
the file on the current branch; 'REMOTE' is the name of a temporary
|
||||
file containing the contents of the file from the branch being
|
||||
merged; 'MERGED' contains the name of the file to which the merge
|
||||
tool should write the results of a successful merge.
|
||||
|
||||
mergetool.<tool>.trustExitCode::
|
||||
For a custom merge command, specify whether the exit code of
|
||||
the merge command can be used to determine whether the merge was
|
||||
successful. If this is not set to true then the merge target file
|
||||
timestamp is checked and the merge assumed to have been successful
|
||||
if the file has been updated, otherwise the user is prompted to
|
||||
indicate the success of the merge.
|
||||
|
||||
mergetool.keepBackup::
|
||||
After performing a merge, the original file with conflict markers
|
||||
can be saved as a file with a `.orig` extension. If this variable
|
||||
is set to `false` then this file is not preserved. Defaults to
|
||||
`true` (i.e. keep the backup files).
|
||||
|
||||
pack.window::
|
||||
The size of the window used by linkgit:git-pack-objects[1] when no
|
||||
window size is given on the command line. Defaults to 10.
|
||||
@@ -808,6 +861,8 @@ pack.threads::
|
||||
warning. This is meant to reduce packing time on multiprocessor
|
||||
machines. The required amount of memory for the delta search window
|
||||
is however multiplied by the number of threads.
|
||||
Specifying 0 will cause git to auto-detect the number of CPU's
|
||||
and set the number of threads accordingly.
|
||||
|
||||
pack.indexVersion::
|
||||
Specify the default pack index version. Valid values are 1 for
|
||||
@@ -818,7 +873,7 @@ pack.indexVersion::
|
||||
whenever the corresponding pack is larger than 2 GB. Otherwise
|
||||
the default is 1.
|
||||
|
||||
pack.packSizeLimit:
|
||||
pack.packSizeLimit::
|
||||
The default maximum size of a pack. This setting only affects
|
||||
packing to a file, i.e. the git:// protocol is unaffected. It
|
||||
can be overridden by the `\--max-pack-size` option of
|
||||
@@ -854,15 +909,15 @@ remote.<name>.skipDefaultUpdate::
|
||||
|
||||
remote.<name>.receivepack::
|
||||
The default program to execute on the remote side when pushing. See
|
||||
option \--exec of linkgit:git-push[1].
|
||||
option \--receive-pack of linkgit:git-push[1].
|
||||
|
||||
remote.<name>.uploadpack::
|
||||
The default program to execute on the remote side when fetching. See
|
||||
option \--exec of linkgit:git-fetch-pack[1].
|
||||
option \--upload-pack of linkgit:git-fetch-pack[1].
|
||||
|
||||
remote.<name>.tagopt::
|
||||
Setting this value to --no-tags disables automatic tag following when fetching
|
||||
from remote <name>
|
||||
Setting this value to \--no-tags disables automatic tag following when
|
||||
fetching from remote <name>
|
||||
|
||||
remotes.<group>::
|
||||
The list of remotes which are fetched by "git remote update
|
||||
@@ -893,6 +948,17 @@ tar.umask::
|
||||
archiving user's umask will be used instead. See umask(2) and
|
||||
linkgit:git-archive[1].
|
||||
|
||||
url.<base>.insteadOf::
|
||||
Any URL that starts with this value will be rewritten to
|
||||
start, instead, with <base>. In cases where some site serves a
|
||||
large number of repositories, and serves them with multiple
|
||||
access methods, and some users need to use different access
|
||||
methods, this feature allows people to specify any of the
|
||||
equivalent URLs and have git automatically rewrite the URL to
|
||||
the best alternative for the particular user, even for a
|
||||
never-before-seen repository on the site. When more than one
|
||||
insteadOf strings match a given URL, the longest match is used.
|
||||
|
||||
user.email::
|
||||
Your email address to be recorded in any newly created commits.
|
||||
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
|
||||
|
||||
@@ -170,6 +170,14 @@ endif::git-format-patch[]
|
||||
Swap two inputs; that is, show differences from index or
|
||||
on-disk file to tree contents.
|
||||
|
||||
--relative[=<path>]::
|
||||
When run from a subdirectory of the project, it can be
|
||||
told to exclude changes outside the directory and show
|
||||
pathnames relative to it with this option. When you are
|
||||
not in a subdirectory (e.g. in a bare repository), you
|
||||
can name which subdirectory to make the output relative
|
||||
to by giving a <path> as an argument.
|
||||
|
||||
--text::
|
||||
Treat all files as text.
|
||||
|
||||
|
||||
@@ -207,16 +207,14 @@ patch::
|
||||
and the working tree file and asks you if you want to stage
|
||||
the change of each hunk. You can say:
|
||||
|
||||
y - add the change from that hunk to index
|
||||
n - do not add the change from that hunk to index
|
||||
a - add the change from that hunk and all the rest to index
|
||||
d - do not the change from that hunk nor any of the rest to index
|
||||
j - do not decide on this hunk now, and view the next
|
||||
undecided hunk
|
||||
J - do not decide on this hunk now, and view the next hunk
|
||||
k - do not decide on this hunk now, and view the previous
|
||||
undecided hunk
|
||||
K - do not decide on this hunk now, and view the previous hunk
|
||||
y - stage this hunk
|
||||
n - do not stage this hunk
|
||||
a - stage this and all the remaining hunks in the file
|
||||
d - do not stage this hunk nor any of the remaining hunks in the file
|
||||
j - leave this hunk undecided, see next undecided hunk
|
||||
J - leave this hunk undecided, see next hunk
|
||||
k - leave this hunk undecided, see previous undecided hunk
|
||||
K - leave this hunk undecided, see previous hunk
|
||||
s - split the current hunk into smaller hunks
|
||||
? - print help
|
||||
+
|
||||
|
||||
@@ -9,7 +9,7 @@ git-am - Apply a series of patches from a mailbox
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
|
||||
'git-am' [--signoff] [--keep] [--utf8 | --no-utf8]
|
||||
[--3way] [--interactive] [--binary]
|
||||
[--whitespace=<option>] [-C<n>] [-p<n>]
|
||||
<mbox>|<Maildir>...
|
||||
@@ -32,10 +32,6 @@ OPTIONS
|
||||
Add `Signed-off-by:` line to the commit message, using
|
||||
the committer identity of yourself.
|
||||
|
||||
-d=<dir>, --dotest=<dir>::
|
||||
Instead of `.dotest` directory, use <dir> as a working
|
||||
area to store extracted patches.
|
||||
|
||||
-k, --keep::
|
||||
Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
|
||||
|
||||
@@ -138,7 +134,7 @@ aborts in the middle,. You can recover from this in one of two ways:
|
||||
|
||||
The command refuses to process new mailboxes while `.dotest`
|
||||
directory exists, so if you decide to start over from scratch,
|
||||
run `rm -f .dotest` before running the command with mailbox
|
||||
run `rm -f -r .dotest` before running the command with mailbox
|
||||
names.
|
||||
|
||||
|
||||
|
||||
@@ -35,11 +35,10 @@ working tree to it; use "git checkout <newbranch>" to switch to the
|
||||
new branch.
|
||||
|
||||
When a local branch is started off a remote branch, git sets up the
|
||||
branch so that linkgit:git-pull[1] will appropriately merge from that
|
||||
remote branch. If this behavior is not desired, it is possible to
|
||||
disable it using the global `branch.autosetupmerge` configuration
|
||||
flag. That setting can be overridden by using the `--track`
|
||||
and `--no-track` options.
|
||||
branch so that linkgit:git-pull[1] will appropriately merge from
|
||||
the remote branch. This behavior may be changed via the global
|
||||
`branch.autosetupmerge` configuration flag. That setting can be
|
||||
overridden by using the `--track` and `--no-track` options.
|
||||
|
||||
With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
|
||||
If <oldbranch> had a corresponding reflog, it is renamed to match
|
||||
@@ -105,20 +104,19 @@ OPTIONS
|
||||
Display the full sha1s in output listing rather than abbreviating them.
|
||||
|
||||
--track::
|
||||
Set up configuration so that git-pull will automatically
|
||||
retrieve data from the remote branch. Use this if you always
|
||||
pull from the same remote branch into the new branch, or if you
|
||||
don't want to use "git pull <repository> <refspec>" explicitly.
|
||||
This behavior is the default. Set the
|
||||
branch.autosetupmerge configuration variable to false if you
|
||||
want git-checkout and git-branch to always behave as if
|
||||
'--no-track' were given.
|
||||
When creating a new branch, set up configuration so that git-pull
|
||||
will automatically retrieve data from the start point, which must be
|
||||
a branch. Use this if you always pull from the same upstream branch
|
||||
into the new branch, and if you don't want to use "git pull
|
||||
<repository> <refspec>" explicitly. This behavior is the default
|
||||
when the start point is a remote branch. Set the
|
||||
branch.autosetupmerge configuration variable to `false` if you want
|
||||
git-checkout and git-branch to always behave as if '--no-track' were
|
||||
given. Set it to `always` if you want this behavior when the
|
||||
start-point is either a local or remote branch.
|
||||
|
||||
--no-track::
|
||||
When a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will not retrieve data
|
||||
from the remote branch, ignoring the branch.autosetupmerge
|
||||
configuration variable.
|
||||
Ignore the branch.autosetupmerge configuration variable.
|
||||
|
||||
<branchname>::
|
||||
The name of the branch to create or delete.
|
||||
|
||||
@@ -99,36 +99,62 @@ Assume two repositories exist as R1 on machine A, and R2 on machine B.
|
||||
For whatever reason, direct connection between A and B is not allowed,
|
||||
but we can move data from A to B via some mechanism (CD, email, etc).
|
||||
We want to update R2 with developments made on branch master in R1.
|
||||
|
||||
To create the bundle you have to specify the basis. You have some options:
|
||||
|
||||
- Without basis.
|
||||
+
|
||||
This is useful when sending the whole history.
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master
|
||||
------------
|
||||
|
||||
- Using temporally tags.
|
||||
+
|
||||
We set a tag in R1 (lastR2bundle) after the previous such transport,
|
||||
and move it afterwards to help build the bundle.
|
||||
|
||||
in R1 on A:
|
||||
|
||||
------------
|
||||
$ git-bundle create mybundle master ^lastR2bundle
|
||||
$ git tag -f lastR2bundle master
|
||||
------------
|
||||
|
||||
(move mybundle from A to B by some mechanism)
|
||||
- Using a tag present in both repositories
|
||||
|
||||
in R2 on B:
|
||||
------------
|
||||
$ git bundle create mybundle master ^v1.0.0
|
||||
------------
|
||||
|
||||
- A basis based on time.
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master --since=10.days.ago
|
||||
------------
|
||||
|
||||
- With a limit on the number of commits
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master -n 10
|
||||
------------
|
||||
|
||||
Then you move mybundle from A to B, and in R2 on B:
|
||||
|
||||
------------
|
||||
$ git-bundle verify mybundle
|
||||
$ git-fetch mybundle refspec
|
||||
$ git-fetch mybundle master:localRef
|
||||
------------
|
||||
|
||||
where refspec is refInBundle:localRef
|
||||
|
||||
|
||||
Also, with something like this in your config:
|
||||
With something like this in the config in R2:
|
||||
|
||||
------------------------
|
||||
[remote "bundle"]
|
||||
url = /home/me/tmp/file.bdl
|
||||
fetch = refs/heads/*:refs/remotes/origin/*
|
||||
------------------------
|
||||
|
||||
You can first sneakernet the bundle file to ~/tmp/file.bdl and
|
||||
then these commands:
|
||||
then these commands on machine B:
|
||||
|
||||
------------
|
||||
$ git ls-remote bundle
|
||||
|
||||
@@ -48,21 +48,19 @@ OPTIONS
|
||||
may restrict the characters allowed in a branch name.
|
||||
|
||||
--track::
|
||||
When -b is given and a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will automatically
|
||||
retrieve data from the remote branch. Use this if you always
|
||||
pull from the same remote branch into the new branch, or if you
|
||||
don't want to use "git pull <repository> <refspec>" explicitly.
|
||||
This behavior is the default. Set the
|
||||
branch.autosetupmerge configuration variable to false if you
|
||||
want git-checkout and git-branch to always behave as if
|
||||
'--no-track' were given.
|
||||
When creating a new branch, set up configuration so that git-pull
|
||||
will automatically retrieve data from the start point, which must be
|
||||
a branch. Use this if you always pull from the same upstream branch
|
||||
into the new branch, and if you don't want to use "git pull
|
||||
<repository> <refspec>" explicitly. This behavior is the default
|
||||
when the start point is a remote branch. Set the
|
||||
branch.autosetupmerge configuration variable to `false` if you want
|
||||
git-checkout and git-branch to always behave as if '--no-track' were
|
||||
given. Set it to `always` if you want this behavior when the
|
||||
start-point is either a local or remote branch.
|
||||
|
||||
--no-track::
|
||||
When -b is given and a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will not retrieve data
|
||||
from the remote branch, ignoring the branch.autosetupmerge
|
||||
configuration variable.
|
||||
Ignore the branch.autosetupmerge configuration variable.
|
||||
|
||||
-l::
|
||||
Create the new branch's reflog. This activates recording of
|
||||
|
||||
@@ -45,7 +45,7 @@ OPTIONS
|
||||
default is not to do `-x` so this option is a no-op.
|
||||
|
||||
-m parent-number|--mainline parent-number::
|
||||
Usually you cannot revert a merge because you do not know which
|
||||
Usually you cannot cherry-pick a merge because you do not know which
|
||||
side of the merge should be considered the mainline. This
|
||||
option specifies the parent number (starting from 1) of
|
||||
the mainline and allows cherry-pick to replay the change
|
||||
|
||||
@@ -102,13 +102,17 @@ If you need to pass multiple options, separate them with a comma.
|
||||
|
||||
-m::
|
||||
Attempt to detect merges based on the commit message. This option
|
||||
will enable default regexes that try to capture the name source
|
||||
will enable default regexes that try to capture the source
|
||||
branch name from the commit message.
|
||||
|
||||
-M <regex>::
|
||||
Attempt to detect merges based on the commit message with a custom
|
||||
regex. It can be used with '-m' to enable the default regexes
|
||||
as well. You must escape forward slashes.
|
||||
+
|
||||
The regex must capture the source branch name in $1.
|
||||
+
|
||||
This option can be used several times to provide several detection regexes.
|
||||
|
||||
-S <regex>::
|
||||
Skip paths matching the regex.
|
||||
|
||||
@@ -45,12 +45,26 @@ OPTIONS
|
||||
candidates to describe the input committish consider
|
||||
up to <n> candidates. Increasing <n> above 10 will take
|
||||
slightly longer but may produce a more accurate result.
|
||||
An <n> of 0 will cause only exact matches to be output.
|
||||
|
||||
--exact-match::
|
||||
Only output exact matches (a tag directly references the
|
||||
supplied commit). This is a synonym for --candidates=0.
|
||||
|
||||
--debug::
|
||||
Verbosely display information about the searching strategy
|
||||
being employed to standard error. The tag name will still
|
||||
be printed to standard out.
|
||||
|
||||
--long::
|
||||
Always output the long format (the tag, the number of commits
|
||||
and the abbreviated commit name) even when it matches a tag.
|
||||
This is useful when you want to see parts of the commit object name
|
||||
in "describe" output, even when the commit in question happens to be
|
||||
a tagged version. Instead of just emitting the tag name, it will
|
||||
describe such a commit as v1.2-0-deadbeef (0th commit since tag v1.2
|
||||
that points at object deadbeef....).
|
||||
|
||||
--match <pattern>::
|
||||
Only consider tags matching the given pattern (can be used to avoid
|
||||
leaking private tags made from the repository).
|
||||
|
||||
@@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
|
||||
'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -45,6 +45,12 @@ OPTIONS
|
||||
Spend extra cycles to minimize the number of objects to be sent.
|
||||
Use it on slower connection.
|
||||
|
||||
\--include-tag::
|
||||
If the remote side supports it, annotated tags objects will
|
||||
be downloaded on the same connection as the other objects if
|
||||
the object the tag references is downloaded. The caller must
|
||||
otherwise determine the tags this option made available.
|
||||
|
||||
\--upload-pack=<git-upload-pack>::
|
||||
Use this to specify the path to 'git-upload-pack' on the
|
||||
remote side, if is not found on your $PATH.
|
||||
|
||||
@@ -56,7 +56,9 @@ notable exception of the commit filter, for technical reasons).
|
||||
Prior to that, the $GIT_COMMIT environment variable will be set to contain
|
||||
the id of the commit being rewritten. Also, GIT_AUTHOR_NAME,
|
||||
GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL,
|
||||
and GIT_COMMITTER_DATE are set according to the current commit.
|
||||
and GIT_COMMITTER_DATE are set according to the current commit. If any
|
||||
evaluation of <command> returns a non-zero exit status, the whole operation
|
||||
will be aborted.
|
||||
|
||||
A 'map' function is available that takes an "original sha1 id" argument
|
||||
and outputs a "rewritten sha1 id" if the commit has been already
|
||||
@@ -197,7 +199,7 @@ happened). If this is not the case, use:
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
git filter-branch --parent-filter \
|
||||
'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' HEAD
|
||||
'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
or even simpler:
|
||||
@@ -240,6 +242,15 @@ committed a merge between P1 and P2, it will be propagated properly
|
||||
and all children of the merge will become merge commits with P1,P2
|
||||
as their parents instead of the merge commit.
|
||||
|
||||
You can rewrite the commit log messages using `--message-filter`. For
|
||||
example, `git-svn-id` strings in a repository created by `git-svn` can
|
||||
be removed this way:
|
||||
|
||||
-------------------------------------------------------
|
||||
git filter-branch --message-filter '
|
||||
sed -e "/^git-svn-id:/d"
|
||||
'
|
||||
-------------------------------------------------------
|
||||
|
||||
To restrict rewriting to only part of the history, specify a revision
|
||||
range in addition to the new branch name. The new branch name will
|
||||
|
||||
@@ -10,13 +10,15 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]
|
||||
[--attach[=<boundary>] | --inline[=<boundary>]]
|
||||
[-s | --signoff] [<common diff options>]
|
||||
[-n | --numbered | -N | --no-numbered]
|
||||
[--start-number <n>] [--numbered-files]
|
||||
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
|
||||
[--ignore-if-in-upstream]
|
||||
[--subject-prefix=Subject-Prefix]
|
||||
[--attach[=<boundary>] | --inline[=<boundary>]]
|
||||
[-s | --signoff] [<common diff options>]
|
||||
[-n | --numbered | -N | --no-numbered]
|
||||
[--start-number <n>] [--numbered-files]
|
||||
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
|
||||
[--ignore-if-in-upstream]
|
||||
[--subject-prefix=Subject-Prefix]
|
||||
[--cc=<email>]
|
||||
[--cover-letter]
|
||||
[ <since> | <revision range> ]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -135,6 +137,15 @@ include::diff-options.txt[]
|
||||
allows for useful naming of a patch series, and can be
|
||||
combined with the --numbered option.
|
||||
|
||||
--cc=<email>::
|
||||
Add a "Cc:" header to the email headers. This is in addition
|
||||
to any configured headers, and may be used multiple times.
|
||||
|
||||
--cover-letter::
|
||||
Generate a cover letter template. You still have to fill in
|
||||
a description, but the shortlog and the diffstat will be
|
||||
generated for you.
|
||||
|
||||
--suffix=.<sfx>::
|
||||
Instead of using `.patch` as the suffix for generated
|
||||
filenames, use specified suffix. A common alternative is
|
||||
|
||||
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-gc' [--prune] [--aggressive] [--auto]
|
||||
'git-gc' [--aggressive] [--auto] [--quiet]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -25,17 +25,6 @@ operating performance. Some git commands may automatically run
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
--prune::
|
||||
Usually `git-gc` packs refs, expires old reflog entries,
|
||||
packs loose objects,
|
||||
and removes old 'rerere' records. Removal
|
||||
of unreferenced loose objects is an unsafe operation
|
||||
while other git operations are in progress, so it is not
|
||||
done by default. Pass this option if you want it, and only
|
||||
when you know nobody else is creating new objects in the
|
||||
repository at the same time (e.g. never use this option
|
||||
in a cron script).
|
||||
|
||||
--aggressive::
|
||||
Usually 'git-gc' runs very quickly while providing good disk
|
||||
space utilization and performance. This option will cause
|
||||
@@ -63,6 +52,9 @@ are consolidated into a single pack by using the `-A` option of
|
||||
`git-repack`. Setting `gc.autopacklimit` to 0 disables
|
||||
automatic consolidation of packs.
|
||||
|
||||
--quiet::
|
||||
Suppress all progress reports.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
@@ -101,6 +93,10 @@ the value, the more time is spent optimizing the delta compression. See
|
||||
the documentation for the --window' option in linkgit:git-repack[1] for
|
||||
more details. This defaults to 10.
|
||||
|
||||
The optional configuration variable 'gc.pruneExpire' controls how old
|
||||
the unreferenced loose objects have to be before they are pruned. The
|
||||
default is "2 weeks ago".
|
||||
|
||||
See Also
|
||||
--------
|
||||
linkgit:git-prune[1]
|
||||
|
||||
@@ -78,7 +78,7 @@ OPTIONS
|
||||
-l | --files-with-matches | --name-only | -L | --files-without-match::
|
||||
Instead of showing every matched line, show only the
|
||||
names of files that contain (or do not contain) matches.
|
||||
For better compatability with git-diff, --name-only is a
|
||||
For better compatibility with git-diff, --name-only is a
|
||||
synonym for --files-with-matches.
|
||||
|
||||
-c | --count::
|
||||
|
||||
@@ -33,17 +33,21 @@ OPTIONS
|
||||
option supersedes any other option.
|
||||
|
||||
-i|--info::
|
||||
Use the 'info' program to display the manual page, instead of
|
||||
the 'man' program that is used by default.
|
||||
Display manual page for the command in the 'info' format. The
|
||||
'info' program will be used for that purpose.
|
||||
|
||||
-m|--man::
|
||||
Use the 'man' program to display the manual page. This may be
|
||||
used to override a value set in the 'help.format'
|
||||
configuration variable.
|
||||
Display manual page for the command in the 'man' format. This
|
||||
option may be used to override a value set in the
|
||||
'help.format' configuration variable.
|
||||
+
|
||||
By default the 'man' program will be used to display the manual page,
|
||||
but the 'man.viewer' configuration variable may be used to choose
|
||||
other display programs (see below).
|
||||
|
||||
-w|--web::
|
||||
Use a web browser to display the HTML manual page, instead of
|
||||
the 'man' program that is used by default.
|
||||
Display manual page for the command in the 'web' (HTML)
|
||||
format. A web browser will be used for that purpose.
|
||||
+
|
||||
The web browser can be specified using the configuration variable
|
||||
'help.browser', or 'web.browser' if the former is not set. If none of
|
||||
@@ -54,6 +58,9 @@ linkgit:git-web--browse[1] for more information about this.
|
||||
CONFIGURATION VARIABLES
|
||||
-----------------------
|
||||
|
||||
help.format
|
||||
~~~~~~~~~~~
|
||||
|
||||
If no command line option is passed, the 'help.format' configuration
|
||||
variable will be checked. The following values are supported for this
|
||||
variable; they make 'git-help' behave as their corresponding command
|
||||
@@ -61,15 +68,47 @@ line option:
|
||||
|
||||
* "man" corresponds to '-m|--man',
|
||||
* "info" corresponds to '-i|--info',
|
||||
* "web" or "html" correspond to '-w|--web',
|
||||
* "web" or "html" correspond to '-w|--web'.
|
||||
|
||||
help.browser, web.browser and browser.<tool>.path
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
|
||||
be checked if the 'web' format is chosen (either by command line
|
||||
option or configuration variable). See '-w|--web' in the OPTIONS
|
||||
section above and linkgit:git-web--browse[1].
|
||||
|
||||
Note that these configuration variables should probably be set using
|
||||
the '--global' flag, for example like this:
|
||||
man.viewer
|
||||
~~~~~~~~~~
|
||||
|
||||
The 'man.viewer' config variable will be checked if the 'man' format
|
||||
is chosen. Only the following values are currently supported:
|
||||
|
||||
* "man": use the 'man' program as usual,
|
||||
* "woman": use 'emacsclient' to launch the "woman" mode in emacs
|
||||
(this only works starting with emacsclient versions 22),
|
||||
* "konqueror": use a man KIO slave in konqueror.
|
||||
|
||||
Multiple values may be given to this configuration variable. Their
|
||||
corresponding programs will be tried in the order listed in the
|
||||
configuration file.
|
||||
|
||||
For example, this configuration:
|
||||
|
||||
[man]
|
||||
viewer = konqueror
|
||||
viewer = woman
|
||||
|
||||
will try to use konqueror first. But this may fail (for example if
|
||||
DISPLAY is not set) and in that case emacs' woman mode will be tried.
|
||||
|
||||
If everything fails the 'man' program will be tried anyway.
|
||||
|
||||
Note about git config --global
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Note that all these configuration variables should probably be set
|
||||
using the '--global' flag, for example like this:
|
||||
|
||||
------------------------------------------------
|
||||
$ git config --global help.format web
|
||||
|
||||
@@ -75,6 +75,9 @@ OPTIONS
|
||||
to force the version for the generated pack index, and to force
|
||||
64-bit index entries on objects located above the given offset.
|
||||
|
||||
--strict::
|
||||
Die, if the pack contains broken objects or links.
|
||||
|
||||
|
||||
Note
|
||||
----
|
||||
|
||||
@@ -68,7 +68,8 @@ HOW MERGE WORKS
|
||||
---------------
|
||||
|
||||
A merge is always between the current `HEAD` and one or more
|
||||
remote branch heads, and the index file must exactly match the
|
||||
commits (usually, branch head or tag), and the index file must
|
||||
exactly match the
|
||||
tree of `HEAD` commit (i.e. the contents of the last commit) when
|
||||
it happens. In other words, `git-diff --cached HEAD` must
|
||||
report no changes.
|
||||
|
||||
@@ -12,12 +12,12 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
Use 'git mergetool' to run one of several merge utilities to resolve
|
||||
Use `git mergetool` to run one of several merge utilities to resolve
|
||||
merge conflicts. It is typically run after linkgit:git-merge[1].
|
||||
|
||||
If one or more <file> parameters are given, the merge tool program will
|
||||
be run to resolve differences on each file. If no <file> names are
|
||||
specified, 'git mergetool' will run the merge tool program on every file
|
||||
specified, `git mergetool` will run the merge tool program on every file
|
||||
with merge conflicts.
|
||||
|
||||
OPTIONS
|
||||
@@ -27,16 +27,38 @@ OPTIONS
|
||||
Valid merge tools are:
|
||||
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
|
||||
+
|
||||
If a merge resolution program is not specified, 'git mergetool'
|
||||
will use the configuration variable merge.tool. If the
|
||||
configuration variable merge.tool is not set, 'git mergetool'
|
||||
If a merge resolution program is not specified, `git mergetool`
|
||||
will use the configuration variable `merge.tool`. If the
|
||||
configuration variable `merge.tool` is not set, `git mergetool`
|
||||
will pick a suitable default.
|
||||
+
|
||||
You can explicitly provide a full path to the tool by setting the
|
||||
configuration variable mergetool.<tool>.path. For example, you
|
||||
configuration variable `mergetool.<tool>.path`. For example, you
|
||||
can configure the absolute path to kdiff3 by setting
|
||||
mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool
|
||||
is available in PATH.
|
||||
`mergetool.kdiff3.path`. Otherwise, `git mergetool` assumes the
|
||||
tool is available in PATH.
|
||||
+
|
||||
Instead of running one of the known merge tool programs
|
||||
`git mergetool` can be customized to run an alternative program
|
||||
by specifying the command line to invoke in a configration
|
||||
variable `mergetool.<tool>.cmd`.
|
||||
+
|
||||
When `git mergetool` is invoked with this tool (either through the
|
||||
`-t` or `--tool` option or the `merge.tool` configuration
|
||||
variable) the configured command line will be invoked with `$BASE`
|
||||
set to the name of a temporary file containing the common base for
|
||||
the merge, if available; `$LOCAL` set to the name of a temporary
|
||||
file containing the contents of the file on the current branch;
|
||||
`$REMOTE` set to the name of a temporary file containing the
|
||||
contents of the file to be merged, and `$MERGED` set to the name
|
||||
of the file to which the merge tool should write the result of the
|
||||
merge resolution.
|
||||
+
|
||||
If the custom merge tool correctly indicates the success of a
|
||||
merge resolution with its exit code then the configuration
|
||||
variable `mergetool.<tool>.trustExitCode` can be set to `true`.
|
||||
Otherwise, `git mergetool` will prompt the user to indicate the
|
||||
success of the resolution after the custom tool has exited.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
@@ -73,6 +73,11 @@ base-name::
|
||||
as if all refs under `$GIT_DIR/refs` are specified to be
|
||||
included.
|
||||
|
||||
--include-tag::
|
||||
Include unasked-for annotated tags if the object they
|
||||
reference was included in the resulting packfile. This
|
||||
can be useful to send new tags to native git clients.
|
||||
|
||||
--window=[N], --depth=[N]::
|
||||
These two options affect how the objects contained in
|
||||
the pack are stored using delta compression. The
|
||||
@@ -177,6 +182,8 @@ base-name::
|
||||
This is meant to reduce packing time on multiprocessor machines.
|
||||
The required amount of memory for the delta search window is
|
||||
however multiplied by the number of threads.
|
||||
Specifying 0 will cause git to auto-detect the number of CPU's
|
||||
and set the number of threads accordingly.
|
||||
|
||||
--index-version=<version>[,<offset>]::
|
||||
This is intended to be used by the test suite only. It allows
|
||||
|
||||
@@ -21,6 +21,8 @@ Note that you can use `.` (current directory) as the
|
||||
<repository> to pull from the local repository -- this is useful
|
||||
when merging local branches into the current branch.
|
||||
|
||||
Also note that options meant for `git-pull` itself and underlying
|
||||
`git-merge` must be given before the options meant for `git-fetch`.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
@@ -9,6 +9,7 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
|
||||
[-s <strategy> | --strategy=<strategy>]
|
||||
[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
|
||||
[--onto <newbase>] <upstream> [<branch>]
|
||||
'git-rebase' --continue | --skip | --abort
|
||||
@@ -261,8 +262,7 @@ hook if one exists. You can use this hook to do sanity checks and
|
||||
reject the rebase if it isn't appropriate. Please see the template
|
||||
pre-rebase hook script for an example.
|
||||
|
||||
You must be in the top directory of your project to start (or continue)
|
||||
a rebase. Upon completion, <branch> will be the current branch.
|
||||
Upon completion, <branch> will be the current branch.
|
||||
|
||||
INTERACTIVE MODE
|
||||
----------------
|
||||
|
||||
@@ -19,6 +19,8 @@ depending on the subcommand:
|
||||
git reflog expire [--dry-run] [--stale-fix] [--verbose]
|
||||
[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
|
||||
|
||||
git reflog delete ref@\{specifier\}...
|
||||
|
||||
git reflog [show] [log-options] [<ref>]
|
||||
|
||||
Reflog is a mechanism to record when the tip of branches are
|
||||
@@ -43,6 +45,9 @@ two moves ago", `master@\{one.week.ago\}` means "where master used to
|
||||
point to one week ago", and so on. See linkgit:git-rev-parse[1] for
|
||||
more details.
|
||||
|
||||
To delete single entries from the reflog, use the subcommand "delete"
|
||||
and specify the _exact_ entry (e.g. ``git reflog delete master@\{2\}'').
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@@ -75,6 +80,15 @@ them.
|
||||
--all::
|
||||
Instead of listing <refs> explicitly, prune all refs.
|
||||
|
||||
--updateref::
|
||||
Update the ref with the sha1 of the top reflog entry (i.e.
|
||||
<ref>@\{0\}) after expiring or deleting.
|
||||
|
||||
--rewrite::
|
||||
While expiring or deleting, adjust each reflog entry to ensure
|
||||
that the `old` sha1 field points to the `new` sha1 field of the
|
||||
previous entry.
|
||||
|
||||
--verbose::
|
||||
Print extra information on screen.
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ SYNOPSIS
|
||||
[ \--full-history ]
|
||||
[ \--not ]
|
||||
[ \--all ]
|
||||
[ \--branches ]
|
||||
[ \--tags ]
|
||||
[ \--remotes ]
|
||||
[ \--stdin ]
|
||||
[ \--quiet ]
|
||||
[ \--topo-order ]
|
||||
@@ -31,6 +34,7 @@ SYNOPSIS
|
||||
[ \--(author|committer|grep)=<pattern> ]
|
||||
[ \--regexp-ignore-case | \-i ]
|
||||
[ \--extended-regexp | \-E ]
|
||||
[ \--fixed-strings | \-F ]
|
||||
[ \--date={local|relative|default|iso|rfc|short} ]
|
||||
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
|
||||
[ \--pretty | \--header ]
|
||||
|
||||
@@ -325,7 +325,7 @@ The lines after the separator describe the options.
|
||||
Each line of options has this format:
|
||||
|
||||
------------
|
||||
<opt_spec><arg_spec>? SP+ help LF
|
||||
<opt_spec><flags>* SP+ help LF
|
||||
------------
|
||||
|
||||
`<opt_spec>`::
|
||||
@@ -334,10 +334,17 @@ Each line of options has this format:
|
||||
is necessary. `h,help`, `dry-run` and `f` are all three correct
|
||||
`<opt_spec>`.
|
||||
|
||||
`<arg_spec>`::
|
||||
an `<arg_spec>` tells the option parser if the option has an argument
|
||||
(`=`), an optional one (`?` though its use is discouraged) or none
|
||||
(no `<arg_spec>` in that case).
|
||||
`<flags>`::
|
||||
`<flags>` are of `*`, `=`, `?` or `!`.
|
||||
* Use `=` if the option takes an argument.
|
||||
|
||||
* Use `?` to mean that the option is optional (though its use is discouraged).
|
||||
|
||||
* Use `*` to mean that this option should not be listed in the usage
|
||||
generated for the `-h` argument. It's shown for `--help-all` as
|
||||
documented in linkgit:gitcli[5].
|
||||
|
||||
* Use `!` to not make the corresponding negated long option available.
|
||||
|
||||
The remainder of the line, after stripping the spaces, is used
|
||||
as the help associated to the option.
|
||||
|
||||
@@ -8,7 +8,7 @@ git-stash - Stash the changes in a dirty working directory away
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
|
||||
'git-stash' (list | show [<stash>] | apply [<stash>] | clear | drop [<stash>] | pop [<stash>])
|
||||
'git-stash' [save [<message>]]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -85,6 +85,17 @@ clear::
|
||||
Remove all the stashed states. Note that those states will then
|
||||
be subject to pruning, and may be difficult or impossible to recover.
|
||||
|
||||
drop [<stash>]::
|
||||
|
||||
Remove a single stashed state from the stash list. When no `<stash>`
|
||||
is given, it removes the latest one. i.e. `stash@\{0}`
|
||||
|
||||
pop [<stash>]::
|
||||
|
||||
Remove a single stashed state from the stash list and apply on top
|
||||
of the current working tree state. When no `<stash>` is given,
|
||||
`stash@\{0}` is assumed. See also `apply`.
|
||||
|
||||
|
||||
DISCUSSION
|
||||
----------
|
||||
|
||||
@@ -12,14 +12,16 @@ SYNOPSIS
|
||||
'git-submodule' [--quiet] add [-b branch] [--] <repository> [<path>]
|
||||
'git-submodule' [--quiet] status [--cached] [--] [<path>...]
|
||||
'git-submodule' [--quiet] [init|update] [--] [<path>...]
|
||||
'git-submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
|
||||
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
add::
|
||||
Add the given repository as a submodule at the given path
|
||||
to the changeset to be committed next. In particular, the
|
||||
repository is cloned at the specified path, added to the
|
||||
to the changeset to be committed next. If path is a valid
|
||||
repository within the project, it is added as is. Otherwise,
|
||||
repository is cloned at the specified path. path is added to the
|
||||
changeset and registered in .gitmodules. If no path is
|
||||
specified, the path is deduced from the repository specification.
|
||||
If the repository url begins with ./ or ../, it is stored as
|
||||
@@ -46,6 +48,11 @@ update::
|
||||
checkout the commit specified in the index of the containing repository.
|
||||
This will make the submodules HEAD be detached.
|
||||
|
||||
summary::
|
||||
Show commit summary between the given commit (defaults to HEAD) and
|
||||
working tree/index. For a submodule in question, a series of commits
|
||||
in the submodule between the given super project commit and the
|
||||
index or working tree (switched by --cached) are shown.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@@ -56,9 +63,16 @@ OPTIONS
|
||||
Branch of repository to add as submodule.
|
||||
|
||||
--cached::
|
||||
Display the SHA-1 stored in the index, not the SHA-1 of the currently
|
||||
checked out submodule commit. This option is only valid for the
|
||||
status command.
|
||||
This option is only valid for status and summary commands. These
|
||||
commands typically use the commit found in the submodule HEAD, but
|
||||
with this option, the commit stored in the index is used instead.
|
||||
|
||||
-n, --summary-limit::
|
||||
This option is only valid for the summary command.
|
||||
Limit the summary size (number of commits shown in total).
|
||||
Giving 0 will disable the summary; a negative number means unlimted
|
||||
(the default). This limit only applies to modified submodules. The
|
||||
size is always limited to 1 for added/deleted/typechanged submodules.
|
||||
|
||||
<path>::
|
||||
Path to submodule(s). When specified this will restrict the command
|
||||
|
||||
@@ -159,6 +159,10 @@ New features:
|
||||
our version of --pretty=oneline
|
||||
--
|
||||
+
|
||||
NOTE: SVN itself only stores times in UTC and nothing else. The regular svn
|
||||
client converts the UTC time to the local time (or based on the TZ=
|
||||
environment). This command has the same behaviour.
|
||||
+
|
||||
Any other arguments are passed directly to `git log'
|
||||
|
||||
'blame'::
|
||||
|
||||
@@ -32,11 +32,11 @@ OUTPUT FORMAT
|
||||
-------------
|
||||
When specifying the -v option the format used is:
|
||||
|
||||
SHA1 type size offset-in-packfile
|
||||
SHA1 type size size-in-pack-file offset-in-packfile
|
||||
|
||||
for objects that are not deltified in the pack, and
|
||||
|
||||
SHA1 type size offset-in-packfile depth base-SHA1
|
||||
SHA1 type size size-in-packfile offset-in-packfile depth base-SHA1
|
||||
|
||||
for objects that are deltified.
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ The following browsers (or commands) are currently supported:
|
||||
* dillo
|
||||
* open (this is the default under Mac OS X GUI)
|
||||
|
||||
Custom commands may also be specified.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-b BROWSER|--browser=BROWSER::
|
||||
@@ -43,16 +45,35 @@ OPTIONS
|
||||
CONFIGURATION VARIABLES
|
||||
-----------------------
|
||||
|
||||
CONF.VAR (from -c option) and web.browser
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The web browser can be specified using a configuration variable passed
|
||||
with the -c (or --config) command line option, or the 'web.browser'
|
||||
configuration variable if the former is not used.
|
||||
|
||||
browser.<tool>.path
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can explicitly provide a full path to your preferred browser by
|
||||
setting the configuration variable 'browser.<tool>.path'. For example,
|
||||
you can configure the absolute path to firefox by setting
|
||||
'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool
|
||||
is available in PATH.
|
||||
|
||||
browser.<tool>.cmd
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the browser, specified by options or configuration variables, is
|
||||
not among the supported ones, then the corresponding
|
||||
'browser.<tool>.cmd' configuration variable will be looked up. If this
|
||||
variable exists then "git web--browse" will treat the specified tool
|
||||
as a custom command and will use a shell eval to run the command with
|
||||
the URLs passed as arguments.
|
||||
|
||||
Note about git config --global
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Note that these configuration variables should probably be set using
|
||||
the '--global' flag, for example like this:
|
||||
|
||||
|
||||
@@ -38,11 +38,6 @@ OPTIONS
|
||||
Show git internal diff output, but for the whole tree,
|
||||
not just the top level.
|
||||
|
||||
--pretty=<format>::
|
||||
Controls the output format for the commit logs.
|
||||
<format> can be one of 'raw', 'medium', 'short', 'full',
|
||||
and 'oneline'.
|
||||
|
||||
-m::
|
||||
By default, differences for merge commits are not shown.
|
||||
With this flag, show differences to that commit from all
|
||||
@@ -51,6 +46,10 @@ OPTIONS
|
||||
However, it is not very useful in general, although it
|
||||
*is* useful on a file-by-file basis.
|
||||
|
||||
include::pretty-options.txt[]
|
||||
|
||||
include::pretty-formats.txt[]
|
||||
|
||||
Examples
|
||||
--------
|
||||
git-whatchanged -p v2.6.12.. include/scsi drivers/scsi::
|
||||
|
||||
@@ -43,9 +43,11 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.5.4.2/git.html[documentation for release 1.5.4.2]
|
||||
* link:v1.5.4.4/git.html[documentation for release 1.5.4.4]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.4.4.txt[1.5.4.4],
|
||||
link:RelNotes-1.5.4.3.txt[1.5.4.3],
|
||||
link:RelNotes-1.5.4.2.txt[1.5.4.2],
|
||||
link:RelNotes-1.5.4.1.txt[1.5.4.1],
|
||||
link:RelNotes-1.5.4.txt[1.5.4].
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<!-- callout.xsl: converts asciidoc callouts to man page format -->
|
||||
<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL -->
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||
|
||||
<xsl:param name="man.output.quietly" select="1"/>
|
||||
<xsl:param name="refentry.meta.get.quietly" select="1"/>
|
||||
|
||||
<xsl:template match="co">
|
||||
<xsl:value-of select="concat('▓fB(',substring-after(@id,'-'),')▓fR')"/>
|
||||
</xsl:template>
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
where '<format>' can be one of 'oneline', 'short', 'medium',
|
||||
'full', 'fuller', 'email', 'raw' and 'format:<string>'.
|
||||
When omitted, the format defaults to 'medium'.
|
||||
+
|
||||
Note: you can specify the default pretty format in the repository
|
||||
configuration (see linkgit:git-config[1]).
|
||||
|
||||
--abbrev-commit::
|
||||
Instead of showing the full 40-byte hexadecimal commit object
|
||||
|
||||
@@ -130,9 +130,11 @@ limiting may be applied.
|
||||
|
||||
Show commits older than a specific date.
|
||||
|
||||
ifdef::git-rev-list[]
|
||||
--max-age='timestamp', --min-age='timestamp'::
|
||||
|
||||
Limit the commits output to specified time range.
|
||||
endif::git-rev-list[]
|
||||
|
||||
--author='pattern', --committer='pattern'::
|
||||
|
||||
@@ -153,6 +155,11 @@ limiting may be applied.
|
||||
Consider the limiting patterns to be extended regular expressions
|
||||
instead of the default basic regular expressions.
|
||||
|
||||
-F, --fixed-strings::
|
||||
|
||||
Consider the limiting patterns to be fixed strings (don't interpret
|
||||
pattern as a regular expression).
|
||||
|
||||
--remove-empty::
|
||||
|
||||
Stop when a given path disappears from the tree.
|
||||
|
||||
@@ -39,7 +39,7 @@ Calling sequence
|
||||
* Once you finish feeding the pairs of files, call `diffcore_std()`.
|
||||
This will tell the diffcore library to go ahead and do its work.
|
||||
|
||||
* Calling `diffcore_flush()` will produce the output.
|
||||
* Calling `diff_flush()` will produce the output.
|
||||
|
||||
|
||||
Data structures
|
||||
|
||||
@@ -49,7 +49,7 @@ Functions
|
||||
|
||||
`finish_async`::
|
||||
|
||||
Wait for the completeion of an asynchronous function that was
|
||||
Wait for the completion of an asynchronous function that was
|
||||
started with start_async().
|
||||
|
||||
|
||||
@@ -111,9 +111,10 @@ stderr as follows:
|
||||
.no_stdin, .no_stdout, .no_stderr: The respective channel is
|
||||
redirected to /dev/null.
|
||||
|
||||
.stdout_to_stderr: stdout of the child is redirected to the
|
||||
parent's stderr (i.e. *not* to what .err or
|
||||
.no_stderr specify).
|
||||
.stdout_to_stderr: stdout of the child is redirected to its
|
||||
stderr. This happens after stderr is itself redirected.
|
||||
So stdout will follow stderr to wherever it is
|
||||
redirected.
|
||||
|
||||
To modify the environment of the sub-process, specify an array of
|
||||
string pointers (NULL terminated) in .env:
|
||||
@@ -121,8 +122,8 @@ string pointers (NULL terminated) in .env:
|
||||
. If the string is of the form "VAR=value", i.e. it contains '='
|
||||
the variable is added to the child process's environment.
|
||||
|
||||
. If the string does not contain '=', it names an environement
|
||||
variable that will be removed from the child process's envionment.
|
||||
. If the string does not contain '=', it names an environment
|
||||
variable that will be removed from the child process's environment.
|
||||
|
||||
To specify a new initial working directory for the sub-process,
|
||||
specify it in the .dir member.
|
||||
|
||||
@@ -44,3 +44,26 @@ endif::git-clone[]
|
||||
ifdef::git-clone[]
|
||||
They are equivalent, except the former implies --local option.
|
||||
endif::git-clone[]
|
||||
|
||||
|
||||
If there are a large number of similarly-named remote repositories and
|
||||
you want to use a different format for them (such that the URLs you
|
||||
use will be rewritten into URLs that work), you can create a
|
||||
configuration section of the form:
|
||||
|
||||
------------
|
||||
[url "<actual url base>"]
|
||||
insteadOf = <other url base>
|
||||
------------
|
||||
|
||||
For example, with this:
|
||||
|
||||
------------
|
||||
[url "git://git.host.xz/"]
|
||||
insteadOf = host.xz:/path/to/
|
||||
insteadOf = work:
|
||||
------------
|
||||
|
||||
a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
|
||||
rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ elif test -d .git &&
|
||||
case "$VN" in
|
||||
*$LF*) (exit 1) ;;
|
||||
v[0-9]*)
|
||||
git diff-index --quiet HEAD || VN="$VN-dirty" ;;
|
||||
test -z "$(git diff-index --name-only HEAD)" ||
|
||||
VN="$VN-dirty" ;;
|
||||
esac
|
||||
then
|
||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||
|
||||
63
Makefile
63
Makefile
@@ -3,6 +3,10 @@ all::
|
||||
|
||||
# Define V=1 to have a more verbose compile.
|
||||
#
|
||||
# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
|
||||
# or vsnprintf() return -1 instead of number of characters which would
|
||||
# have been written to the final string if enough space had been available.
|
||||
#
|
||||
# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
|
||||
# when attempting to read from an fopen'ed directory.
|
||||
#
|
||||
@@ -147,6 +151,9 @@ all::
|
||||
# is a simplified version of the merge sort used in glibc. This is
|
||||
# recommended if Git triggers O(n^2) behavior in your platform's qsort().
|
||||
#
|
||||
# Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
|
||||
# your external grep (e.g., if your system lacks grep, if its grep is
|
||||
# broken, or spawning external process is slower than built-in grep git has).
|
||||
|
||||
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
||||
@"$(SHELL_PATH)" ./GIT-VERSION-GEN
|
||||
@@ -229,7 +236,7 @@ BASIC_CFLAGS =
|
||||
BASIC_LDFLAGS =
|
||||
|
||||
SCRIPT_SH = \
|
||||
git-bisect.sh git-checkout.sh \
|
||||
git-bisect.sh \
|
||||
git-clone.sh \
|
||||
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
|
||||
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
|
||||
@@ -246,7 +253,7 @@ SCRIPT_SH = \
|
||||
SCRIPT_PERL = \
|
||||
git-add--interactive.perl \
|
||||
git-archimport.perl git-cvsimport.perl git-relink.perl \
|
||||
git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
|
||||
git-cvsserver.perl git-cvsexportcommit.perl \
|
||||
git-send-email.perl git-svn.perl
|
||||
|
||||
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
@@ -267,23 +274,24 @@ PROGRAMS = \
|
||||
git-upload-pack$X \
|
||||
git-pack-redundant$X git-var$X \
|
||||
git-merge-tree$X \
|
||||
git-merge-recursive$X \
|
||||
$(POSIX_ONLY_PROGRAMS) \
|
||||
$(EXTRA_PROGRAMS)
|
||||
|
||||
# Empty...
|
||||
EXTRA_PROGRAMS =
|
||||
|
||||
# List built-in command $C whose implementation cmd_$C() is not in
|
||||
# builtin-$C.o but is linked in as part of some other command.
|
||||
BUILT_INS = \
|
||||
git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
|
||||
git-get-tar-commit-id$X git-init$X git-repo-config$X \
|
||||
git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
|
||||
git-merge-subtree$X \
|
||||
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
|
||||
|
||||
# what 'all' will build and 'install' will install, in gitexecdir
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
|
||||
|
||||
ALL_PROGRAMS += git-merge-subtree$X
|
||||
|
||||
# what 'all' will build but not install in gitexecdir
|
||||
OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
|
||||
|
||||
@@ -306,7 +314,8 @@ LIB_H = \
|
||||
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
|
||||
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
|
||||
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
|
||||
mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
|
||||
mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h ll-merge.h fsck.h \
|
||||
pack-revindex.h compat/mingw.h
|
||||
|
||||
DIFF_OBJS = \
|
||||
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
|
||||
@@ -321,7 +330,7 @@ LIB_OBJS = \
|
||||
patch-ids.o \
|
||||
object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
|
||||
sideband.o reachable.o reflog-walk.o \
|
||||
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
|
||||
quote.o read-cache.o refs.o run-command.o dir.o \
|
||||
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
|
||||
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
|
||||
revision.o pager.o tree-walk.o xdiff-interface.o \
|
||||
@@ -329,7 +338,8 @@ LIB_OBJS = \
|
||||
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
|
||||
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
|
||||
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
|
||||
transport.o bundle.o walker.o parse-options.o ws.o archive.o
|
||||
transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
|
||||
ll-merge.o alias.o fsck.o pack-revindex.o
|
||||
|
||||
BUILTIN_OBJS = \
|
||||
builtin-add.o \
|
||||
@@ -341,6 +351,7 @@ BUILTIN_OBJS = \
|
||||
builtin-bundle.o \
|
||||
builtin-cat-file.o \
|
||||
builtin-check-attr.o \
|
||||
builtin-checkout.o \
|
||||
builtin-checkout-index.o \
|
||||
builtin-check-ref-format.o \
|
||||
builtin-clean.o \
|
||||
@@ -371,6 +382,7 @@ BUILTIN_OBJS = \
|
||||
builtin-merge-base.o \
|
||||
builtin-merge-file.o \
|
||||
builtin-merge-ours.o \
|
||||
builtin-merge-recursive.o \
|
||||
builtin-mv.o \
|
||||
builtin-name-rev.o \
|
||||
builtin-pack-objects.o \
|
||||
@@ -379,6 +391,7 @@ BUILTIN_OBJS = \
|
||||
builtin-push.o \
|
||||
builtin-read-tree.o \
|
||||
builtin-reflog.o \
|
||||
builtin-remote.o \
|
||||
builtin-send-pack.o \
|
||||
builtin-config.o \
|
||||
builtin-rerere.o \
|
||||
@@ -476,6 +489,7 @@ ifeq ($(uname_S),FreeBSD)
|
||||
NO_MEMMEM = YesPlease
|
||||
BASIC_CFLAGS += -I/usr/local/include
|
||||
BASIC_LDFLAGS += -L/usr/local/lib
|
||||
DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),OpenBSD)
|
||||
NO_STRCASESTR = YesPlease
|
||||
@@ -542,12 +556,14 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
NO_C99_FORMAT = YesPlease
|
||||
NO_STRTOUMAX = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
SNPRINTF_RETURNS_BOGUS = YesPlease
|
||||
NO_SVN_TESTS = YesPlease
|
||||
NO_PERL_MAKEMAKER = YesPlease
|
||||
NO_R_TO_GCC_LINKER = YesPlease
|
||||
INTERNAL_QSORT = YesPlease
|
||||
NO_EXTRA_PROGRAMS = YesPlease
|
||||
NO_POSIX_ONLY_PROGRAMS = YesPlease
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
|
||||
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
|
||||
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
|
||||
COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o
|
||||
EXTLIBS += -lws2_32
|
||||
@@ -616,8 +632,8 @@ ifdef ZLIB_PATH
|
||||
endif
|
||||
EXTLIBS += -lz
|
||||
|
||||
ifndef NO_EXTRA_PROGRAMS
|
||||
EXTRA_PROGRAMS += \
|
||||
ifndef NO_POSIX_ONLY_PROGRAMS
|
||||
POSIX_ONLY_PROGRAMS = \
|
||||
git-daemon$X \
|
||||
git-imap-send$X
|
||||
endif
|
||||
@@ -663,6 +679,10 @@ endif
|
||||
ifdef NO_C99_FORMAT
|
||||
BASIC_CFLAGS += -DNO_C99_FORMAT
|
||||
endif
|
||||
ifdef SNPRINTF_RETURNS_BOGUS
|
||||
COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
|
||||
COMPAT_OBJS += compat/snprintf.o
|
||||
endif
|
||||
ifdef FREAD_READS_DIRECTORIES
|
||||
COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
|
||||
COMPAT_OBJS += compat/fopen.o
|
||||
@@ -779,6 +799,13 @@ endif
|
||||
ifdef THREADED_DELTA_SEARCH
|
||||
BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
|
||||
EXTLIBS += -lpthread
|
||||
LIB_OBJS += thread-utils.o
|
||||
endif
|
||||
ifdef DIR_HAS_BSD_GROUP_SEMANTICS
|
||||
COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
|
||||
endif
|
||||
ifdef NO_EXTERNAL_GREP
|
||||
BASIC_CFLAGS += -DNO_EXTERNAL_GREP
|
||||
endif
|
||||
|
||||
ifeq ($(TCLTK_PATH),)
|
||||
@@ -851,7 +878,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
|
||||
|
||||
### Build rules
|
||||
|
||||
all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS)
|
||||
all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
|
||||
ifneq (,$X)
|
||||
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$p';)
|
||||
endif
|
||||
@@ -881,9 +908,6 @@ help.o: help.c common-cmds.h GIT-CFLAGS
|
||||
'-DGIT_MAN_PATH="$(mandir_SQ)"' \
|
||||
'-DGIT_INFO_PATH="$(infodir_SQ)"' $<
|
||||
|
||||
git-merge-subtree$X: git-merge-recursive$X
|
||||
$(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
|
||||
|
||||
$(BUILT_INS): git$X
|
||||
$(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
|
||||
|
||||
@@ -895,6 +919,7 @@ common-cmds.h: $(wildcard Documentation/git-*.txt)
|
||||
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
$(QUIET_GEN)$(RM) $@ $@+ && \
|
||||
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
|
||||
-e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
|
||||
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
|
||||
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
|
||||
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
|
||||
@@ -1054,6 +1079,9 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
|
||||
echo "$$FLAGS" >GIT-CFLAGS; \
|
||||
fi
|
||||
|
||||
GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
|
||||
@echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@
|
||||
|
||||
### Detect Tck/Tk interpreter path changes
|
||||
ifndef NO_TCLTK
|
||||
TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
|
||||
@@ -1154,7 +1182,7 @@ git.spec: git.spec.in
|
||||
mv $@+ $@
|
||||
|
||||
GIT_TARNAME=git-$(GIT_VERSION)
|
||||
dist: git.spec git-archive configure
|
||||
dist: git.spec git-archive$(X) configure
|
||||
./git-archive --format=tar \
|
||||
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
|
||||
@mkdir -p $(GIT_TARNAME)
|
||||
@@ -1217,10 +1245,11 @@ ifndef NO_TCLTK
|
||||
$(MAKE) -C gitk-git clean
|
||||
$(MAKE) -C git-gui clean
|
||||
endif
|
||||
$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
|
||||
$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
|
||||
|
||||
.PHONY: all install clean strip
|
||||
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
|
||||
.PHONY: .FORCE-GIT-BUILD-OPTIONS
|
||||
|
||||
### Check documentation
|
||||
#
|
||||
|
||||
22
alias.c
Normal file
22
alias.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "cache.h"
|
||||
|
||||
static const char *alias_key;
|
||||
static char *alias_val;
|
||||
static int alias_lookup_cb(const char *k, const char *v)
|
||||
{
|
||||
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
|
||||
if (!v)
|
||||
return config_error_nonbool(k);
|
||||
alias_val = xstrdup(v);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *alias_lookup(const char *alias)
|
||||
{
|
||||
alias_key = alias;
|
||||
alias_val = NULL;
|
||||
git_config(alias_lookup_cb);
|
||||
return alias_val;
|
||||
}
|
||||
152
branch.c
Normal file
152
branch.c
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "cache.h"
|
||||
#include "branch.h"
|
||||
#include "refs.h"
|
||||
#include "remote.h"
|
||||
#include "commit.h"
|
||||
|
||||
struct tracking {
|
||||
struct refspec spec;
|
||||
char *src;
|
||||
const char *remote;
|
||||
int matches;
|
||||
};
|
||||
|
||||
static int find_tracked_branch(struct remote *remote, void *priv)
|
||||
{
|
||||
struct tracking *tracking = priv;
|
||||
|
||||
if (!remote_find_tracking(remote, &tracking->spec)) {
|
||||
if (++tracking->matches == 1) {
|
||||
tracking->src = tracking->spec.src;
|
||||
tracking->remote = remote->name;
|
||||
} else {
|
||||
free(tracking->spec.src);
|
||||
if (tracking->src) {
|
||||
free(tracking->src);
|
||||
tracking->src = NULL;
|
||||
}
|
||||
}
|
||||
tracking->spec.src = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when new_ref is branched off of orig_ref, and tries
|
||||
* to infer the settings for branch.<new_ref>.{remote,merge} from the
|
||||
* config.
|
||||
*/
|
||||
static int setup_tracking(const char *new_ref, const char *orig_ref,
|
||||
enum branch_track track)
|
||||
{
|
||||
char key[1024];
|
||||
struct tracking tracking;
|
||||
|
||||
if (strlen(new_ref) > 1024 - 7 - 7 - 1)
|
||||
return error("Tracking not set up: name too long: %s",
|
||||
new_ref);
|
||||
|
||||
memset(&tracking, 0, sizeof(tracking));
|
||||
tracking.spec.dst = (char *)orig_ref;
|
||||
if (for_each_remote(find_tracked_branch, &tracking))
|
||||
return 1;
|
||||
|
||||
if (!tracking.matches)
|
||||
switch (track) {
|
||||
case BRANCH_TRACK_ALWAYS:
|
||||
case BRANCH_TRACK_EXPLICIT:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (tracking.matches > 1)
|
||||
return error("Not tracking: ambiguous information for ref %s",
|
||||
orig_ref);
|
||||
|
||||
sprintf(key, "branch.%s.remote", new_ref);
|
||||
git_config_set(key, tracking.remote ? tracking.remote : ".");
|
||||
sprintf(key, "branch.%s.merge", new_ref);
|
||||
git_config_set(key, tracking.src ? tracking.src : orig_ref);
|
||||
free(tracking.src);
|
||||
printf("Branch %s set up to track %s branch %s.\n", new_ref,
|
||||
tracking.remote ? "remote" : "local", orig_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void create_branch(const char *head,
|
||||
const char *name, const char *start_name,
|
||||
int force, int reflog, enum branch_track track)
|
||||
{
|
||||
struct ref_lock *lock;
|
||||
struct commit *commit;
|
||||
unsigned char sha1[20];
|
||||
char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
|
||||
int forcing = 0;
|
||||
|
||||
snprintf(ref, sizeof ref, "refs/heads/%s", name);
|
||||
if (check_ref_format(ref))
|
||||
die("'%s' is not a valid branch name.", name);
|
||||
|
||||
if (resolve_ref(ref, sha1, 1, NULL)) {
|
||||
if (!force)
|
||||
die("A branch named '%s' already exists.", name);
|
||||
else if (!is_bare_repository() && !strcmp(head, name))
|
||||
die("Cannot force update the current branch.");
|
||||
forcing = 1;
|
||||
}
|
||||
|
||||
real_ref = NULL;
|
||||
if (get_sha1(start_name, sha1))
|
||||
die("Not a valid object name: '%s'.", start_name);
|
||||
|
||||
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
|
||||
case 0:
|
||||
/* Not branching from any existing branch */
|
||||
if (track == BRANCH_TRACK_EXPLICIT)
|
||||
die("Cannot setup tracking information; starting point is not a branch.");
|
||||
break;
|
||||
case 1:
|
||||
/* Unique completion -- good */
|
||||
break;
|
||||
default:
|
||||
die("Ambiguous object name: '%s'.", start_name);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((commit = lookup_commit_reference(sha1)) == NULL)
|
||||
die("Not a valid branch point: '%s'.", start_name);
|
||||
hashcpy(sha1, commit->object.sha1);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, NULL, 0);
|
||||
if (!lock)
|
||||
die("Failed to lock ref for update: %s.", strerror(errno));
|
||||
|
||||
if (reflog)
|
||||
log_all_ref_updates = 1;
|
||||
|
||||
if (forcing)
|
||||
snprintf(msg, sizeof msg, "branch: Reset from %s",
|
||||
start_name);
|
||||
else
|
||||
snprintf(msg, sizeof msg, "branch: Created from %s",
|
||||
start_name);
|
||||
|
||||
if (real_ref && track)
|
||||
setup_tracking(name, real_ref, track);
|
||||
|
||||
if (write_ref_sha1(lock, sha1, msg) < 0)
|
||||
die("Failed to write ref: %s.", strerror(errno));
|
||||
|
||||
free(real_ref);
|
||||
}
|
||||
|
||||
void remove_branch_state(void)
|
||||
{
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
unlink(git_path("SQUASH_MSG"));
|
||||
}
|
||||
24
branch.h
Normal file
24
branch.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef BRANCH_H
|
||||
#define BRANCH_H
|
||||
|
||||
/* Functions for acting on the information about branches. */
|
||||
|
||||
/*
|
||||
* Creates a new branch, where head is the branch currently checked
|
||||
* out, name is the new branch name, start_name is the name of the
|
||||
* existing branch that the new branch should start from, force
|
||||
* enables overwriting an existing (non-head) branch, reflog creates a
|
||||
* reflog for the branch, and track causes the new branch to be
|
||||
* configured to merge the remote branch that start_name is a tracking
|
||||
* branch for (if any).
|
||||
*/
|
||||
void create_branch(const char *head, const char *name, const char *start_name,
|
||||
int force, int reflog, enum branch_track track);
|
||||
|
||||
/*
|
||||
* Remove information about the state of working on the current
|
||||
* branch. (E.g., MERGE_HEAD)
|
||||
*/
|
||||
void remove_branch_state(void);
|
||||
|
||||
#endif
|
||||
@@ -228,18 +228,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (*argv) {
|
||||
/* Was there an invalid path? */
|
||||
if (pathspec) {
|
||||
int num;
|
||||
for (num = 0; pathspec[num]; num++)
|
||||
; /* just counting */
|
||||
if (argc != num)
|
||||
exit(1); /* error message already given */
|
||||
} else
|
||||
exit(1); /* error message already given */
|
||||
}
|
||||
|
||||
fill_directory(&dir, pathspec, ignored_too);
|
||||
|
||||
if (show_only) {
|
||||
|
||||
711
builtin-apply.c
711
builtin-apply.c
@@ -161,6 +161,84 @@ struct patch {
|
||||
struct patch *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* A line in a file, len-bytes long (includes the terminating LF,
|
||||
* except for an incomplete line at the end if the file ends with
|
||||
* one), and its contents hashes to 'hash'.
|
||||
*/
|
||||
struct line {
|
||||
size_t len;
|
||||
unsigned hash : 24;
|
||||
unsigned flag : 8;
|
||||
#define LINE_COMMON 1
|
||||
};
|
||||
|
||||
/*
|
||||
* This represents a "file", which is an array of "lines".
|
||||
*/
|
||||
struct image {
|
||||
char *buf;
|
||||
size_t len;
|
||||
size_t nr;
|
||||
size_t alloc;
|
||||
struct line *line_allocated;
|
||||
struct line *line;
|
||||
};
|
||||
|
||||
static uint32_t hash_line(const char *cp, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t h;
|
||||
for (i = 0, h = 0; i < len; i++) {
|
||||
if (!isspace(cp[i])) {
|
||||
h = h * 3 + (cp[i] & 0xff);
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
|
||||
{
|
||||
ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
|
||||
img->line_allocated[img->nr].len = len;
|
||||
img->line_allocated[img->nr].hash = hash_line(bol, len);
|
||||
img->line_allocated[img->nr].flag = flag;
|
||||
img->nr++;
|
||||
}
|
||||
|
||||
static void prepare_image(struct image *image, char *buf, size_t len,
|
||||
int prepare_linetable)
|
||||
{
|
||||
const char *cp, *ep;
|
||||
|
||||
memset(image, 0, sizeof(*image));
|
||||
image->buf = buf;
|
||||
image->len = len;
|
||||
|
||||
if (!prepare_linetable)
|
||||
return;
|
||||
|
||||
ep = image->buf + image->len;
|
||||
cp = image->buf;
|
||||
while (cp < ep) {
|
||||
const char *next;
|
||||
for (next = cp; next < ep && *next != '\n'; next++)
|
||||
;
|
||||
if (next < ep)
|
||||
next++;
|
||||
add_line_info(image, cp, next - cp, 0);
|
||||
cp = next;
|
||||
}
|
||||
image->line = image->line_allocated;
|
||||
}
|
||||
|
||||
static void clear_image(struct image *image)
|
||||
{
|
||||
free(image->buf);
|
||||
image->buf = NULL;
|
||||
image->len = 0;
|
||||
}
|
||||
|
||||
static void say_patch_name(FILE *output, const char *pre,
|
||||
struct patch *patch, const char *post)
|
||||
{
|
||||
@@ -1437,227 +1515,338 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
|
||||
}
|
||||
}
|
||||
|
||||
static int find_offset(const char *buf, unsigned long size,
|
||||
const char *fragment, unsigned long fragsize,
|
||||
int line, int *lines)
|
||||
static void update_pre_post_images(struct image *preimage,
|
||||
struct image *postimage,
|
||||
char *buf,
|
||||
size_t len)
|
||||
{
|
||||
int i;
|
||||
unsigned long start, backwards, forwards;
|
||||
int i, ctx;
|
||||
char *new, *old, *fixed;
|
||||
struct image fixed_preimage;
|
||||
|
||||
if (fragsize > size)
|
||||
return -1;
|
||||
/*
|
||||
* Update the preimage with whitespace fixes. Note that we
|
||||
* are not losing preimage->buf -- apply_one_fragment() will
|
||||
* free "oldlines".
|
||||
*/
|
||||
prepare_image(&fixed_preimage, buf, len, 1);
|
||||
assert(fixed_preimage.nr == preimage->nr);
|
||||
for (i = 0; i < preimage->nr; i++)
|
||||
fixed_preimage.line[i].flag = preimage->line[i].flag;
|
||||
free(preimage->line_allocated);
|
||||
*preimage = fixed_preimage;
|
||||
|
||||
start = 0;
|
||||
if (line > 1) {
|
||||
unsigned long offset = 0;
|
||||
i = line-1;
|
||||
while (offset + fragsize <= size) {
|
||||
if (buf[offset++] == '\n') {
|
||||
start = offset;
|
||||
if (!--i)
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Adjust the common context lines in postimage, in place.
|
||||
* This is possible because whitespace fixing does not make
|
||||
* the string grow.
|
||||
*/
|
||||
new = old = postimage->buf;
|
||||
fixed = preimage->buf;
|
||||
for (i = ctx = 0; i < postimage->nr; i++) {
|
||||
size_t len = postimage->line[i].len;
|
||||
if (!(postimage->line[i].flag & LINE_COMMON)) {
|
||||
/* an added line -- no counterparts in preimage */
|
||||
memmove(new, old, len);
|
||||
old += len;
|
||||
new += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* a common context -- skip it in the original postimage */
|
||||
old += len;
|
||||
|
||||
/* and find the corresponding one in the fixed preimage */
|
||||
while (ctx < preimage->nr &&
|
||||
!(preimage->line[ctx].flag & LINE_COMMON)) {
|
||||
fixed += preimage->line[ctx].len;
|
||||
ctx++;
|
||||
}
|
||||
if (preimage->nr <= ctx)
|
||||
die("oops");
|
||||
|
||||
/* and copy it in, while fixing the line length */
|
||||
len = preimage->line[ctx].len;
|
||||
memcpy(new, fixed, len);
|
||||
new += len;
|
||||
fixed += len;
|
||||
postimage->line[i].len = len;
|
||||
ctx++;
|
||||
}
|
||||
|
||||
/* Exact line number? */
|
||||
if ((start + fragsize <= size) &&
|
||||
!memcmp(buf + start, fragment, fragsize))
|
||||
return start;
|
||||
/* Fix the length of the whole thing */
|
||||
postimage->len = new - postimage->buf;
|
||||
}
|
||||
|
||||
static int match_fragment(struct image *img,
|
||||
struct image *preimage,
|
||||
struct image *postimage,
|
||||
unsigned long try,
|
||||
int try_lno,
|
||||
unsigned ws_rule,
|
||||
int match_beginning, int match_end)
|
||||
{
|
||||
int i;
|
||||
char *fixed_buf, *buf, *orig, *target;
|
||||
|
||||
if (preimage->nr + try_lno > img->nr)
|
||||
return 0;
|
||||
|
||||
if (match_beginning && try_lno)
|
||||
return 0;
|
||||
|
||||
if (match_end && preimage->nr + try_lno != img->nr)
|
||||
return 0;
|
||||
|
||||
/* Quick hash check */
|
||||
for (i = 0; i < preimage->nr; i++)
|
||||
if (preimage->line[i].hash != img->line[try_lno + i].hash)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Do we have an exact match? If we were told to match
|
||||
* at the end, size must be exactly at try+fragsize,
|
||||
* otherwise try+fragsize must be still within the preimage,
|
||||
* and either case, the old piece should match the preimage
|
||||
* exactly.
|
||||
*/
|
||||
if ((match_end
|
||||
? (try + preimage->len == img->len)
|
||||
: (try + preimage->len <= img->len)) &&
|
||||
!memcmp(img->buf + try, preimage->buf, preimage->len))
|
||||
return 1;
|
||||
|
||||
if (ws_error_action != correct_ws_error)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The hunk does not apply byte-by-byte, but the hash says
|
||||
* it might with whitespace fuzz.
|
||||
*/
|
||||
fixed_buf = xmalloc(preimage->len + 1);
|
||||
buf = fixed_buf;
|
||||
orig = preimage->buf;
|
||||
target = img->buf + try;
|
||||
for (i = 0; i < preimage->nr; i++) {
|
||||
size_t fixlen; /* length after fixing the preimage */
|
||||
size_t oldlen = preimage->line[i].len;
|
||||
size_t tgtlen = img->line[try_lno + i].len;
|
||||
size_t tgtfixlen; /* length after fixing the target line */
|
||||
char tgtfixbuf[1024], *tgtfix;
|
||||
int match;
|
||||
|
||||
/* Try fixing the line in the preimage */
|
||||
fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
|
||||
|
||||
/* Try fixing the line in the target */
|
||||
if (sizeof(tgtfixbuf) < tgtlen)
|
||||
tgtfix = tgtfixbuf;
|
||||
else
|
||||
tgtfix = xmalloc(tgtlen);
|
||||
tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
|
||||
|
||||
/*
|
||||
* If they match, either the preimage was based on
|
||||
* a version before our tree fixed whitespace breakage,
|
||||
* or we are lacking a whitespace-fix patch the tree
|
||||
* the preimage was based on already had (i.e. target
|
||||
* has whitespace breakage, the preimage doesn't).
|
||||
* In either case, we are fixing the whitespace breakages
|
||||
* so we might as well take the fix together with their
|
||||
* real change.
|
||||
*/
|
||||
match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
|
||||
|
||||
if (tgtfix != tgtfixbuf)
|
||||
free(tgtfix);
|
||||
if (!match)
|
||||
goto unmatch_exit;
|
||||
|
||||
orig += oldlen;
|
||||
buf += fixlen;
|
||||
target += tgtlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Yes, the preimage is based on an older version that still
|
||||
* has whitespace breakages unfixed, and fixing them makes the
|
||||
* hunk match. Update the context lines in the postimage.
|
||||
*/
|
||||
update_pre_post_images(preimage, postimage,
|
||||
fixed_buf, buf - fixed_buf);
|
||||
return 1;
|
||||
|
||||
unmatch_exit:
|
||||
free(fixed_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_pos(struct image *img,
|
||||
struct image *preimage,
|
||||
struct image *postimage,
|
||||
int line,
|
||||
unsigned ws_rule,
|
||||
int match_beginning, int match_end)
|
||||
{
|
||||
int i;
|
||||
unsigned long backwards, forwards, try;
|
||||
int backwards_lno, forwards_lno, try_lno;
|
||||
|
||||
if (preimage->nr > img->nr)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* If match_begining or match_end is specified, there is no
|
||||
* point starting from a wrong line that will never match and
|
||||
* wander around and wait for a match at the specified end.
|
||||
*/
|
||||
if (match_beginning)
|
||||
line = 0;
|
||||
else if (match_end)
|
||||
line = img->nr - preimage->nr;
|
||||
|
||||
if (line > img->nr)
|
||||
line = img->nr;
|
||||
|
||||
try = 0;
|
||||
for (i = 0; i < line; i++)
|
||||
try += img->line[i].len;
|
||||
|
||||
/*
|
||||
* There's probably some smart way to do this, but I'll leave
|
||||
* that to the smart and beautiful people. I'm simple and stupid.
|
||||
*/
|
||||
backwards = start;
|
||||
forwards = start;
|
||||
for (i = 0; ; i++) {
|
||||
unsigned long try;
|
||||
int n;
|
||||
backwards = try;
|
||||
backwards_lno = line;
|
||||
forwards = try;
|
||||
forwards_lno = line;
|
||||
try_lno = line;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
if (match_fragment(img, preimage, postimage,
|
||||
try, try_lno, ws_rule,
|
||||
match_beginning, match_end))
|
||||
return try_lno;
|
||||
|
||||
again:
|
||||
if (backwards_lno == 0 && forwards_lno == img->nr)
|
||||
break;
|
||||
|
||||
/* "backward" */
|
||||
if (i & 1) {
|
||||
if (!backwards) {
|
||||
if (forwards + fragsize > size)
|
||||
break;
|
||||
continue;
|
||||
if (backwards_lno == 0) {
|
||||
i++;
|
||||
goto again;
|
||||
}
|
||||
do {
|
||||
--backwards;
|
||||
} while (backwards && buf[backwards-1] != '\n');
|
||||
backwards_lno--;
|
||||
backwards -= img->line[backwards_lno].len;
|
||||
try = backwards;
|
||||
try_lno = backwards_lno;
|
||||
} else {
|
||||
while (forwards + fragsize <= size) {
|
||||
if (buf[forwards++] == '\n')
|
||||
break;
|
||||
if (forwards_lno == img->nr) {
|
||||
i++;
|
||||
goto again;
|
||||
}
|
||||
forwards += img->line[forwards_lno].len;
|
||||
forwards_lno++;
|
||||
try = forwards;
|
||||
try_lno = forwards_lno;
|
||||
}
|
||||
|
||||
if (try + fragsize > size)
|
||||
continue;
|
||||
if (memcmp(buf + try, fragment, fragsize))
|
||||
continue;
|
||||
n = (i >> 1)+1;
|
||||
if (i & 1)
|
||||
n = -n;
|
||||
*lines = n;
|
||||
return try;
|
||||
}
|
||||
|
||||
/*
|
||||
* We should start searching forward and backward.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void remove_first_line(const char **rbuf, int *rsize)
|
||||
static void remove_first_line(struct image *img)
|
||||
{
|
||||
const char *buf = *rbuf;
|
||||
int size = *rsize;
|
||||
unsigned long offset;
|
||||
offset = 0;
|
||||
while (offset <= size) {
|
||||
if (buf[offset++] == '\n')
|
||||
break;
|
||||
}
|
||||
*rsize = size - offset;
|
||||
*rbuf = buf + offset;
|
||||
img->buf += img->line[0].len;
|
||||
img->len -= img->line[0].len;
|
||||
img->line++;
|
||||
img->nr--;
|
||||
}
|
||||
|
||||
static void remove_last_line(const char **rbuf, int *rsize)
|
||||
static void remove_last_line(struct image *img)
|
||||
{
|
||||
const char *buf = *rbuf;
|
||||
int size = *rsize;
|
||||
unsigned long offset;
|
||||
offset = size - 1;
|
||||
while (offset > 0) {
|
||||
if (buf[--offset] == '\n')
|
||||
break;
|
||||
}
|
||||
*rsize = offset + 1;
|
||||
img->len -= img->line[--img->nr].len;
|
||||
}
|
||||
|
||||
static int apply_line(char *output, const char *patch, int plen,
|
||||
unsigned ws_rule)
|
||||
static void update_image(struct image *img,
|
||||
int applied_pos,
|
||||
struct image *preimage,
|
||||
struct image *postimage)
|
||||
{
|
||||
/*
|
||||
* plen is number of bytes to be copied from patch,
|
||||
* starting at patch+1 (patch[0] is '+'). Typically
|
||||
* patch[plen] is '\n', unless this is the incomplete
|
||||
* last line.
|
||||
* remove the copy of preimage at offset in img
|
||||
* and replace it with postimage
|
||||
*/
|
||||
int i;
|
||||
int add_nl_to_tail = 0;
|
||||
int fixed = 0;
|
||||
int last_tab_in_indent = 0;
|
||||
int last_space_in_indent = 0;
|
||||
int need_fix_leading_space = 0;
|
||||
char *buf;
|
||||
int i, nr;
|
||||
size_t remove_count, insert_count, applied_at = 0;
|
||||
char *result;
|
||||
|
||||
if ((ws_error_action != correct_ws_error) || !whitespace_error ||
|
||||
*patch != '+') {
|
||||
memcpy(output, patch + 1, plen);
|
||||
return plen;
|
||||
}
|
||||
for (i = 0; i < applied_pos; i++)
|
||||
applied_at += img->line[i].len;
|
||||
|
||||
/*
|
||||
* Strip trailing whitespace
|
||||
*/
|
||||
if ((ws_rule & WS_TRAILING_SPACE) &&
|
||||
(1 < plen && isspace(patch[plen-1]))) {
|
||||
if (patch[plen] == '\n')
|
||||
add_nl_to_tail = 1;
|
||||
plen--;
|
||||
while (0 < plen && isspace(patch[plen]))
|
||||
plen--;
|
||||
fixed = 1;
|
||||
}
|
||||
remove_count = 0;
|
||||
for (i = 0; i < preimage->nr; i++)
|
||||
remove_count += img->line[applied_pos + i].len;
|
||||
insert_count = postimage->len;
|
||||
|
||||
/*
|
||||
* Check leading whitespaces (indent)
|
||||
*/
|
||||
for (i = 1; i < plen; i++) {
|
||||
char ch = patch[i];
|
||||
if (ch == '\t') {
|
||||
last_tab_in_indent = i;
|
||||
if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
|
||||
0 < last_space_in_indent)
|
||||
need_fix_leading_space = 1;
|
||||
} else if (ch == ' ') {
|
||||
last_space_in_indent = i;
|
||||
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
|
||||
8 <= i - last_tab_in_indent)
|
||||
need_fix_leading_space = 1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
buf = output;
|
||||
if (need_fix_leading_space) {
|
||||
int consecutive_spaces = 0;
|
||||
int last = last_tab_in_indent + 1;
|
||||
|
||||
if (ws_rule & WS_INDENT_WITH_NON_TAB) {
|
||||
/* have "last" point at one past the indent */
|
||||
if (last_tab_in_indent < last_space_in_indent)
|
||||
last = last_space_in_indent + 1;
|
||||
else
|
||||
last = last_tab_in_indent + 1;
|
||||
}
|
||||
/* Adjust the contents */
|
||||
result = xmalloc(img->len + insert_count - remove_count + 1);
|
||||
memcpy(result, img->buf, applied_at);
|
||||
memcpy(result + applied_at, postimage->buf, postimage->len);
|
||||
memcpy(result + applied_at + postimage->len,
|
||||
img->buf + (applied_at + remove_count),
|
||||
img->len - (applied_at + remove_count));
|
||||
free(img->buf);
|
||||
img->buf = result;
|
||||
img->len += insert_count - remove_count;
|
||||
result[img->len] = '\0';
|
||||
|
||||
/* Adjust the line table */
|
||||
nr = img->nr + postimage->nr - preimage->nr;
|
||||
if (preimage->nr < postimage->nr) {
|
||||
/*
|
||||
* between patch[1..last], strip the funny spaces,
|
||||
* updating them to tab as needed.
|
||||
* NOTE: this knows that we never call remove_first_line()
|
||||
* on anything other than pre/post image.
|
||||
*/
|
||||
for (i = 1; i < last; i++, plen--) {
|
||||
char ch = patch[i];
|
||||
if (ch != ' ') {
|
||||
consecutive_spaces = 0;
|
||||
*output++ = ch;
|
||||
} else {
|
||||
consecutive_spaces++;
|
||||
if (consecutive_spaces == 8) {
|
||||
*output++ = '\t';
|
||||
consecutive_spaces = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (0 < consecutive_spaces--)
|
||||
*output++ = ' ';
|
||||
fixed = 1;
|
||||
i = last;
|
||||
img->line = xrealloc(img->line, nr * sizeof(*img->line));
|
||||
img->line_allocated = img->line;
|
||||
}
|
||||
else
|
||||
i = 1;
|
||||
|
||||
memcpy(output, patch + i, plen);
|
||||
if (add_nl_to_tail)
|
||||
output[plen++] = '\n';
|
||||
if (fixed)
|
||||
applied_after_fixing_ws++;
|
||||
return output + plen - buf;
|
||||
if (preimage->nr != postimage->nr)
|
||||
memmove(img->line + applied_pos + postimage->nr,
|
||||
img->line + applied_pos + preimage->nr,
|
||||
(img->nr - (applied_pos + preimage->nr)) *
|
||||
sizeof(*img->line));
|
||||
memcpy(img->line + applied_pos,
|
||||
postimage->line,
|
||||
postimage->nr * sizeof(*img->line));
|
||||
img->nr = nr;
|
||||
}
|
||||
|
||||
static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
static int apply_one_fragment(struct image *img, struct fragment *frag,
|
||||
int inaccurate_eof, unsigned ws_rule)
|
||||
{
|
||||
int match_beginning, match_end;
|
||||
const char *patch = frag->patch;
|
||||
int offset, size = frag->size;
|
||||
char *old = xmalloc(size);
|
||||
char *new = xmalloc(size);
|
||||
const char *oldlines, *newlines;
|
||||
int oldsize = 0, newsize = 0;
|
||||
int size = frag->size;
|
||||
char *old, *new, *oldlines, *newlines;
|
||||
int new_blank_lines_at_end = 0;
|
||||
unsigned long leading, trailing;
|
||||
int pos, lines;
|
||||
int pos, applied_pos;
|
||||
struct image preimage;
|
||||
struct image postimage;
|
||||
|
||||
memset(&preimage, 0, sizeof(preimage));
|
||||
memset(&postimage, 0, sizeof(postimage));
|
||||
oldlines = xmalloc(size);
|
||||
newlines = xmalloc(size);
|
||||
|
||||
old = oldlines;
|
||||
new = newlines;
|
||||
while (size > 0) {
|
||||
char first;
|
||||
int len = linelen(patch, size);
|
||||
int plen;
|
||||
int plen, added;
|
||||
int added_blank_line = 0;
|
||||
|
||||
if (!len)
|
||||
@@ -1670,7 +1859,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
* followed by "\ No newline", then we also remove the
|
||||
* last one (which is the newline, of course).
|
||||
*/
|
||||
plen = len-1;
|
||||
plen = len - 1;
|
||||
if (len < size && patch[len] == '\\')
|
||||
plen--;
|
||||
first = *patch;
|
||||
@@ -1687,25 +1876,40 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
if (plen < 0)
|
||||
/* ... followed by '\No newline'; nothing */
|
||||
break;
|
||||
old[oldsize++] = '\n';
|
||||
new[newsize++] = '\n';
|
||||
*old++ = '\n';
|
||||
*new++ = '\n';
|
||||
add_line_info(&preimage, "\n", 1, LINE_COMMON);
|
||||
add_line_info(&postimage, "\n", 1, LINE_COMMON);
|
||||
break;
|
||||
case ' ':
|
||||
case '-':
|
||||
memcpy(old + oldsize, patch + 1, plen);
|
||||
oldsize += plen;
|
||||
memcpy(old, patch + 1, plen);
|
||||
add_line_info(&preimage, old, plen,
|
||||
(first == ' ' ? LINE_COMMON : 0));
|
||||
old += plen;
|
||||
if (first == '-')
|
||||
break;
|
||||
/* Fall-through for ' ' */
|
||||
case '+':
|
||||
if (first != '+' || !no_add) {
|
||||
int added = apply_line(new + newsize, patch,
|
||||
plen, ws_rule);
|
||||
newsize += added;
|
||||
if (first == '+' &&
|
||||
added == 1 && new[newsize-1] == '\n')
|
||||
added_blank_line = 1;
|
||||
/* --no-add does not add new lines */
|
||||
if (first == '+' && no_add)
|
||||
break;
|
||||
|
||||
if (first != '+' ||
|
||||
!whitespace_error ||
|
||||
ws_error_action != correct_ws_error) {
|
||||
memcpy(new, patch + 1, plen);
|
||||
added = plen;
|
||||
}
|
||||
else {
|
||||
added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
|
||||
}
|
||||
add_line_info(&postimage, new, added,
|
||||
(first == '+' ? 0 : LINE_COMMON));
|
||||
new += added;
|
||||
if (first == '+' &&
|
||||
added == 1 && new[-1] == '\n')
|
||||
added_blank_line = 1;
|
||||
break;
|
||||
case '@': case '\\':
|
||||
/* Ignore it, we already handled it */
|
||||
@@ -1722,16 +1926,13 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
patch += len;
|
||||
size -= len;
|
||||
}
|
||||
|
||||
if (inaccurate_eof &&
|
||||
oldsize > 0 && old[oldsize - 1] == '\n' &&
|
||||
newsize > 0 && new[newsize - 1] == '\n') {
|
||||
oldsize--;
|
||||
newsize--;
|
||||
old > oldlines && old[-1] == '\n' &&
|
||||
new > newlines && new[-1] == '\n') {
|
||||
old--;
|
||||
new--;
|
||||
}
|
||||
|
||||
oldlines = old;
|
||||
newlines = new;
|
||||
leading = frag->leading;
|
||||
trailing = frag->trailing;
|
||||
|
||||
@@ -1752,33 +1953,21 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
match_end = !trailing;
|
||||
}
|
||||
|
||||
lines = 0;
|
||||
pos = frag->newpos;
|
||||
pos = frag->newpos ? (frag->newpos - 1) : 0;
|
||||
preimage.buf = oldlines;
|
||||
preimage.len = old - oldlines;
|
||||
postimage.buf = newlines;
|
||||
postimage.len = new - newlines;
|
||||
preimage.line = preimage.line_allocated;
|
||||
postimage.line = postimage.line_allocated;
|
||||
|
||||
for (;;) {
|
||||
offset = find_offset(buf->buf, buf->len,
|
||||
oldlines, oldsize, pos, &lines);
|
||||
if (match_end && offset + oldsize != buf->len)
|
||||
offset = -1;
|
||||
if (match_beginning && offset)
|
||||
offset = -1;
|
||||
if (offset >= 0) {
|
||||
if (ws_error_action == correct_ws_error &&
|
||||
(buf->len - oldsize - offset == 0)) /* end of file? */
|
||||
newsize -= new_blank_lines_at_end;
|
||||
|
||||
/* Warn if it was necessary to reduce the number
|
||||
* of context lines.
|
||||
*/
|
||||
if ((leading != frag->leading) ||
|
||||
(trailing != frag->trailing))
|
||||
fprintf(stderr, "Context reduced to (%ld/%ld)"
|
||||
" to apply fragment at %d\n",
|
||||
leading, trailing, pos + lines);
|
||||
applied_pos = find_pos(img, &preimage, &postimage, pos,
|
||||
ws_rule, match_beginning, match_end);
|
||||
|
||||
strbuf_splice(buf, offset, oldsize, newlines, newsize);
|
||||
offset = 0;
|
||||
if (applied_pos >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Am I at my context limits? */
|
||||
if ((leading <= p_context) && (trailing <= p_context))
|
||||
@@ -1787,33 +1976,64 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
match_beginning = match_end = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reduce the number of context lines; reduce both
|
||||
* leading and trailing if they are equal otherwise
|
||||
* just reduce the larger context.
|
||||
*/
|
||||
if (leading >= trailing) {
|
||||
remove_first_line(&oldlines, &oldsize);
|
||||
remove_first_line(&newlines, &newsize);
|
||||
remove_first_line(&preimage);
|
||||
remove_first_line(&postimage);
|
||||
pos--;
|
||||
leading--;
|
||||
}
|
||||
if (trailing > leading) {
|
||||
remove_last_line(&oldlines, &oldsize);
|
||||
remove_last_line(&newlines, &newsize);
|
||||
remove_last_line(&preimage);
|
||||
remove_last_line(&postimage);
|
||||
trailing--;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset && apply_verbosely)
|
||||
error("while searching for:\n%.*s", oldsize, oldlines);
|
||||
if (applied_pos >= 0) {
|
||||
if (ws_error_action == correct_ws_error &&
|
||||
new_blank_lines_at_end &&
|
||||
postimage.nr + applied_pos == img->nr) {
|
||||
/*
|
||||
* If the patch application adds blank lines
|
||||
* at the end, and if the patch applies at the
|
||||
* end of the image, remove those added blank
|
||||
* lines.
|
||||
*/
|
||||
while (new_blank_lines_at_end--)
|
||||
remove_last_line(&postimage);
|
||||
}
|
||||
|
||||
free(old);
|
||||
free(new);
|
||||
return offset;
|
||||
/*
|
||||
* Warn if it was necessary to reduce the number
|
||||
* of context lines.
|
||||
*/
|
||||
if ((leading != frag->leading) ||
|
||||
(trailing != frag->trailing))
|
||||
fprintf(stderr, "Context reduced to (%ld/%ld)"
|
||||
" to apply fragment at %d\n",
|
||||
leading, trailing, applied_pos+1);
|
||||
update_image(img, applied_pos, &preimage, &postimage);
|
||||
} else {
|
||||
if (apply_verbosely)
|
||||
error("while searching for:\n%.*s",
|
||||
(int)(old - oldlines), oldlines);
|
||||
}
|
||||
|
||||
free(oldlines);
|
||||
free(newlines);
|
||||
free(preimage.line_allocated);
|
||||
free(postimage.line_allocated);
|
||||
|
||||
return (applied_pos < 0);
|
||||
}
|
||||
|
||||
static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
|
||||
static int apply_binary_fragment(struct image *img, struct patch *patch)
|
||||
{
|
||||
struct fragment *fragment = patch->fragments;
|
||||
unsigned long len;
|
||||
@@ -1830,22 +2050,26 @@ static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
|
||||
}
|
||||
switch (fragment->binary_patch_method) {
|
||||
case BINARY_DELTA_DEFLATED:
|
||||
dst = patch_delta(buf->buf, buf->len, fragment->patch,
|
||||
dst = patch_delta(img->buf, img->len, fragment->patch,
|
||||
fragment->size, &len);
|
||||
if (!dst)
|
||||
return -1;
|
||||
/* XXX patch_delta NUL-terminates */
|
||||
strbuf_attach(buf, dst, len, len + 1);
|
||||
clear_image(img);
|
||||
img->buf = dst;
|
||||
img->len = len;
|
||||
return 0;
|
||||
case BINARY_LITERAL_DEFLATED:
|
||||
strbuf_reset(buf);
|
||||
strbuf_add(buf, fragment->patch, fragment->size);
|
||||
clear_image(img);
|
||||
img->len = fragment->size;
|
||||
img->buf = xmalloc(img->len+1);
|
||||
memcpy(img->buf, fragment->patch, img->len);
|
||||
img->buf[img->len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
static int apply_binary(struct image *img, struct patch *patch)
|
||||
{
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
unsigned char sha1[20];
|
||||
@@ -1866,7 +2090,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
* See if the old one matches what the patch
|
||||
* applies to.
|
||||
*/
|
||||
hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
|
||||
hash_sha1_file(img->buf, img->len, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
|
||||
return error("the patch applies to '%s' (%s), "
|
||||
"which does not match the "
|
||||
@@ -1875,14 +2099,14 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
}
|
||||
else {
|
||||
/* Otherwise, the old one must be empty. */
|
||||
if (buf->len)
|
||||
if (img->len)
|
||||
return error("the patch applies to an empty "
|
||||
"'%s' but it is not empty", name);
|
||||
}
|
||||
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1);
|
||||
if (is_null_sha1(sha1)) {
|
||||
strbuf_release(buf);
|
||||
clear_image(img);
|
||||
return 0; /* deletion patch */
|
||||
}
|
||||
|
||||
@@ -1897,20 +2121,21 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
return error("the necessary postimage %s for "
|
||||
"'%s' cannot be read",
|
||||
patch->new_sha1_prefix, name);
|
||||
/* XXX read_sha1_file NUL-terminates */
|
||||
strbuf_attach(buf, result, size, size + 1);
|
||||
clear_image(img);
|
||||
img->buf = result;
|
||||
img->len = size;
|
||||
} else {
|
||||
/*
|
||||
* We have verified buf matches the preimage;
|
||||
* apply the patch data to it, which is stored
|
||||
* in the patch->fragments->{patch,size}.
|
||||
*/
|
||||
if (apply_binary_fragment(buf, patch))
|
||||
if (apply_binary_fragment(img, patch))
|
||||
return error("binary patch does not apply to '%s'",
|
||||
name);
|
||||
|
||||
/* verify that the result matches */
|
||||
hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
|
||||
hash_sha1_file(img->buf, img->len, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
|
||||
return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
|
||||
name, patch->new_sha1_prefix, sha1_to_hex(sha1));
|
||||
@@ -1919,7 +2144,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_fragments(struct strbuf *buf, struct patch *patch)
|
||||
static int apply_fragments(struct image *img, struct patch *patch)
|
||||
{
|
||||
struct fragment *frag = patch->fragments;
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
@@ -1927,10 +2152,10 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)
|
||||
unsigned inaccurate_eof = patch->inaccurate_eof;
|
||||
|
||||
if (patch->is_binary)
|
||||
return apply_binary(buf, patch);
|
||||
return apply_binary(img, patch);
|
||||
|
||||
while (frag) {
|
||||
if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
|
||||
if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
|
||||
error("patch failed: %s:%ld", name, frag->oldpos);
|
||||
if (!apply_with_reject)
|
||||
return -1;
|
||||
@@ -1966,6 +2191,9 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
|
||||
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
|
||||
{
|
||||
struct strbuf buf;
|
||||
struct image image;
|
||||
size_t len;
|
||||
char *img;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
if (cached) {
|
||||
@@ -1988,9 +2216,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
|
||||
}
|
||||
}
|
||||
|
||||
if (apply_fragments(&buf, patch) < 0)
|
||||
img = strbuf_detach(&buf, &len);
|
||||
prepare_image(&image, img, len, !patch->is_binary);
|
||||
|
||||
if (apply_fragments(&image, patch) < 0)
|
||||
return -1; /* note with --reject this succeeds. */
|
||||
patch->result = strbuf_detach(&buf, &patch->resultsize);
|
||||
patch->result = image.buf;
|
||||
patch->resultsize = image.len;
|
||||
free(image.line_allocated);
|
||||
|
||||
if (0 < patch->is_delete && patch->resultsize)
|
||||
return error("removal patch leaves file contents");
|
||||
|
||||
@@ -123,8 +123,7 @@ static inline struct origin *origin_incref(struct origin *o)
|
||||
static void origin_decref(struct origin *o)
|
||||
{
|
||||
if (o && --o->refcnt <= 0) {
|
||||
if (o->file.ptr)
|
||||
free(o->file.ptr);
|
||||
free(o->file.ptr);
|
||||
free(o);
|
||||
}
|
||||
}
|
||||
|
||||
158
builtin-branch.c
158
builtin-branch.c
@@ -12,6 +12,7 @@
|
||||
#include "builtin.h"
|
||||
#include "remote.h"
|
||||
#include "parse-options.h"
|
||||
#include "branch.h"
|
||||
|
||||
static const char * const builtin_branch_usage[] = {
|
||||
"git-branch [options] [-r | -a]",
|
||||
@@ -29,8 +30,6 @@ static const char * const builtin_branch_usage[] = {
|
||||
static const char *head;
|
||||
static unsigned char head_sha1[20];
|
||||
|
||||
static int branch_track = 1;
|
||||
|
||||
static int branch_use_color = -1;
|
||||
static char branch_colors[][COLOR_MAXLEN] = {
|
||||
"\033[m", /* reset */
|
||||
@@ -75,10 +74,6 @@ static int git_branch_config(const char *var, const char *value)
|
||||
color_parse(value, var, branch_colors[slot]);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "branch.autosetupmerge")) {
|
||||
branch_track = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
return git_color_default_config(var, value);
|
||||
}
|
||||
|
||||
@@ -126,8 +121,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name)
|
||||
free(name);
|
||||
free(name);
|
||||
|
||||
name = xstrdup(mkpath(fmt, argv[i]));
|
||||
if (!resolve_ref(name, sha1, 1, NULL)) {
|
||||
@@ -172,8 +166,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||
}
|
||||
}
|
||||
|
||||
if (name)
|
||||
free(name);
|
||||
free(name);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
@@ -359,141 +352,6 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
|
||||
free_ref_list(&ref_list);
|
||||
}
|
||||
|
||||
struct tracking {
|
||||
struct refspec spec;
|
||||
char *src;
|
||||
const char *remote;
|
||||
int matches;
|
||||
};
|
||||
|
||||
static int find_tracked_branch(struct remote *remote, void *priv)
|
||||
{
|
||||
struct tracking *tracking = priv;
|
||||
|
||||
if (!remote_find_tracking(remote, &tracking->spec)) {
|
||||
if (++tracking->matches == 1) {
|
||||
tracking->src = tracking->spec.src;
|
||||
tracking->remote = remote->name;
|
||||
} else {
|
||||
free(tracking->spec.src);
|
||||
if (tracking->src) {
|
||||
free(tracking->src);
|
||||
tracking->src = NULL;
|
||||
}
|
||||
}
|
||||
tracking->spec.src = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is called when new_ref is branched off of orig_ref, and tries
|
||||
* to infer the settings for branch.<new_ref>.{remote,merge} from the
|
||||
* config.
|
||||
*/
|
||||
static int setup_tracking(const char *new_ref, const char *orig_ref)
|
||||
{
|
||||
char key[1024];
|
||||
struct tracking tracking;
|
||||
|
||||
if (strlen(new_ref) > 1024 - 7 - 7 - 1)
|
||||
return error("Tracking not set up: name too long: %s",
|
||||
new_ref);
|
||||
|
||||
memset(&tracking, 0, sizeof(tracking));
|
||||
tracking.spec.dst = (char *)orig_ref;
|
||||
if (for_each_remote(find_tracked_branch, &tracking) ||
|
||||
!tracking.matches)
|
||||
return 1;
|
||||
|
||||
if (tracking.matches > 1)
|
||||
return error("Not tracking: ambiguous information for ref %s",
|
||||
orig_ref);
|
||||
|
||||
if (tracking.matches == 1) {
|
||||
sprintf(key, "branch.%s.remote", new_ref);
|
||||
git_config_set(key, tracking.remote ? tracking.remote : ".");
|
||||
sprintf(key, "branch.%s.merge", new_ref);
|
||||
git_config_set(key, tracking.src);
|
||||
free(tracking.src);
|
||||
printf("Branch %s set up to track remote branch %s.\n",
|
||||
new_ref, orig_ref);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_branch(const char *name, const char *start_name,
|
||||
int force, int reflog, int track)
|
||||
{
|
||||
struct ref_lock *lock;
|
||||
struct commit *commit;
|
||||
unsigned char sha1[20];
|
||||
char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
|
||||
int forcing = 0;
|
||||
|
||||
snprintf(ref, sizeof ref, "refs/heads/%s", name);
|
||||
if (check_ref_format(ref))
|
||||
die("'%s' is not a valid branch name.", name);
|
||||
|
||||
if (resolve_ref(ref, sha1, 1, NULL)) {
|
||||
if (!force)
|
||||
die("A branch named '%s' already exists.", name);
|
||||
else if (!is_bare_repository() && !strcmp(head, name))
|
||||
die("Cannot force update the current branch.");
|
||||
forcing = 1;
|
||||
}
|
||||
|
||||
real_ref = NULL;
|
||||
if (get_sha1(start_name, sha1))
|
||||
die("Not a valid object name: '%s'.", start_name);
|
||||
|
||||
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
|
||||
case 0:
|
||||
/* Not branching from any existing branch */
|
||||
real_ref = NULL;
|
||||
break;
|
||||
case 1:
|
||||
/* Unique completion -- good */
|
||||
break;
|
||||
default:
|
||||
die("Ambiguous object name: '%s'.", start_name);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((commit = lookup_commit_reference(sha1)) == NULL)
|
||||
die("Not a valid branch point: '%s'.", start_name);
|
||||
hashcpy(sha1, commit->object.sha1);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, NULL, 0);
|
||||
if (!lock)
|
||||
die("Failed to lock ref for update: %s.", strerror(errno));
|
||||
|
||||
if (reflog)
|
||||
log_all_ref_updates = 1;
|
||||
|
||||
if (forcing)
|
||||
snprintf(msg, sizeof msg, "branch: Reset from %s",
|
||||
start_name);
|
||||
else
|
||||
snprintf(msg, sizeof msg, "branch: Created from %s",
|
||||
start_name);
|
||||
|
||||
/* When branching off a remote branch, set up so that git-pull
|
||||
automatically merges from there. So far, this is only done for
|
||||
remotes registered via .git/config. */
|
||||
if (real_ref && track)
|
||||
setup_tracking(name, real_ref);
|
||||
|
||||
if (write_ref_sha1(lock, sha1, msg) < 0)
|
||||
die("Failed to write ref: %s.", strerror(errno));
|
||||
|
||||
if (real_ref)
|
||||
free(real_ref);
|
||||
}
|
||||
|
||||
static void rename_branch(const char *oldname, const char *newname, int force)
|
||||
{
|
||||
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
|
||||
@@ -554,14 +412,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int delete = 0, rename = 0, force_create = 0;
|
||||
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
|
||||
int reflog = 0, track;
|
||||
int reflog = 0;
|
||||
enum branch_track track;
|
||||
int kinds = REF_LOCAL_BRANCH;
|
||||
struct commit_list *with_commit = NULL;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_GROUP("Generic options"),
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"),
|
||||
OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))",
|
||||
BRANCH_TRACK_EXPLICIT),
|
||||
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
|
||||
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
|
||||
REF_REMOTE_BRANCH),
|
||||
@@ -592,7 +452,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
if (branch_use_color == -1)
|
||||
branch_use_color = git_use_color_default;
|
||||
|
||||
track = branch_track;
|
||||
track = git_branch_track;
|
||||
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
|
||||
if (!!delete + !!rename + !!force_create > 1)
|
||||
usage_with_options(builtin_branch_usage, options);
|
||||
@@ -618,7 +478,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
else if (rename && (argc == 2))
|
||||
rename_branch(argv[0], argv[1], rename > 1);
|
||||
else if (argc <= 2)
|
||||
create_branch(argv[0], (argc == 2) ? argv[1] : head,
|
||||
create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
|
||||
force_create, reflog, track);
|
||||
else
|
||||
usage_with_options(builtin_branch_usage, options);
|
||||
|
||||
577
builtin-checkout.c
Normal file
577
builtin-checkout.c
Normal file
@@ -0,0 +1,577 @@
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "parse-options.h"
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
#include "tree.h"
|
||||
#include "tree-walk.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "dir.h"
|
||||
#include "run-command.h"
|
||||
#include "merge-recursive.h"
|
||||
#include "branch.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "remote.h"
|
||||
|
||||
static const char * const checkout_usage[] = {
|
||||
"git checkout [options] <branch>",
|
||||
"git checkout [options] [<branch>] -- <file>...",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int post_checkout_hook(struct commit *old, struct commit *new,
|
||||
int changed)
|
||||
{
|
||||
struct child_process proc;
|
||||
const char *name = git_path("hooks/post-checkout");
|
||||
const char *argv[5];
|
||||
|
||||
if (access(name, X_OK) < 0)
|
||||
return 0;
|
||||
|
||||
memset(&proc, 0, sizeof(proc));
|
||||
argv[0] = name;
|
||||
argv[1] = xstrdup(sha1_to_hex(old->object.sha1));
|
||||
argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
|
||||
argv[3] = changed ? "1" : "0";
|
||||
argv[4] = NULL;
|
||||
proc.argv = argv;
|
||||
proc.no_stdin = 1;
|
||||
proc.stdout_to_stderr = 1;
|
||||
return run_command(&proc);
|
||||
}
|
||||
|
||||
static int update_some(const unsigned char *sha1, const char *base, int baselen,
|
||||
const char *pathname, unsigned mode, int stage)
|
||||
{
|
||||
int len;
|
||||
struct cache_entry *ce;
|
||||
|
||||
if (S_ISGITLINK(mode))
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(mode))
|
||||
return READ_TREE_RECURSIVE;
|
||||
|
||||
len = baselen + strlen(pathname);
|
||||
ce = xcalloc(1, cache_entry_size(len));
|
||||
hashcpy(ce->sha1, sha1);
|
||||
memcpy(ce->name, base, baselen);
|
||||
memcpy(ce->name + baselen, pathname, len - baselen);
|
||||
ce->ce_flags = create_ce_flags(len, 0);
|
||||
ce->ce_mode = create_ce_mode(mode);
|
||||
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_tree_some(struct tree *tree, const char **pathspec)
|
||||
{
|
||||
read_tree_recursive(tree, "", 0, 0, pathspec, update_some);
|
||||
|
||||
/* update the index with the given tree's info
|
||||
* for all args, expanding wildcards, and exit
|
||||
* with any non-zero return code.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checkout_paths(struct tree *source_tree, const char **pathspec)
|
||||
{
|
||||
int pos;
|
||||
struct checkout state;
|
||||
static char *ps_matched;
|
||||
unsigned char rev[20];
|
||||
int flag;
|
||||
struct commit *head;
|
||||
|
||||
int newfd;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
newfd = hold_locked_index(lock_file, 1);
|
||||
read_cache();
|
||||
|
||||
if (source_tree)
|
||||
read_tree_some(source_tree, pathspec);
|
||||
|
||||
for (pos = 0; pathspec[pos]; pos++)
|
||||
;
|
||||
ps_matched = xcalloc(1, pos);
|
||||
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
struct cache_entry *ce = active_cache[pos];
|
||||
pathspec_match(pathspec, ps_matched, ce->name, 0);
|
||||
}
|
||||
|
||||
if (report_path_error(ps_matched, pathspec, 0))
|
||||
return 1;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.force = 1;
|
||||
state.refresh_cache = 1;
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
struct cache_entry *ce = active_cache[pos];
|
||||
if (pathspec_match(pathspec, NULL, ce->name, 0)) {
|
||||
checkout_entry(ce, &state, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
die("unable to write new index file");
|
||||
|
||||
resolve_ref("HEAD", rev, 0, &flag);
|
||||
head = lookup_commit_reference_gently(rev, 1);
|
||||
|
||||
return post_checkout_hook(head, head, 0);
|
||||
}
|
||||
|
||||
static void show_local_changes(struct object *head)
|
||||
{
|
||||
struct rev_info rev;
|
||||
/* I think we want full paths, even if we're in a subdirectory. */
|
||||
init_revisions(&rev, NULL);
|
||||
rev.abbrev = 0;
|
||||
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
|
||||
add_pending_object(&rev, head, NULL);
|
||||
run_diff_index(&rev, 0);
|
||||
}
|
||||
|
||||
static void describe_detached_head(char *msg, struct commit *commit)
|
||||
{
|
||||
struct strbuf sb;
|
||||
strbuf_init(&sb, 0);
|
||||
parse_commit(commit);
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, "", "", 0, 0);
|
||||
fprintf(stderr, "%s %s... %s\n", msg,
|
||||
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
static int reset_to_new(struct tree *tree, int quiet)
|
||||
{
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc tree_desc;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.head_idx = -1;
|
||||
opts.update = 1;
|
||||
opts.reset = 1;
|
||||
opts.merge = 1;
|
||||
opts.fn = oneway_merge;
|
||||
opts.verbose_update = !quiet;
|
||||
opts.src_index = &the_index;
|
||||
opts.dst_index = &the_index;
|
||||
parse_tree(tree);
|
||||
init_tree_desc(&tree_desc, tree->buffer, tree->size);
|
||||
if (unpack_trees(1, &tree_desc, &opts))
|
||||
return 128;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_clean_to_new(struct tree *tree, int quiet)
|
||||
{
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc tree_desc;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.head_idx = -1;
|
||||
opts.skip_unmerged = 1;
|
||||
opts.reset = 1;
|
||||
opts.merge = 1;
|
||||
opts.fn = oneway_merge;
|
||||
opts.verbose_update = !quiet;
|
||||
opts.src_index = &the_index;
|
||||
opts.dst_index = &the_index;
|
||||
parse_tree(tree);
|
||||
init_tree_desc(&tree_desc, tree->buffer, tree->size);
|
||||
if (unpack_trees(1, &tree_desc, &opts))
|
||||
exit(128);
|
||||
}
|
||||
|
||||
struct checkout_opts {
|
||||
int quiet;
|
||||
int merge;
|
||||
int force;
|
||||
|
||||
char *new_branch;
|
||||
int new_branch_log;
|
||||
enum branch_track track;
|
||||
};
|
||||
|
||||
struct branch_info {
|
||||
const char *name; /* The short name used */
|
||||
const char *path; /* The full name of a real branch */
|
||||
struct commit *commit; /* The named commit */
|
||||
};
|
||||
|
||||
static void setup_branch_path(struct branch_info *branch)
|
||||
{
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_addstr(&buf, "refs/heads/");
|
||||
strbuf_addstr(&buf, branch->name);
|
||||
branch->path = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static int merge_working_tree(struct checkout_opts *opts,
|
||||
struct branch_info *old, struct branch_info *new)
|
||||
{
|
||||
int ret;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
int newfd = hold_locked_index(lock_file, 1);
|
||||
read_cache();
|
||||
|
||||
if (opts->force) {
|
||||
ret = reset_to_new(new->commit->tree, opts->quiet);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
struct tree_desc trees[2];
|
||||
struct tree *tree;
|
||||
struct unpack_trees_options topts;
|
||||
|
||||
memset(&topts, 0, sizeof(topts));
|
||||
topts.head_idx = -1;
|
||||
topts.src_index = &the_index;
|
||||
topts.dst_index = &the_index;
|
||||
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
if (unmerged_cache()) {
|
||||
error("you need to resolve your current index first");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 2-way merge to the new branch */
|
||||
topts.update = 1;
|
||||
topts.merge = 1;
|
||||
topts.gently = opts->merge;
|
||||
topts.verbose_update = !opts->quiet;
|
||||
topts.fn = twoway_merge;
|
||||
topts.dir = xcalloc(1, sizeof(*topts.dir));
|
||||
topts.dir->show_ignored = 1;
|
||||
topts.dir->exclude_per_dir = ".gitignore";
|
||||
tree = parse_tree_indirect(old->commit->object.sha1);
|
||||
init_tree_desc(&trees[0], tree->buffer, tree->size);
|
||||
tree = parse_tree_indirect(new->commit->object.sha1);
|
||||
init_tree_desc(&trees[1], tree->buffer, tree->size);
|
||||
|
||||
if (unpack_trees(2, trees, &topts)) {
|
||||
/*
|
||||
* Unpack couldn't do a trivial merge; either
|
||||
* give up or do a real merge, depending on
|
||||
* whether the merge flag was used.
|
||||
*/
|
||||
struct tree *result;
|
||||
struct tree *work;
|
||||
if (!opts->merge)
|
||||
return 1;
|
||||
parse_commit(old->commit);
|
||||
|
||||
/* Do more real merge */
|
||||
|
||||
/*
|
||||
* We update the index fully, then write the
|
||||
* tree from the index, then merge the new
|
||||
* branch with the current tree, with the old
|
||||
* branch as the base. Then we reset the index
|
||||
* (but not the working tree) to the new
|
||||
* branch, leaving the working tree as the
|
||||
* merged version, but skipping unmerged
|
||||
* entries in the index.
|
||||
*/
|
||||
|
||||
add_files_to_cache(0, NULL, NULL);
|
||||
work = write_tree_from_memory();
|
||||
|
||||
ret = reset_to_new(new->commit->tree, opts->quiet);
|
||||
if (ret)
|
||||
return ret;
|
||||
merge_trees(new->commit->tree, work, old->commit->tree,
|
||||
new->name, "local", &result);
|
||||
reset_clean_to_new(new->commit->tree, opts->quiet);
|
||||
}
|
||||
}
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
die("unable to write new index file");
|
||||
|
||||
if (!opts->force)
|
||||
show_local_changes(&new->commit->object);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
|
||||
{
|
||||
/*
|
||||
* We have switched to a new branch; is it building on
|
||||
* top of another branch, and if so does that other branch
|
||||
* have changes we do not have yet?
|
||||
*/
|
||||
char *base;
|
||||
unsigned char sha1[20];
|
||||
struct commit *ours, *theirs;
|
||||
char symmetric[84];
|
||||
struct rev_info revs;
|
||||
const char *rev_argv[10];
|
||||
int rev_argc;
|
||||
int num_ours, num_theirs;
|
||||
const char *remote_msg;
|
||||
struct branch *branch = branch_get(new->name);
|
||||
|
||||
/*
|
||||
* Nothing to report unless we are marked to build on top of
|
||||
* somebody else.
|
||||
*/
|
||||
if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If what we used to build on no longer exists, there is
|
||||
* nothing to report.
|
||||
*/
|
||||
base = branch->merge[0]->dst;
|
||||
if (!resolve_ref(base, sha1, 1, NULL))
|
||||
return;
|
||||
|
||||
theirs = lookup_commit(sha1);
|
||||
ours = new->commit;
|
||||
if (!hashcmp(sha1, ours->object.sha1))
|
||||
return; /* we are the same */
|
||||
|
||||
/* Run "rev-list --left-right ours...theirs" internally... */
|
||||
rev_argc = 0;
|
||||
rev_argv[rev_argc++] = NULL;
|
||||
rev_argv[rev_argc++] = "--left-right";
|
||||
rev_argv[rev_argc++] = symmetric;
|
||||
rev_argv[rev_argc++] = "--";
|
||||
rev_argv[rev_argc] = NULL;
|
||||
|
||||
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
||||
strcpy(symmetric + 40, "...");
|
||||
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
||||
prepare_revision_walk(&revs);
|
||||
|
||||
/* ... and count the commits on each side. */
|
||||
num_ours = 0;
|
||||
num_theirs = 0;
|
||||
while (1) {
|
||||
struct commit *c = get_revision(&revs);
|
||||
if (!c)
|
||||
break;
|
||||
if (c->object.flags & SYMMETRIC_LEFT)
|
||||
num_ours++;
|
||||
else
|
||||
num_theirs++;
|
||||
}
|
||||
|
||||
if (!prefixcmp(base, "refs/remotes/")) {
|
||||
remote_msg = " remote";
|
||||
base += strlen("refs/remotes/");
|
||||
} else {
|
||||
remote_msg = "";
|
||||
}
|
||||
|
||||
if (!num_theirs)
|
||||
printf("Your branch is ahead of the tracked%s branch '%s' "
|
||||
"by %d commit%s.\n",
|
||||
remote_msg, base,
|
||||
num_ours, (num_ours == 1) ? "" : "s");
|
||||
else if (!num_ours)
|
||||
printf("Your branch is behind the tracked%s branch '%s' "
|
||||
"by %d commit%s,\n"
|
||||
"and can be fast-forwarded.\n",
|
||||
remote_msg, base,
|
||||
num_theirs, (num_theirs == 1) ? "" : "s");
|
||||
else
|
||||
printf("Your branch and the tracked%s branch '%s' "
|
||||
"have diverged,\nand respectively "
|
||||
"have %d and %d different commit(s) each.\n",
|
||||
remote_msg, base,
|
||||
num_ours, num_theirs);
|
||||
}
|
||||
|
||||
static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
struct branch_info *old,
|
||||
struct branch_info *new)
|
||||
{
|
||||
struct strbuf msg;
|
||||
const char *old_desc;
|
||||
if (opts->new_branch) {
|
||||
create_branch(old->name, opts->new_branch, new->name, 0,
|
||||
opts->new_branch_log, opts->track);
|
||||
new->name = opts->new_branch;
|
||||
setup_branch_path(new);
|
||||
}
|
||||
|
||||
strbuf_init(&msg, 0);
|
||||
old_desc = old->name;
|
||||
if (!old_desc)
|
||||
old_desc = sha1_to_hex(old->commit->object.sha1);
|
||||
strbuf_addf(&msg, "checkout: moving from %s to %s",
|
||||
old_desc, new->name);
|
||||
|
||||
if (new->path) {
|
||||
create_symref("HEAD", new->path, msg.buf);
|
||||
if (!opts->quiet) {
|
||||
if (old->path && !strcmp(new->path, old->path))
|
||||
fprintf(stderr, "Already on \"%s\"\n",
|
||||
new->name);
|
||||
else
|
||||
fprintf(stderr, "Switched to%s branch \"%s\"\n",
|
||||
opts->new_branch ? " a new" : "",
|
||||
new->name);
|
||||
}
|
||||
} else if (strcmp(new->name, "HEAD")) {
|
||||
update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
|
||||
REF_NODEREF, DIE_ON_ERR);
|
||||
if (!opts->quiet) {
|
||||
if (old->path)
|
||||
fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b <new_branch_name>\n", new->name);
|
||||
describe_detached_head("HEAD is now at", new->commit);
|
||||
}
|
||||
}
|
||||
remove_branch_state();
|
||||
strbuf_release(&msg);
|
||||
if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
|
||||
report_tracking(new, opts);
|
||||
}
|
||||
|
||||
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
|
||||
{
|
||||
int ret = 0;
|
||||
struct branch_info old;
|
||||
unsigned char rev[20];
|
||||
int flag;
|
||||
memset(&old, 0, sizeof(old));
|
||||
old.path = resolve_ref("HEAD", rev, 0, &flag);
|
||||
old.commit = lookup_commit_reference_gently(rev, 1);
|
||||
if (!(flag & REF_ISSYMREF))
|
||||
old.path = NULL;
|
||||
|
||||
if (old.path && !prefixcmp(old.path, "refs/heads/"))
|
||||
old.name = old.path + strlen("refs/heads/");
|
||||
|
||||
if (!new->name) {
|
||||
new->name = "HEAD";
|
||||
new->commit = old.commit;
|
||||
if (!new->commit)
|
||||
die("You are on a branch yet to be born");
|
||||
parse_commit(new->commit);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the new thing isn't a branch and isn't HEAD and we're
|
||||
* not starting a new branch, and we want messages, and we
|
||||
* weren't on a branch, and we're moving to a new commit,
|
||||
* describe the old commit.
|
||||
*/
|
||||
if (!new->path && strcmp(new->name, "HEAD") && !opts->new_branch &&
|
||||
!opts->quiet && !old.path && new->commit != old.commit)
|
||||
describe_detached_head("Previous HEAD position was", old.commit);
|
||||
|
||||
if (!old.commit) {
|
||||
if (!opts->quiet) {
|
||||
fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
|
||||
fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
|
||||
}
|
||||
opts->force = 1;
|
||||
}
|
||||
|
||||
ret = merge_working_tree(opts, &old, new);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
update_refs_for_switch(opts, &old, new);
|
||||
|
||||
return post_checkout_hook(old.commit, new->commit, 1);
|
||||
}
|
||||
|
||||
int cmd_checkout(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct checkout_opts opts;
|
||||
unsigned char rev[20];
|
||||
const char *arg;
|
||||
struct branch_info new;
|
||||
struct tree *source_tree = NULL;
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&opts.quiet),
|
||||
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
|
||||
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
|
||||
OPT_SET_INT( 0 , "track", &opts.track, "track",
|
||||
BRANCH_TRACK_EXPLICIT),
|
||||
OPT_BOOLEAN('f', NULL, &opts.force, "force"),
|
||||
OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
memset(&new, 0, sizeof(new));
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
opts.track = git_branch_track;
|
||||
|
||||
argc = parse_options(argc, argv, options, checkout_usage, 0);
|
||||
if (argc) {
|
||||
arg = argv[0];
|
||||
if (get_sha1(arg, rev))
|
||||
;
|
||||
else if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
|
||||
new.name = arg;
|
||||
setup_branch_path(&new);
|
||||
if (resolve_ref(new.path, rev, 1, NULL))
|
||||
new.commit = lookup_commit_reference(rev);
|
||||
else
|
||||
new.path = NULL;
|
||||
parse_commit(new.commit);
|
||||
source_tree = new.commit->tree;
|
||||
argv++;
|
||||
argc--;
|
||||
} else if ((source_tree = parse_tree_indirect(rev))) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc && !strcmp(argv[0], "--")) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (!opts.new_branch && (opts.track != git_branch_track))
|
||||
die("git checkout: --track and --no-track require -b");
|
||||
|
||||
if (opts.force && opts.merge)
|
||||
die("git checkout: -f and -m are incompatible");
|
||||
|
||||
if (argc) {
|
||||
const char **pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
if (!pathspec)
|
||||
die("invalid path specification");
|
||||
|
||||
/* Checkout paths */
|
||||
if (opts.new_branch || opts.force || opts.merge) {
|
||||
if (argc == 1) {
|
||||
die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
|
||||
} else {
|
||||
die("git checkout: updating paths is incompatible with switching branches/forcing");
|
||||
}
|
||||
}
|
||||
|
||||
return checkout_paths(source_tree, pathspec);
|
||||
}
|
||||
|
||||
if (new.name && !new.commit) {
|
||||
die("Cannot switch branch to a non-commit.");
|
||||
}
|
||||
|
||||
return switch_branches(&opts, &new);
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "cache.h"
|
||||
#include "dir.h"
|
||||
#include "parse-options.h"
|
||||
#include "quote.h"
|
||||
|
||||
static int force = -1; /* unset */
|
||||
|
||||
@@ -34,7 +35,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
struct dir_struct dir;
|
||||
const char *path, *base;
|
||||
static const char **pathspec;
|
||||
int prefix_offset = 0;
|
||||
struct strbuf buf;
|
||||
const char *qname;
|
||||
char *seen = NULL;
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&quiet),
|
||||
@@ -56,6 +58,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
if (ignored_only)
|
||||
dir.show_ignored = 1;
|
||||
@@ -72,8 +75,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
if (!ignored)
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
if (prefix)
|
||||
prefix_offset = strlen(prefix);
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
read_cache();
|
||||
|
||||
@@ -134,39 +135,34 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
strbuf_addstr(&directory, ent->name);
|
||||
qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
|
||||
if (show_only && (remove_directories || matches)) {
|
||||
printf("Would remove %s\n",
|
||||
directory.buf + prefix_offset);
|
||||
printf("Would remove %s\n", qname);
|
||||
} else if (remove_directories || matches) {
|
||||
if (!quiet)
|
||||
printf("Removing %s\n",
|
||||
directory.buf + prefix_offset);
|
||||
printf("Removing %s\n", qname);
|
||||
if (remove_dir_recursively(&directory, 0) != 0) {
|
||||
warning("failed to remove '%s'",
|
||||
directory.buf + prefix_offset);
|
||||
warning("failed to remove '%s'", qname);
|
||||
errors++;
|
||||
}
|
||||
} else if (show_only) {
|
||||
printf("Would not remove %s\n",
|
||||
directory.buf + prefix_offset);
|
||||
printf("Would not remove %s\n", qname);
|
||||
} else {
|
||||
printf("Not removing %s\n",
|
||||
directory.buf + prefix_offset);
|
||||
printf("Not removing %s\n", qname);
|
||||
}
|
||||
strbuf_reset(&directory);
|
||||
} else {
|
||||
if (pathspec && !matches)
|
||||
continue;
|
||||
qname = quote_path_relative(ent->name, -1, &buf, prefix);
|
||||
if (show_only) {
|
||||
printf("Would remove %s\n",
|
||||
ent->name + prefix_offset);
|
||||
printf("Would remove %s\n", qname);
|
||||
continue;
|
||||
} else if (!quiet) {
|
||||
printf("Removing %s\n",
|
||||
ent->name + prefix_offset);
|
||||
printf("Removing %s\n", qname);
|
||||
}
|
||||
if (unlink(ent->name) != 0) {
|
||||
warning("failed to remove '%s'", ent->name);
|
||||
warning("failed to remove '%s'", qname);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,8 @@ static void create_base_index(void)
|
||||
opts.head_idx = 1;
|
||||
opts.index_only = 1;
|
||||
opts.merge = 1;
|
||||
opts.src_index = &the_index;
|
||||
opts.dst_index = &the_index;
|
||||
|
||||
opts.fn = oneway_merge;
|
||||
tree = parse_tree_indirect(head_sha1);
|
||||
@@ -205,7 +207,8 @@ static void create_base_index(void)
|
||||
die("failed to unpack HEAD tree object");
|
||||
parse_tree(tree);
|
||||
init_tree_desc(&t, tree->buffer, tree->size);
|
||||
unpack_trees(1, &t, &opts);
|
||||
if (unpack_trees(1, &t, &opts))
|
||||
exit(128); /* We've already reported the error, finish dying */
|
||||
}
|
||||
|
||||
static char *prepare_index(int argc, const char **argv, const char *prefix)
|
||||
|
||||
@@ -17,12 +17,16 @@ static const char * const describe_usage[] = {
|
||||
static int debug; /* Display lots of verbose info */
|
||||
static int all; /* Default to annotated tags only */
|
||||
static int tags; /* But allow any tags if --tags is specified */
|
||||
static int longformat;
|
||||
static int abbrev = DEFAULT_ABBREV;
|
||||
static int max_candidates = 10;
|
||||
const char *pattern = NULL;
|
||||
static int always;
|
||||
|
||||
struct commit_name {
|
||||
struct tag *tag;
|
||||
int prio; /* annotated tag = 2, tag = 1, head = 0 */
|
||||
unsigned char sha1[20];
|
||||
char path[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
static const char *prio_names[] = {
|
||||
@@ -31,14 +35,17 @@ static const char *prio_names[] = {
|
||||
|
||||
static void add_to_known_names(const char *path,
|
||||
struct commit *commit,
|
||||
int prio)
|
||||
int prio,
|
||||
const unsigned char *sha1)
|
||||
{
|
||||
struct commit_name *e = commit->util;
|
||||
if (!e || e->prio < prio) {
|
||||
size_t len = strlen(path)+1;
|
||||
free(e);
|
||||
e = xmalloc(sizeof(struct commit_name) + len);
|
||||
e->tag = NULL;
|
||||
e->prio = prio;
|
||||
hashcpy(e->sha1, sha1);
|
||||
memcpy(e->path, path, len);
|
||||
commit->util = e;
|
||||
}
|
||||
@@ -46,19 +53,34 @@ static void add_to_known_names(const char *path,
|
||||
|
||||
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
|
||||
int might_be_tag = !prefixcmp(path, "refs/tags/");
|
||||
struct commit *commit;
|
||||
struct object *object;
|
||||
int prio;
|
||||
unsigned char peeled[20];
|
||||
int is_tag, prio;
|
||||
|
||||
if (!commit)
|
||||
if (!all && !might_be_tag)
|
||||
return 0;
|
||||
object = parse_object(sha1);
|
||||
|
||||
if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
|
||||
commit = lookup_commit_reference_gently(peeled, 1);
|
||||
if (!commit)
|
||||
return 0;
|
||||
is_tag = !!hashcmp(sha1, commit->object.sha1);
|
||||
} else {
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
object = parse_object(sha1);
|
||||
if (!commit || !object)
|
||||
return 0;
|
||||
is_tag = object->type == OBJ_TAG;
|
||||
}
|
||||
|
||||
/* If --all, then any refs are used.
|
||||
* If --tags, then any tags are used.
|
||||
* Otherwise only annotated tags are used.
|
||||
*/
|
||||
if (!prefixcmp(path, "refs/tags/")) {
|
||||
if (object->type == OBJ_TAG) {
|
||||
if (might_be_tag) {
|
||||
if (is_tag) {
|
||||
prio = 2;
|
||||
if (pattern && fnmatch(pattern, path + 10, 0))
|
||||
prio = 0;
|
||||
@@ -74,7 +96,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void
|
||||
if (!tags && prio < 2)
|
||||
return 0;
|
||||
}
|
||||
add_to_known_names(all ? path + 5 : path + 10, commit, prio);
|
||||
add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -131,6 +153,27 @@ static unsigned long finish_depth_computation(
|
||||
return seen_commits;
|
||||
}
|
||||
|
||||
static void display_name(struct commit_name *n)
|
||||
{
|
||||
if (n->prio == 2 && !n->tag) {
|
||||
n->tag = lookup_tag(n->sha1);
|
||||
if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
|
||||
die("annotated tag %s not available", n->path);
|
||||
if (strcmp(n->tag->tag, n->path))
|
||||
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
|
||||
}
|
||||
|
||||
if (n->tag)
|
||||
printf("%s", n->tag->tag);
|
||||
else
|
||||
printf("%s", n->path);
|
||||
}
|
||||
|
||||
static void show_suffix(int depth, const unsigned char *sha1)
|
||||
{
|
||||
printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
|
||||
}
|
||||
|
||||
static void describe(const char *arg, int last_one)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
@@ -155,10 +198,18 @@ static void describe(const char *arg, int last_one)
|
||||
|
||||
n = cmit->util;
|
||||
if (n) {
|
||||
printf("%s\n", n->path);
|
||||
/*
|
||||
* Exact match to an existing ref.
|
||||
*/
|
||||
display_name(n);
|
||||
if (longformat)
|
||||
show_suffix(0, n->tag->tagged->sha1);
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!max_candidates)
|
||||
die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
|
||||
if (debug)
|
||||
fprintf(stderr, "searching to describe %s\n", arg);
|
||||
|
||||
@@ -207,8 +258,14 @@ static void describe(const char *arg, int last_one)
|
||||
}
|
||||
}
|
||||
|
||||
if (!match_cnt)
|
||||
die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
|
||||
if (!match_cnt) {
|
||||
const unsigned char *sha1 = cmit->object.sha1;
|
||||
if (always) {
|
||||
printf("%s\n", find_unique_abbrev(sha1, abbrev));
|
||||
return;
|
||||
}
|
||||
die("cannot describe '%s'", sha1_to_hex(sha1));
|
||||
}
|
||||
|
||||
qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
|
||||
|
||||
@@ -235,12 +292,11 @@ static void describe(const char *arg, int last_one)
|
||||
sha1_to_hex(gave_up_on->object.sha1));
|
||||
}
|
||||
}
|
||||
if (abbrev == 0)
|
||||
printf("%s\n", all_matches[0].name->path );
|
||||
else
|
||||
printf("%s-%d-g%s\n", all_matches[0].name->path,
|
||||
all_matches[0].depth,
|
||||
find_unique_abbrev(cmit->object.sha1, abbrev));
|
||||
|
||||
display_name(all_matches[0].name);
|
||||
if (abbrev)
|
||||
show_suffix(all_matches[0].depth, cmit->object.sha1);
|
||||
printf("\n");
|
||||
|
||||
if (!last_one)
|
||||
clear_commit_marks(cmit, -1);
|
||||
@@ -254,28 +310,38 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOLEAN(0, "debug", &debug, "debug search strategy on stderr"),
|
||||
OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"),
|
||||
OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"),
|
||||
OPT_BOOLEAN(0, "long", &longformat, "always use long format"),
|
||||
OPT__ABBREV(&abbrev),
|
||||
OPT_SET_INT(0, "exact-match", &max_candidates,
|
||||
"only output exact matches", 0),
|
||||
OPT_INTEGER(0, "candidates", &max_candidates,
|
||||
"consider <n> most recent tags (default: 10)"),
|
||||
OPT_STRING(0, "match", &pattern, "pattern",
|
||||
"only consider tags matching <pattern>"),
|
||||
OPT_BOOLEAN(0, "always", &always,
|
||||
"show abbreviated commit object as fallback"),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, describe_usage, 0);
|
||||
if (max_candidates < 1)
|
||||
max_candidates = 1;
|
||||
if (max_candidates < 0)
|
||||
max_candidates = 0;
|
||||
else if (max_candidates > MAX_TAGS)
|
||||
max_candidates = MAX_TAGS;
|
||||
|
||||
save_commit_buffer = 0;
|
||||
|
||||
if (longformat && abbrev == 0)
|
||||
die("--long is incompatible with --abbrev=0");
|
||||
|
||||
if (contains) {
|
||||
const char **args = xmalloc((6 + argc) * sizeof(char*));
|
||||
const char **args = xmalloc((7 + argc) * sizeof(char*));
|
||||
int i = 0;
|
||||
args[i++] = "name-rev";
|
||||
args[i++] = "--name-only";
|
||||
args[i++] = "--no-undefined";
|
||||
if (always)
|
||||
args[i++] = "--always";
|
||||
if (!all) {
|
||||
args[i++] = "--tags";
|
||||
if (pattern) {
|
||||
|
||||
@@ -44,12 +44,17 @@ static void stuff_change(struct diff_options *opt,
|
||||
tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
|
||||
tmp_c = old_name; old_name = new_name; new_name = tmp_c;
|
||||
}
|
||||
|
||||
if (opt->prefix &&
|
||||
(strncmp(old_name, opt->prefix, opt->prefix_length) ||
|
||||
strncmp(new_name, opt->prefix, opt->prefix_length)))
|
||||
return;
|
||||
|
||||
one = alloc_filespec(old_name);
|
||||
two = alloc_filespec(new_name);
|
||||
fill_filespec(one, old_sha1, old_mode);
|
||||
fill_filespec(two, new_sha1, new_mode);
|
||||
|
||||
/* NEEDSWORK: shouldn't this part of diffopt??? */
|
||||
diff_queue(&diff_queued_diff, one, two);
|
||||
}
|
||||
|
||||
@@ -246,6 +251,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
if (diff_setup_done(&rev.diffopt) < 0)
|
||||
die("diff_setup_done failed");
|
||||
}
|
||||
if (rev.diffopt.prefix && nongit) {
|
||||
rev.diffopt.prefix = NULL;
|
||||
rev.diffopt.prefix_length = 0;
|
||||
}
|
||||
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
|
||||
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ static void show_filemodify(struct diff_queue_struct *q,
|
||||
printf("D %s\n", spec->path);
|
||||
else {
|
||||
struct object *object = lookup_object(spec->sha1);
|
||||
printf("M 0%06o :%d %s\n", spec->mode,
|
||||
printf("M %06o :%d %s\n", spec->mode,
|
||||
get_object_mark(object), spec->path);
|
||||
}
|
||||
}
|
||||
@@ -196,8 +196,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
|
||||
? strlen(reencoded) : message
|
||||
? strlen(message) : 0),
|
||||
reencoded ? reencoded : message ? message : "");
|
||||
if (reencoded)
|
||||
free(reencoded);
|
||||
free(reencoded);
|
||||
|
||||
for (i = 0, p = commit->parents; p; p = p->next) {
|
||||
int mark = get_object_mark(&p->item->object);
|
||||
|
||||
@@ -18,7 +18,7 @@ static struct fetch_pack_args args = {
|
||||
};
|
||||
|
||||
static const char fetch_pack_usage[] =
|
||||
"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
|
||||
"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
|
||||
|
||||
#define COMPLETE (1U << 0)
|
||||
#define COMMON (1U << 1)
|
||||
@@ -41,7 +41,8 @@ static void rev_list_push(struct commit *commit, int mark)
|
||||
commit->object.flags |= mark;
|
||||
|
||||
if (!(commit->object.parsed))
|
||||
parse_commit(commit);
|
||||
if (parse_commit(commit))
|
||||
return;
|
||||
|
||||
insert_by_date(commit, &rev_list);
|
||||
|
||||
@@ -83,7 +84,8 @@ static void mark_common(struct commit *commit,
|
||||
if (!ancestors_only && !(o->flags & POPPED))
|
||||
non_common_revs--;
|
||||
if (!o->parsed && !dont_parse)
|
||||
parse_commit(commit);
|
||||
if (parse_commit(commit))
|
||||
return;
|
||||
|
||||
for (parents = commit->parents;
|
||||
parents;
|
||||
@@ -103,20 +105,20 @@ static const unsigned char* get_rev(void)
|
||||
|
||||
while (commit == NULL) {
|
||||
unsigned int mark;
|
||||
struct commit_list* parents;
|
||||
struct commit_list *parents = NULL;
|
||||
|
||||
if (rev_list == NULL || non_common_revs == 0)
|
||||
return NULL;
|
||||
|
||||
commit = rev_list->item;
|
||||
if (!(commit->object.parsed))
|
||||
parse_commit(commit);
|
||||
if (!parse_commit(commit))
|
||||
parents = commit->parents;
|
||||
|
||||
commit->object.flags |= POPPED;
|
||||
if (!(commit->object.flags & COMMON))
|
||||
non_common_revs--;
|
||||
|
||||
parents = commit->parents;
|
||||
|
||||
if (commit->object.flags & COMMON) {
|
||||
/* do not send "have", and ignore ancestors */
|
||||
commit = NULL;
|
||||
@@ -174,13 +176,14 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
}
|
||||
|
||||
if (!fetching)
|
||||
packet_write(fd[1], "want %s%s%s%s%s%s%s\n",
|
||||
packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
|
||||
sha1_to_hex(remote),
|
||||
(multi_ack ? " multi_ack" : ""),
|
||||
(use_sideband == 2 ? " side-band-64k" : ""),
|
||||
(use_sideband == 1 ? " side-band" : ""),
|
||||
(args.use_thin_pack ? " thin-pack" : ""),
|
||||
(args.no_progress ? " no-progress" : ""),
|
||||
(args.include_tag ? " include-tag" : ""),
|
||||
" ofs-delta");
|
||||
else
|
||||
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
|
||||
@@ -212,7 +215,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
|
||||
if (!lookup_object(sha1))
|
||||
die("object not found: %s", line);
|
||||
/* make sure that it is parsed as shallow */
|
||||
parse_object(sha1);
|
||||
if (!parse_object(sha1))
|
||||
die("error in object: %s", line);
|
||||
if (unregister_shallow(sha1))
|
||||
die("no shallow found: %s", line);
|
||||
continue;
|
||||
@@ -387,7 +391,6 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
|
||||
int retval;
|
||||
unsigned long cutoff = 0;
|
||||
|
||||
track_object_refs = 0;
|
||||
save_commit_buffer = 0;
|
||||
|
||||
for (ref = *refs; ref; ref = ref->next) {
|
||||
@@ -539,8 +542,10 @@ static int get_pack(int xd[2], char **pack_lockfile)
|
||||
cmd.git_cmd = 1;
|
||||
if (start_command(&cmd))
|
||||
die("fetch-pack: unable to fork off %s", argv[0]);
|
||||
if (do_keep && pack_lockfile)
|
||||
if (do_keep && pack_lockfile) {
|
||||
*pack_lockfile = index_pack_lockfile(cmd.out);
|
||||
close(cmd.out);
|
||||
}
|
||||
|
||||
if (finish_command(&cmd))
|
||||
die("%s failed", argv[0]);
|
||||
@@ -680,6 +685,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
||||
args.use_thin_pack = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--include-tag", arg)) {
|
||||
args.include_tag = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--all", arg)) {
|
||||
args.fetch_all = 1;
|
||||
continue;
|
||||
|
||||
@@ -40,6 +40,8 @@ static struct option builtin_fetch_options[] = {
|
||||
"force overwrite of local branch"),
|
||||
OPT_SET_INT('t', "tags", &tags,
|
||||
"fetch all tags and associated objects", TAGS_SET),
|
||||
OPT_SET_INT('n', NULL, &tags,
|
||||
"do not fetch all tags (--no-tags)", TAGS_UNSET),
|
||||
OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
|
||||
OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
|
||||
"allow updating of HEAD ref"),
|
||||
@@ -101,6 +103,10 @@ static void add_merge_config(struct ref **head,
|
||||
}
|
||||
}
|
||||
|
||||
static void find_non_local_tags(struct transport *transport,
|
||||
struct ref **head,
|
||||
struct ref ***tail);
|
||||
|
||||
static struct ref *get_ref_map(struct transport *transport,
|
||||
struct refspec *refs, int ref_count, int tags,
|
||||
int *autotags)
|
||||
@@ -157,8 +163,11 @@ static struct ref *get_ref_map(struct transport *transport,
|
||||
if (!ref_map)
|
||||
die("Couldn't find remote ref HEAD");
|
||||
ref_map->merge = 1;
|
||||
tail = &ref_map->next;
|
||||
}
|
||||
}
|
||||
if (tags == TAGS_DEFAULT && *autotags)
|
||||
find_non_local_tags(transport, &ref_map, &tail);
|
||||
ref_remove_duplicates(ref_map);
|
||||
|
||||
return ref_map;
|
||||
@@ -452,18 +461,28 @@ static int add_existing(const char *refname, const unsigned char *sha1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ref *find_non_local_tags(struct transport *transport,
|
||||
struct ref *fetch_map)
|
||||
static int will_fetch(struct ref **head, const unsigned char *sha1)
|
||||
{
|
||||
static struct path_list existing_refs = { NULL, 0, 0, 0 };
|
||||
struct ref *rm = *head;
|
||||
while (rm) {
|
||||
if (!hashcmp(rm->old_sha1, sha1))
|
||||
return 1;
|
||||
rm = rm->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void find_non_local_tags(struct transport *transport,
|
||||
struct ref **head,
|
||||
struct ref ***tail)
|
||||
{
|
||||
struct path_list existing_refs = { NULL, 0, 0, 0 };
|
||||
struct path_list new_refs = { NULL, 0, 0, 1 };
|
||||
char *ref_name;
|
||||
int ref_name_len;
|
||||
const unsigned char *ref_sha1;
|
||||
const struct ref *tag_ref;
|
||||
struct ref *rm = NULL;
|
||||
struct ref *ref_map = NULL;
|
||||
struct ref **tail = &ref_map;
|
||||
const struct ref *ref;
|
||||
|
||||
for_each_ref(add_existing, &existing_refs);
|
||||
@@ -489,7 +508,8 @@ static struct ref *find_non_local_tags(struct transport *transport,
|
||||
|
||||
if (!path_list_has_path(&existing_refs, ref_name) &&
|
||||
!path_list_has_path(&new_refs, ref_name) &&
|
||||
has_sha1_file(ref->old_sha1)) {
|
||||
(has_sha1_file(ref->old_sha1) ||
|
||||
will_fetch(head, ref->old_sha1))) {
|
||||
path_list_insert(ref_name, &new_refs);
|
||||
|
||||
rm = alloc_ref(strlen(ref_name) + 1);
|
||||
@@ -498,19 +518,19 @@ static struct ref *find_non_local_tags(struct transport *transport,
|
||||
strcpy(rm->peer_ref->name, ref_name);
|
||||
hashcpy(rm->old_sha1, ref_sha1);
|
||||
|
||||
*tail = rm;
|
||||
tail = &rm->next;
|
||||
**tail = rm;
|
||||
*tail = &rm->next;
|
||||
}
|
||||
free(ref_name);
|
||||
}
|
||||
|
||||
return ref_map;
|
||||
path_list_clear(&existing_refs, 0);
|
||||
path_list_clear(&new_refs, 0);
|
||||
}
|
||||
|
||||
static int do_fetch(struct transport *transport,
|
||||
struct refspec *refs, int ref_count)
|
||||
{
|
||||
struct ref *ref_map, *fetch_map;
|
||||
struct ref *ref_map;
|
||||
struct ref *rm;
|
||||
int autotags = (transport->remote->fetch_tags == 1);
|
||||
if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
|
||||
@@ -537,26 +557,28 @@ static int do_fetch(struct transport *transport,
|
||||
read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
|
||||
}
|
||||
|
||||
if (tags == TAGS_DEFAULT && autotags)
|
||||
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
|
||||
if (fetch_refs(transport, ref_map)) {
|
||||
free_refs(ref_map);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fetch_map = ref_map;
|
||||
free_refs(ref_map);
|
||||
|
||||
/* if neither --no-tags nor --tags was specified, do automated tag
|
||||
* following ... */
|
||||
if (tags == TAGS_DEFAULT && autotags) {
|
||||
ref_map = find_non_local_tags(transport, fetch_map);
|
||||
struct ref **tail = &ref_map;
|
||||
ref_map = NULL;
|
||||
find_non_local_tags(transport, &ref_map, &tail);
|
||||
if (ref_map) {
|
||||
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
|
||||
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
|
||||
fetch_refs(transport, ref_map);
|
||||
}
|
||||
free_refs(ref_map);
|
||||
}
|
||||
|
||||
free_refs(fetch_map);
|
||||
|
||||
transport_disconnect(transport);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -165,7 +165,7 @@ static int verify_format(const char *format)
|
||||
for (cp = format; *cp && (sp = find_next(cp)); ) {
|
||||
const char *ep = strchr(sp, ')');
|
||||
if (!ep)
|
||||
return error("malformatted format string %s", sp);
|
||||
return error("malformed format string %s", sp);
|
||||
/* sp points at "%(" and ep points at the closing ")" */
|
||||
parse_atom(sp + 2, ep);
|
||||
cp = ep + 1;
|
||||
|
||||
349
builtin-fsck.c
349
builtin-fsck.c
@@ -8,6 +8,7 @@
|
||||
#include "pack.h"
|
||||
#include "cache-tree.h"
|
||||
#include "tree-walk.h"
|
||||
#include "fsck.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
#define REACHABLE 0x0001
|
||||
@@ -54,13 +55,75 @@ static int objerror(struct object *obj, const char *err, ...)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int objwarning(struct object *obj, const char *err, ...)
|
||||
static int fsck_error_func(struct object *obj, int type, const char *err, ...)
|
||||
{
|
||||
va_list params;
|
||||
va_start(params, err);
|
||||
objreport(obj, "warning", err, params);
|
||||
objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
|
||||
va_end(params);
|
||||
return -1;
|
||||
return (type == FSCK_WARN) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int mark_object(struct object *obj, int type, void *data)
|
||||
{
|
||||
struct tree *tree = NULL;
|
||||
struct object *parent = data;
|
||||
int result;
|
||||
|
||||
if (!obj) {
|
||||
printf("broken link from %7s %s\n",
|
||||
typename(parent->type), sha1_to_hex(parent->sha1));
|
||||
printf("broken link from %7s %s\n",
|
||||
(type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
|
||||
errors_found |= ERROR_REACHABLE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (type != OBJ_ANY && obj->type != type)
|
||||
objerror(parent, "wrong object type in link");
|
||||
|
||||
if (obj->flags & REACHABLE)
|
||||
return 0;
|
||||
obj->flags |= REACHABLE;
|
||||
if (!obj->parsed) {
|
||||
if (parent && !has_sha1_file(obj->sha1)) {
|
||||
printf("broken link from %7s %s\n",
|
||||
typename(parent->type), sha1_to_hex(parent->sha1));
|
||||
printf(" to %7s %s\n",
|
||||
typename(obj->type), sha1_to_hex(obj->sha1));
|
||||
errors_found |= ERROR_REACHABLE;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (obj->type == OBJ_TREE) {
|
||||
obj->parsed = 0;
|
||||
tree = (struct tree *)obj;
|
||||
if (parse_tree(tree) < 0)
|
||||
return 1; /* error already displayed */
|
||||
}
|
||||
result = fsck_walk(obj, mark_object, obj);
|
||||
if (tree) {
|
||||
free(tree->buffer);
|
||||
tree->buffer = NULL;
|
||||
}
|
||||
if (result < 0)
|
||||
result = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void mark_object_reachable(struct object *obj)
|
||||
{
|
||||
mark_object(obj, OBJ_ANY, 0);
|
||||
}
|
||||
|
||||
static int mark_used(struct object *obj, int type, void *data)
|
||||
{
|
||||
if (!obj)
|
||||
return 1;
|
||||
obj->used = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -68,8 +131,6 @@ static int objwarning(struct object *obj, const char *err, ...)
|
||||
*/
|
||||
static void check_reachable_object(struct object *obj)
|
||||
{
|
||||
const struct object_refs *refs;
|
||||
|
||||
/*
|
||||
* We obviously want the object to be parsed,
|
||||
* except if it was in a pack-file and we didn't
|
||||
@@ -82,25 +143,6 @@ static void check_reachable_object(struct object *obj)
|
||||
errors_found |= ERROR_REACHABLE;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that everything that we try to reference is also good.
|
||||
*/
|
||||
refs = lookup_object_refs(obj);
|
||||
if (refs) {
|
||||
unsigned j;
|
||||
for (j = 0; j < refs->count; j++) {
|
||||
struct object *ref = refs->ref[j];
|
||||
if (ref->parsed ||
|
||||
(has_sha1_file(ref->sha1)))
|
||||
continue;
|
||||
printf("broken link from %7s %s\n",
|
||||
typename(obj->type), sha1_to_hex(obj->sha1));
|
||||
printf(" to %7s %s\n",
|
||||
typename(ref->type), sha1_to_hex(ref->sha1));
|
||||
errors_found |= ERROR_REACHABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -204,205 +246,6 @@ static void check_connectivity(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The entries in a tree are ordered in the _path_ order,
|
||||
* which means that a directory entry is ordered by adding
|
||||
* a slash to the end of it.
|
||||
*
|
||||
* So a directory called "a" is ordered _after_ a file
|
||||
* called "a.c", because "a/" sorts after "a.c".
|
||||
*/
|
||||
#define TREE_UNORDERED (-1)
|
||||
#define TREE_HAS_DUPS (-2)
|
||||
|
||||
static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
|
||||
{
|
||||
int len1 = strlen(name1);
|
||||
int len2 = strlen(name2);
|
||||
int len = len1 < len2 ? len1 : len2;
|
||||
unsigned char c1, c2;
|
||||
int cmp;
|
||||
|
||||
cmp = memcmp(name1, name2, len);
|
||||
if (cmp < 0)
|
||||
return 0;
|
||||
if (cmp > 0)
|
||||
return TREE_UNORDERED;
|
||||
|
||||
/*
|
||||
* Ok, the first <len> characters are the same.
|
||||
* Now we need to order the next one, but turn
|
||||
* a '\0' into a '/' for a directory entry.
|
||||
*/
|
||||
c1 = name1[len];
|
||||
c2 = name2[len];
|
||||
if (!c1 && !c2)
|
||||
/*
|
||||
* git-write-tree used to write out a nonsense tree that has
|
||||
* entries with the same name, one blob and one tree. Make
|
||||
* sure we do not have duplicate entries.
|
||||
*/
|
||||
return TREE_HAS_DUPS;
|
||||
if (!c1 && S_ISDIR(mode1))
|
||||
c1 = '/';
|
||||
if (!c2 && S_ISDIR(mode2))
|
||||
c2 = '/';
|
||||
return c1 < c2 ? 0 : TREE_UNORDERED;
|
||||
}
|
||||
|
||||
static int fsck_tree(struct tree *item)
|
||||
{
|
||||
int retval;
|
||||
int has_full_path = 0;
|
||||
int has_empty_name = 0;
|
||||
int has_zero_pad = 0;
|
||||
int has_bad_modes = 0;
|
||||
int has_dup_entries = 0;
|
||||
int not_properly_sorted = 0;
|
||||
struct tree_desc desc;
|
||||
unsigned o_mode;
|
||||
const char *o_name;
|
||||
const unsigned char *o_sha1;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking tree %s\n",
|
||||
sha1_to_hex(item->object.sha1));
|
||||
|
||||
init_tree_desc(&desc, item->buffer, item->size);
|
||||
|
||||
o_mode = 0;
|
||||
o_name = NULL;
|
||||
o_sha1 = NULL;
|
||||
while (desc.size) {
|
||||
unsigned mode;
|
||||
const char *name;
|
||||
const unsigned char *sha1;
|
||||
|
||||
sha1 = tree_entry_extract(&desc, &name, &mode);
|
||||
|
||||
if (strchr(name, '/'))
|
||||
has_full_path = 1;
|
||||
if (!*name)
|
||||
has_empty_name = 1;
|
||||
has_zero_pad |= *(char *)desc.buffer == '0';
|
||||
update_tree_entry(&desc);
|
||||
|
||||
switch (mode) {
|
||||
/*
|
||||
* Standard modes..
|
||||
*/
|
||||
case S_IFREG | 0755:
|
||||
case S_IFREG | 0644:
|
||||
case S_IFLNK:
|
||||
case S_IFDIR:
|
||||
case S_IFGITLINK:
|
||||
break;
|
||||
/*
|
||||
* This is nonstandard, but we had a few of these
|
||||
* early on when we honored the full set of mode
|
||||
* bits..
|
||||
*/
|
||||
case S_IFREG | 0664:
|
||||
if (!check_strict)
|
||||
break;
|
||||
default:
|
||||
has_bad_modes = 1;
|
||||
}
|
||||
|
||||
if (o_name) {
|
||||
switch (verify_ordered(o_mode, o_name, mode, name)) {
|
||||
case TREE_UNORDERED:
|
||||
not_properly_sorted = 1;
|
||||
break;
|
||||
case TREE_HAS_DUPS:
|
||||
has_dup_entries = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
o_mode = mode;
|
||||
o_name = name;
|
||||
o_sha1 = sha1;
|
||||
}
|
||||
free(item->buffer);
|
||||
item->buffer = NULL;
|
||||
|
||||
retval = 0;
|
||||
if (has_full_path) {
|
||||
objwarning(&item->object, "contains full pathnames");
|
||||
}
|
||||
if (has_empty_name) {
|
||||
objwarning(&item->object, "contains empty pathname");
|
||||
}
|
||||
if (has_zero_pad) {
|
||||
objwarning(&item->object, "contains zero-padded file modes");
|
||||
}
|
||||
if (has_bad_modes) {
|
||||
objwarning(&item->object, "contains bad file modes");
|
||||
}
|
||||
if (has_dup_entries) {
|
||||
retval = objerror(&item->object, "contains duplicate file entries");
|
||||
}
|
||||
if (not_properly_sorted) {
|
||||
retval = objerror(&item->object, "not properly sorted");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int fsck_commit(struct commit *commit)
|
||||
{
|
||||
char *buffer = commit->buffer;
|
||||
unsigned char tree_sha1[20], sha1[20];
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking commit %s\n",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
|
||||
if (!commit->date)
|
||||
return objerror(&commit->object, "invalid author/committer line");
|
||||
|
||||
if (memcmp(buffer, "tree ", 5))
|
||||
return objerror(&commit->object, "invalid format - expected 'tree' line");
|
||||
if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
|
||||
return objerror(&commit->object, "invalid 'tree' line format - bad sha1");
|
||||
buffer += 46;
|
||||
while (!memcmp(buffer, "parent ", 7)) {
|
||||
if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n')
|
||||
return objerror(&commit->object, "invalid 'parent' line format - bad sha1");
|
||||
buffer += 48;
|
||||
}
|
||||
if (memcmp(buffer, "author ", 7))
|
||||
return objerror(&commit->object, "invalid format - expected 'author' line");
|
||||
free(commit->buffer);
|
||||
commit->buffer = NULL;
|
||||
if (!commit->tree)
|
||||
return objerror(&commit->object, "could not load commit's tree %s", tree_sha1);
|
||||
if (!commit->parents && show_root)
|
||||
printf("root %s\n", sha1_to_hex(commit->object.sha1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsck_tag(struct tag *tag)
|
||||
{
|
||||
struct object *tagged = tag->tagged;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking tag %s\n",
|
||||
sha1_to_hex(tag->object.sha1));
|
||||
|
||||
if (!tagged) {
|
||||
return objerror(&tag->object, "could not load tagged object");
|
||||
}
|
||||
if (!show_tags)
|
||||
return 0;
|
||||
|
||||
printf("tagged %s %s", typename(tagged->type), sha1_to_hex(tagged->sha1));
|
||||
printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsck_sha1(const unsigned char *sha1)
|
||||
{
|
||||
struct object *obj = parse_object(sha1);
|
||||
@@ -414,18 +257,43 @@ static int fsck_sha1(const unsigned char *sha1)
|
||||
if (obj->flags & SEEN)
|
||||
return 0;
|
||||
obj->flags |= SEEN;
|
||||
if (obj->type == OBJ_BLOB)
|
||||
return 0;
|
||||
if (obj->type == OBJ_TREE)
|
||||
return fsck_tree((struct tree *) obj);
|
||||
if (obj->type == OBJ_COMMIT)
|
||||
return fsck_commit((struct commit *) obj);
|
||||
if (obj->type == OBJ_TAG)
|
||||
return fsck_tag((struct tag *) obj);
|
||||
|
||||
/* By now, parse_object() would've returned NULL instead. */
|
||||
return objerror(obj, "unknown type '%d' (internal fsck error)",
|
||||
obj->type);
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking %s %s\n",
|
||||
typename(obj->type), sha1_to_hex(obj->sha1));
|
||||
|
||||
if (fsck_walk(obj, mark_used, 0))
|
||||
objerror(obj, "broken links");
|
||||
if (fsck_object(obj, check_strict, fsck_error_func))
|
||||
return -1;
|
||||
|
||||
if (obj->type == OBJ_TREE) {
|
||||
struct tree *item = (struct tree *) obj;
|
||||
|
||||
free(item->buffer);
|
||||
item->buffer = NULL;
|
||||
}
|
||||
|
||||
if (obj->type == OBJ_COMMIT) {
|
||||
struct commit *commit = (struct commit *) obj;
|
||||
|
||||
free(commit->buffer);
|
||||
commit->buffer = NULL;
|
||||
|
||||
if (!commit->parents && show_root)
|
||||
printf("root %s\n", sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
|
||||
if (obj->type == OBJ_TAG) {
|
||||
struct tag *tag = (struct tag *) obj;
|
||||
|
||||
if (show_tags && tag->tagged) {
|
||||
printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
|
||||
printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -538,13 +406,13 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
obj = lookup_object(osha1);
|
||||
if (obj) {
|
||||
obj->used = 1;
|
||||
mark_reachable(obj, REACHABLE);
|
||||
mark_object_reachable(obj);
|
||||
}
|
||||
}
|
||||
obj = lookup_object(nsha1);
|
||||
if (obj) {
|
||||
obj->used = 1;
|
||||
mark_reachable(obj, REACHABLE);
|
||||
mark_object_reachable(obj);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -574,7 +442,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
|
||||
error("%s: not a commit", refname);
|
||||
default_refs++;
|
||||
obj->used = 1;
|
||||
mark_reachable(obj, REACHABLE);
|
||||
mark_object_reachable(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -660,7 +528,7 @@ static int fsck_cache_tree(struct cache_tree *it)
|
||||
sha1_to_hex(it->sha1));
|
||||
return 1;
|
||||
}
|
||||
mark_reachable(obj, REACHABLE);
|
||||
mark_object_reachable(obj);
|
||||
obj->used = 1;
|
||||
if (obj->type != OBJ_TREE)
|
||||
err |= objerror(obj, "non-tree in cache-tree");
|
||||
@@ -693,7 +561,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, heads;
|
||||
|
||||
track_object_refs = 1;
|
||||
errors_found = 0;
|
||||
|
||||
argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0);
|
||||
@@ -741,7 +608,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
|
||||
obj->used = 1;
|
||||
mark_reachable(obj, REACHABLE);
|
||||
mark_object_reachable(obj);
|
||||
heads++;
|
||||
continue;
|
||||
}
|
||||
@@ -773,7 +640,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
obj = &blob->object;
|
||||
obj->used = 1;
|
||||
mark_reachable(obj, REACHABLE);
|
||||
mark_object_reachable(obj);
|
||||
}
|
||||
if (active_cache_tree)
|
||||
fsck_cache_tree(active_cache_tree);
|
||||
|
||||
21
builtin-gc.c
21
builtin-gc.c
@@ -26,12 +26,13 @@ static int pack_refs = 1;
|
||||
static int aggressive_window = -1;
|
||||
static int gc_auto_threshold = 6700;
|
||||
static int gc_auto_pack_limit = 20;
|
||||
static char *prune_expire = "2.weeks.ago";
|
||||
|
||||
#define MAX_ADD 10
|
||||
static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
|
||||
static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
|
||||
static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
|
||||
static const char *argv_prune[] = {"prune", NULL};
|
||||
static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
|
||||
static const char *argv_rerere[] = {"rerere", "gc", NULL};
|
||||
|
||||
static int gc_config(const char *var, const char *value)
|
||||
@@ -55,6 +56,17 @@ static int gc_config(const char *var, const char *value)
|
||||
gc_auto_pack_limit = git_config_int(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "gc.pruneexpire")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
if (strcmp(value, "now")) {
|
||||
unsigned long now = approxidate("now");
|
||||
if (approxidate(value) >= now)
|
||||
return error("Invalid %s: '%s'", var, value);
|
||||
}
|
||||
prune_expire = xstrdup(value);
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
@@ -172,12 +184,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
int prune = 0;
|
||||
int aggressive = 0;
|
||||
int auto_gc = 0;
|
||||
int quiet = 0;
|
||||
char buf[80];
|
||||
|
||||
struct option builtin_gc_options[] = {
|
||||
OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
|
||||
OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
|
||||
OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
|
||||
OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -197,6 +211,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
append_option(argv_repack, buf, MAX_ADD);
|
||||
}
|
||||
}
|
||||
if (quiet)
|
||||
append_option(argv_repack, "-q", MAX_ADD);
|
||||
|
||||
if (auto_gc) {
|
||||
/*
|
||||
@@ -230,7 +246,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||
if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
|
||||
return error(FAILED_RUN, argv_repack[0]);
|
||||
|
||||
if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
|
||||
argv_prune[2] = prune_expire;
|
||||
if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
|
||||
return error(FAILED_RUN, argv_prune[0]);
|
||||
|
||||
if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
#include "builtin.h"
|
||||
#include "grep.h"
|
||||
|
||||
#ifndef NO_EXTERNAL_GREP
|
||||
#ifdef __unix__
|
||||
#define NO_EXTERNAL_GREP 0
|
||||
#else
|
||||
#define NO_EXTERNAL_GREP 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* git grep pathspecs are somewhat different from diff-tree pathspecs;
|
||||
* pathname wildcards are allowed.
|
||||
@@ -153,7 +161,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef __unix__
|
||||
#if !NO_EXTERNAL_GREP
|
||||
static int exec_grep(int argc, const char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
@@ -372,7 +380,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
||||
int nr;
|
||||
read_cache();
|
||||
|
||||
#ifdef __unix__
|
||||
#if !NO_EXTERNAL_GREP
|
||||
/*
|
||||
* Use the external "grep" command for the case where
|
||||
* we grep through the checked-out files. It tends to
|
||||
|
||||
@@ -59,7 +59,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
|
||||
url = rewritten_url;
|
||||
}
|
||||
|
||||
walker = get_http_walker(url);
|
||||
walker = get_http_walker(url, NULL);
|
||||
walker->get_tree = get_tree;
|
||||
walker->get_history = get_history;
|
||||
walker->get_all = get_all;
|
||||
@@ -80,8 +80,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
|
||||
|
||||
walker_free(walker);
|
||||
|
||||
if (rewritten_url)
|
||||
free(rewritten_url);
|
||||
free(rewritten_url);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -29,27 +29,6 @@ static void safe_create_dir(const char *dir, int share)
|
||||
die("Could not make %s writable by group\n", dir);
|
||||
}
|
||||
|
||||
static int copy_file(const char *dst, const char *src, int mode)
|
||||
{
|
||||
int fdi, fdo, status;
|
||||
|
||||
mode = (mode & 0111) ? 0777 : 0666;
|
||||
if ((fdi = open(src, O_RDONLY)) < 0)
|
||||
return fdi;
|
||||
if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
|
||||
close(fdi);
|
||||
return fdo;
|
||||
}
|
||||
status = copy_fd(fdi, fdo);
|
||||
if (close(fdo) != 0)
|
||||
return error("%s: write error: %s", dst, strerror(errno));
|
||||
|
||||
if (!status && adjust_shared_perm(dst))
|
||||
return -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void copy_templates_1(char *path, int baselen,
|
||||
char *template, int template_baselen,
|
||||
DIR *dir)
|
||||
|
||||
355
builtin-log.c
355
builtin-log.c
@@ -15,9 +15,12 @@
|
||||
#include "reflog-walk.h"
|
||||
#include "patch-ids.h"
|
||||
#include "refs.h"
|
||||
#include "run-command.h"
|
||||
#include "shortlog.h"
|
||||
|
||||
static int default_show_root = 1;
|
||||
static const char *fmt_patch_subject_prefix = "PATCH";
|
||||
static const char *fmt_pretty;
|
||||
|
||||
static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
|
||||
{
|
||||
@@ -52,6 +55,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
|
||||
|
||||
rev->abbrev = DEFAULT_ABBREV;
|
||||
rev->commit_format = CMIT_FMT_DEFAULT;
|
||||
if (fmt_pretty)
|
||||
rev->commit_format = get_commit_format(fmt_pretty);
|
||||
rev->verbose_header = 1;
|
||||
DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
|
||||
rev->show_root_diff = default_show_root;
|
||||
@@ -219,6 +224,8 @@ static int cmd_log_walk(struct rev_info *rev)
|
||||
|
||||
static int git_log_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "format.pretty"))
|
||||
return git_config_string(&fmt_pretty, var, value);
|
||||
if (!strcmp(var, "format.subjectprefix")) {
|
||||
if (!value)
|
||||
config_error_nonbool(var);
|
||||
@@ -428,24 +435,47 @@ static int istitlechar(char c)
|
||||
(c >= '0' && c <= '9') || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
static char *extra_headers = NULL;
|
||||
static int extra_headers_size = 0;
|
||||
static const char *fmt_patch_suffix = ".patch";
|
||||
static int numbered = 0;
|
||||
static int auto_number = 0;
|
||||
|
||||
static char **extra_hdr;
|
||||
static int extra_hdr_nr;
|
||||
static int extra_hdr_alloc;
|
||||
|
||||
static char **extra_to;
|
||||
static int extra_to_nr;
|
||||
static int extra_to_alloc;
|
||||
|
||||
static char **extra_cc;
|
||||
static int extra_cc_nr;
|
||||
static int extra_cc_alloc;
|
||||
|
||||
static void add_header(const char *value)
|
||||
{
|
||||
int len = strlen(value);
|
||||
while (value[len - 1] == '\n')
|
||||
len--;
|
||||
if (!strncasecmp(value, "to: ", 4)) {
|
||||
ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
|
||||
extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
|
||||
return;
|
||||
}
|
||||
if (!strncasecmp(value, "cc: ", 4)) {
|
||||
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
|
||||
extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
|
||||
return;
|
||||
}
|
||||
ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
|
||||
extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
|
||||
}
|
||||
|
||||
static int git_format_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "format.headers")) {
|
||||
int len;
|
||||
|
||||
if (!value)
|
||||
die("format.headers without value");
|
||||
len = strlen(value);
|
||||
extra_headers_size += len + 1;
|
||||
extra_headers = xrealloc(extra_headers, extra_headers_size);
|
||||
extra_headers[extra_headers_size - len - 1] = 0;
|
||||
strcat(extra_headers, value);
|
||||
add_header(value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "format.suffix")) {
|
||||
@@ -470,74 +500,81 @@ static int git_format_config(const char *var, const char *value)
|
||||
}
|
||||
|
||||
|
||||
static FILE *realstdout = NULL;
|
||||
static const char *output_directory = NULL;
|
||||
|
||||
static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
|
||||
int numbered_files)
|
||||
static const char *get_oneline_for_filename(struct commit *commit,
|
||||
int keep_subject)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
static char filename[PATH_MAX];
|
||||
char *sol;
|
||||
int len = 0;
|
||||
int suffix_len = strlen(fmt_patch_suffix) + 1;
|
||||
|
||||
sol = strstr(commit->buffer, "\n\n");
|
||||
if (!sol)
|
||||
filename[0] = '\0';
|
||||
else {
|
||||
int j, space = 0;
|
||||
|
||||
sol += 2;
|
||||
/* strip [PATCH] or [PATCH blabla] */
|
||||
if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
|
||||
char *eos = strchr(sol + 6, ']');
|
||||
if (eos) {
|
||||
while (isspace(*eos))
|
||||
eos++;
|
||||
sol = eos;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0;
|
||||
j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
|
||||
len < sizeof(filename) - suffix_len &&
|
||||
sol[j] && sol[j] != '\n';
|
||||
j++) {
|
||||
if (istitlechar(sol[j])) {
|
||||
if (space) {
|
||||
filename[len++] = '-';
|
||||
space = 0;
|
||||
}
|
||||
filename[len++] = sol[j];
|
||||
if (sol[j] == '.')
|
||||
while (sol[j + 1] == '.')
|
||||
j++;
|
||||
} else
|
||||
space = 1;
|
||||
}
|
||||
while (filename[len - 1] == '.'
|
||||
|| filename[len - 1] == '-')
|
||||
len--;
|
||||
filename[len] = '\0';
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
static FILE *realstdout = NULL;
|
||||
static const char *output_directory = NULL;
|
||||
|
||||
static int reopen_stdout(const char *oneline, int nr, int total)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
int len = 0;
|
||||
int suffix_len = strlen(fmt_patch_suffix) + 1;
|
||||
|
||||
if (output_directory) {
|
||||
if (strlen(output_directory) >=
|
||||
len = snprintf(filename, sizeof(filename), "%s",
|
||||
output_directory);
|
||||
if (len >=
|
||||
sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
|
||||
return error("name of output directory is too long");
|
||||
strlcpy(filename, output_directory, sizeof(filename) - suffix_len);
|
||||
len = strlen(filename);
|
||||
if (filename[len - 1] != '/')
|
||||
filename[len++] = '/';
|
||||
}
|
||||
|
||||
if (numbered_files) {
|
||||
sprintf(filename + len, "%d", nr);
|
||||
len = strlen(filename);
|
||||
|
||||
} else {
|
||||
sprintf(filename + len, "%04d", nr);
|
||||
len = strlen(filename);
|
||||
|
||||
sol = strstr(commit->buffer, "\n\n");
|
||||
if (sol) {
|
||||
int j, space = 1;
|
||||
|
||||
sol += 2;
|
||||
/* strip [PATCH] or [PATCH blabla] */
|
||||
if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
|
||||
char *eos = strchr(sol + 6, ']');
|
||||
if (eos) {
|
||||
while (isspace(*eos))
|
||||
eos++;
|
||||
sol = eos;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0;
|
||||
j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
|
||||
len < sizeof(filename) - suffix_len &&
|
||||
sol[j] && sol[j] != '\n';
|
||||
j++) {
|
||||
if (istitlechar(sol[j])) {
|
||||
if (space) {
|
||||
filename[len++] = '-';
|
||||
space = 0;
|
||||
}
|
||||
filename[len++] = sol[j];
|
||||
if (sol[j] == '.')
|
||||
while (sol[j + 1] == '.')
|
||||
j++;
|
||||
} else
|
||||
space = 1;
|
||||
}
|
||||
while (filename[len - 1] == '.'
|
||||
|| filename[len - 1] == '-')
|
||||
len--;
|
||||
filename[len] = 0;
|
||||
}
|
||||
if (len + suffix_len >= sizeof(filename))
|
||||
return error("Patch pathname too long");
|
||||
if (!oneline)
|
||||
len += sprintf(filename + len, "%d", nr);
|
||||
else {
|
||||
len += sprintf(filename + len, "%04d-", nr);
|
||||
len += snprintf(filename + len, sizeof(filename) - len - 1
|
||||
- suffix_len, "%s", oneline);
|
||||
strcpy(filename + len, fmt_patch_suffix);
|
||||
}
|
||||
|
||||
@@ -594,16 +631,92 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
|
||||
o2->flags = flags2;
|
||||
}
|
||||
|
||||
static void gen_message_id(char *dest, unsigned int length, char *base)
|
||||
static void gen_message_id(struct rev_info *info, char *base)
|
||||
{
|
||||
const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
|
||||
const char *email_start = strrchr(committer, '<');
|
||||
const char *email_end = strrchr(committer, '>');
|
||||
if(!email_start || !email_end || email_start > email_end - 1)
|
||||
struct strbuf buf;
|
||||
if (!email_start || !email_end || email_start > email_end - 1)
|
||||
die("Could not extract email from committer identity.");
|
||||
snprintf(dest, length, "%s.%lu.git.%.*s", base,
|
||||
(unsigned long) time(NULL),
|
||||
(int)(email_end - email_start - 1), email_start + 1);
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
|
||||
(unsigned long) time(NULL),
|
||||
(int)(email_end - email_start - 1), email_start + 1);
|
||||
info->message_id = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static void make_cover_letter(struct rev_info *rev, int use_stdout,
|
||||
int numbered, int numbered_files,
|
||||
struct commit *origin,
|
||||
int nr, struct commit **list, struct commit *head)
|
||||
{
|
||||
const char *committer;
|
||||
char *head_sha1;
|
||||
const char *subject_start = NULL;
|
||||
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
|
||||
const char *msg;
|
||||
const char *extra_headers = rev->extra_headers;
|
||||
struct shortlog log;
|
||||
struct strbuf sb;
|
||||
int i;
|
||||
const char *encoding = "utf-8";
|
||||
struct diff_options opts;
|
||||
int need_8bit_cte = 0;
|
||||
|
||||
if (rev->commit_format != CMIT_FMT_EMAIL)
|
||||
die("Cover letter needs email format");
|
||||
|
||||
if (!use_stdout && reopen_stdout(numbered_files ?
|
||||
NULL : "cover-letter", 0, rev->total))
|
||||
return;
|
||||
|
||||
head_sha1 = sha1_to_hex(head->object.sha1);
|
||||
|
||||
log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
|
||||
&need_8bit_cte);
|
||||
|
||||
committer = git_committer_info(0);
|
||||
|
||||
msg = body;
|
||||
strbuf_init(&sb, 0);
|
||||
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
|
||||
encoding);
|
||||
pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
|
||||
encoding, need_8bit_cte);
|
||||
pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
|
||||
printf("%s\n", sb.buf);
|
||||
|
||||
strbuf_release(&sb);
|
||||
|
||||
shortlog_init(&log);
|
||||
log.wrap_lines = 1;
|
||||
log.wrap = 72;
|
||||
log.in1 = 2;
|
||||
log.in2 = 4;
|
||||
for (i = 0; i < nr; i++)
|
||||
shortlog_add_commit(&log, list[i]);
|
||||
|
||||
shortlog_output(&log);
|
||||
|
||||
/*
|
||||
* We can only do diffstat with a unique reference point
|
||||
*/
|
||||
if (!origin)
|
||||
return;
|
||||
|
||||
memcpy(&opts, &rev->diffopt, sizeof(opts));
|
||||
opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
|
||||
|
||||
diff_setup_done(&opts);
|
||||
|
||||
diff_tree_sha1(origin->tree->object.sha1,
|
||||
head->tree->object.sha1,
|
||||
"", &opts);
|
||||
diffcore_std(&opts);
|
||||
diff_flush(&opts);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const char *clean_message_id(const char *msg_id)
|
||||
@@ -641,11 +754,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
int subject_prefix = 0;
|
||||
int ignore_if_in_upstream = 0;
|
||||
int thread = 0;
|
||||
int cover_letter = 0;
|
||||
int boundary_count = 0;
|
||||
struct commit *origin = NULL, *head = NULL;
|
||||
const char *in_reply_to = NULL;
|
||||
struct patch_ids ids;
|
||||
char *add_signoff = NULL;
|
||||
char message_id[1024];
|
||||
char ref_message_id[1024];
|
||||
struct strbuf buf;
|
||||
|
||||
git_config(git_format_config);
|
||||
init_revisions(&rev, prefix);
|
||||
@@ -658,7 +773,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
||||
|
||||
rev.subject_prefix = fmt_patch_subject_prefix;
|
||||
rev.extra_headers = extra_headers;
|
||||
|
||||
/*
|
||||
* Parse the arguments before setup_revisions(), or something
|
||||
@@ -686,6 +800,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
die("Need a number for --start-number");
|
||||
start_number = strtol(argv[i], NULL, 10);
|
||||
}
|
||||
else if (!prefixcmp(argv[i], "--cc=")) {
|
||||
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
|
||||
extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-k") ||
|
||||
!strcmp(argv[i], "--keep-subject")) {
|
||||
keep_subject = 1;
|
||||
@@ -742,11 +860,44 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
rev.subject_prefix = argv[i] + 17;
|
||||
} else if (!prefixcmp(argv[i], "--suffix="))
|
||||
fmt_patch_suffix = argv[i] + 9;
|
||||
else if (!strcmp(argv[i], "--cover-letter"))
|
||||
cover_letter = 1;
|
||||
else
|
||||
argv[j++] = argv[i];
|
||||
}
|
||||
argc = j;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
|
||||
for (i = 0; i < extra_hdr_nr; i++) {
|
||||
strbuf_addstr(&buf, extra_hdr[i]);
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
if (extra_to_nr)
|
||||
strbuf_addstr(&buf, "To: ");
|
||||
for (i = 0; i < extra_to_nr; i++) {
|
||||
if (i)
|
||||
strbuf_addstr(&buf, " ");
|
||||
strbuf_addstr(&buf, extra_to[i]);
|
||||
if (i + 1 < extra_to_nr)
|
||||
strbuf_addch(&buf, ',');
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
if (extra_cc_nr)
|
||||
strbuf_addstr(&buf, "Cc: ");
|
||||
for (i = 0; i < extra_cc_nr; i++) {
|
||||
if (i)
|
||||
strbuf_addstr(&buf, " ");
|
||||
strbuf_addstr(&buf, extra_cc[i]);
|
||||
if (i + 1 < extra_cc_nr)
|
||||
strbuf_addch(&buf, ',');
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
rev.extra_headers = strbuf_detach(&buf, 0);
|
||||
|
||||
if (start_number < 0)
|
||||
start_number = 1;
|
||||
if (numbered && keep_subject)
|
||||
@@ -793,6 +944,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
* get_revision() to do the usual traversal.
|
||||
*/
|
||||
}
|
||||
if (cover_letter) {
|
||||
/* remember the range */
|
||||
int i;
|
||||
for (i = 0; i < rev.pending.nr; i++) {
|
||||
struct object *o = rev.pending.objects[i].item;
|
||||
if (!(o->flags & UNINTERESTING))
|
||||
head = (struct commit *)o;
|
||||
}
|
||||
/* We can't generate a cover letter without any patches */
|
||||
if (!head)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ignore_if_in_upstream)
|
||||
get_patch_ids(&rev, &ids, prefix);
|
||||
@@ -802,7 +965,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (prepare_revision_walk(&rev))
|
||||
die("revision walk setup failed");
|
||||
rev.boundary = 1;
|
||||
while ((commit = get_revision(&rev)) != NULL) {
|
||||
if (commit->object.flags & BOUNDARY) {
|
||||
boundary_count++;
|
||||
origin = (boundary_count == 1) ? commit : NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore merges */
|
||||
if (commit->parents && commit->parents->next)
|
||||
continue;
|
||||
@@ -820,29 +990,42 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
numbered = 1;
|
||||
if (numbered)
|
||||
rev.total = total + start_number - 1;
|
||||
rev.add_signoff = add_signoff;
|
||||
if (in_reply_to)
|
||||
rev.ref_message_id = clean_message_id(in_reply_to);
|
||||
if (cover_letter) {
|
||||
if (thread)
|
||||
gen_message_id(&rev, "cover");
|
||||
make_cover_letter(&rev, use_stdout, numbered, numbered_files,
|
||||
origin, nr, list, head);
|
||||
total++;
|
||||
start_number--;
|
||||
}
|
||||
rev.add_signoff = add_signoff;
|
||||
while (0 <= --nr) {
|
||||
int shown;
|
||||
commit = list[nr];
|
||||
rev.nr = total - nr + (start_number - 1);
|
||||
/* Make the second and subsequent mails replies to the first */
|
||||
if (thread) {
|
||||
if (nr == (total - 2)) {
|
||||
strncpy(ref_message_id, message_id,
|
||||
sizeof(ref_message_id));
|
||||
ref_message_id[sizeof(ref_message_id)-1]='\0';
|
||||
rev.ref_message_id = ref_message_id;
|
||||
/* Have we already had a message ID? */
|
||||
if (rev.message_id) {
|
||||
/*
|
||||
* If we've got the ID to be a reply
|
||||
* to, discard the current ID;
|
||||
* otherwise, make everything a reply
|
||||
* to that.
|
||||
*/
|
||||
if (rev.ref_message_id)
|
||||
free(rev.message_id);
|
||||
else
|
||||
rev.ref_message_id = rev.message_id;
|
||||
}
|
||||
gen_message_id(message_id, sizeof(message_id),
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
rev.message_id = message_id;
|
||||
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
if (!use_stdout)
|
||||
if (reopen_stdout(commit, rev.nr, keep_subject,
|
||||
numbered_files))
|
||||
die("Failed to create output files");
|
||||
if (!use_stdout && reopen_stdout(numbered_files ? NULL :
|
||||
get_oneline_for_filename(commit, keep_subject),
|
||||
rev.nr, rev.total))
|
||||
die("Failed to create output files");
|
||||
shown = log_tree_commit(&rev, commit);
|
||||
free(commit->buffer);
|
||||
commit->buffer = NULL;
|
||||
|
||||
@@ -574,17 +574,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
||||
pathspec = get_pathspec(prefix, argv + i);
|
||||
|
||||
/* Verify that the pathspec matches the prefix */
|
||||
if (pathspec) {
|
||||
if (argc != i) {
|
||||
int cnt;
|
||||
for (cnt = 0; pathspec[cnt]; cnt++)
|
||||
;
|
||||
if (cnt != (argc - i))
|
||||
exit(1); /* error message already given */
|
||||
}
|
||||
if (pathspec)
|
||||
prefix = verify_pathspec(prefix);
|
||||
} else if (argc != i)
|
||||
exit(1); /* error message already given */
|
||||
|
||||
/* Treat unmatching pathspec elements as errors */
|
||||
if (pathspec && error_unmatch) {
|
||||
|
||||
@@ -94,11 +94,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
|
||||
transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
|
||||
|
||||
ref = transport_get_remote_refs(transport);
|
||||
transport_disconnect(transport);
|
||||
|
||||
if (!ref)
|
||||
if (transport_disconnect(transport))
|
||||
return 1;
|
||||
|
||||
for ( ; ref; ref = ref->next) {
|
||||
if (!check_ref_type(ref, flags))
|
||||
continue;
|
||||
|
||||
@@ -46,7 +46,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
|
||||
&xpp, XDL_MERGE_ZEALOUS, &result);
|
||||
&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
free(mmfs[i].ptr);
|
||||
@@ -57,7 +57,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (!f)
|
||||
ret = error("Could not open %s for writing", filename);
|
||||
else if (fwrite(result.ptr, result.size, 1, f) != 1)
|
||||
else if (result.size &&
|
||||
fwrite(result.ptr, result.size, 1, f) != 1)
|
||||
ret = error("Could not write to %s", filename);
|
||||
else if (fclose(f))
|
||||
ret = error("Could not close %s", filename);
|
||||
|
||||
@@ -7,16 +7,18 @@
|
||||
#include "cache-tree.h"
|
||||
#include "commit.h"
|
||||
#include "blob.h"
|
||||
#include "builtin.h"
|
||||
#include "tree-walk.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "run-command.h"
|
||||
#include "tag.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "path-list.h"
|
||||
#include "xdiff-interface.h"
|
||||
#include "ll-merge.h"
|
||||
#include "interpolate.h"
|
||||
#include "attr.h"
|
||||
#include "merge-recursive.h"
|
||||
|
||||
static int subtree_merge;
|
||||
|
||||
@@ -211,6 +213,8 @@ static int git_merge_trees(int index_only,
|
||||
opts.merge = 1;
|
||||
opts.head_idx = 2;
|
||||
opts.fn = threeway_merge;
|
||||
opts.src_index = &the_index;
|
||||
opts.dst_index = &the_index;
|
||||
|
||||
init_tree_desc_from_tree(t+0, common);
|
||||
init_tree_desc_from_tree(t+1, head);
|
||||
@@ -221,22 +225,11 @@ static int git_merge_trees(int index_only,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int unmerged_index(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tree *git_write_tree(void)
|
||||
struct tree *write_tree_from_memory(void)
|
||||
{
|
||||
struct tree *result = NULL;
|
||||
|
||||
if (unmerged_index()) {
|
||||
if (unmerged_cache()) {
|
||||
int i;
|
||||
output(0, "There are unmerged index entries:");
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
@@ -624,364 +617,16 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
|
||||
mm->size = size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Customizable low-level merge drivers support.
|
||||
*/
|
||||
|
||||
struct ll_merge_driver;
|
||||
typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
|
||||
const char *path,
|
||||
mmfile_t *orig,
|
||||
mmfile_t *src1, const char *name1,
|
||||
mmfile_t *src2, const char *name2,
|
||||
mmbuffer_t *result);
|
||||
|
||||
struct ll_merge_driver {
|
||||
const char *name;
|
||||
const char *description;
|
||||
ll_merge_fn fn;
|
||||
const char *recursive;
|
||||
struct ll_merge_driver *next;
|
||||
char *cmdline;
|
||||
};
|
||||
|
||||
/*
|
||||
* Built-in low-levels
|
||||
*/
|
||||
static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
|
||||
const char *path_unused,
|
||||
mmfile_t *orig,
|
||||
mmfile_t *src1, const char *name1,
|
||||
mmfile_t *src2, const char *name2,
|
||||
mmbuffer_t *result)
|
||||
{
|
||||
/*
|
||||
* The tentative merge result is "ours" for the final round,
|
||||
* or common ancestor for an internal merge. Still return
|
||||
* "conflicted merge" status.
|
||||
*/
|
||||
mmfile_t *stolen = index_only ? orig : src1;
|
||||
|
||||
result->ptr = stolen->ptr;
|
||||
result->size = stolen->size;
|
||||
stolen->ptr = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
|
||||
const char *path_unused,
|
||||
mmfile_t *orig,
|
||||
mmfile_t *src1, const char *name1,
|
||||
mmfile_t *src2, const char *name2,
|
||||
mmbuffer_t *result)
|
||||
{
|
||||
xpparam_t xpp;
|
||||
|
||||
if (buffer_is_binary(orig->ptr, orig->size) ||
|
||||
buffer_is_binary(src1->ptr, src1->size) ||
|
||||
buffer_is_binary(src2->ptr, src2->size)) {
|
||||
warning("Cannot merge binary files: %s vs. %s\n",
|
||||
name1, name2);
|
||||
return ll_binary_merge(drv_unused, path_unused,
|
||||
orig, src1, name1,
|
||||
src2, name2,
|
||||
result);
|
||||
}
|
||||
|
||||
memset(&xpp, 0, sizeof(xpp));
|
||||
return xdl_merge(orig,
|
||||
src1, name1,
|
||||
src2, name2,
|
||||
&xpp, XDL_MERGE_ZEALOUS,
|
||||
result);
|
||||
}
|
||||
|
||||
static int ll_union_merge(const struct ll_merge_driver *drv_unused,
|
||||
const char *path_unused,
|
||||
mmfile_t *orig,
|
||||
mmfile_t *src1, const char *name1,
|
||||
mmfile_t *src2, const char *name2,
|
||||
mmbuffer_t *result)
|
||||
{
|
||||
char *src, *dst;
|
||||
long size;
|
||||
const int marker_size = 7;
|
||||
|
||||
int status = ll_xdl_merge(drv_unused, path_unused,
|
||||
orig, src1, NULL, src2, NULL, result);
|
||||
if (status <= 0)
|
||||
return status;
|
||||
size = result->size;
|
||||
src = dst = result->ptr;
|
||||
while (size) {
|
||||
char ch;
|
||||
if ((marker_size < size) &&
|
||||
(*src == '<' || *src == '=' || *src == '>')) {
|
||||
int i;
|
||||
ch = *src;
|
||||
for (i = 0; i < marker_size; i++)
|
||||
if (src[i] != ch)
|
||||
goto not_a_marker;
|
||||
if (src[marker_size] != '\n')
|
||||
goto not_a_marker;
|
||||
src += marker_size + 1;
|
||||
size -= marker_size + 1;
|
||||
continue;
|
||||
}
|
||||
not_a_marker:
|
||||
do {
|
||||
ch = *src++;
|
||||
*dst++ = ch;
|
||||
size--;
|
||||
} while (ch != '\n' && size);
|
||||
}
|
||||
result->size = dst - result->ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LL_BINARY_MERGE 0
|
||||
#define LL_TEXT_MERGE 1
|
||||
#define LL_UNION_MERGE 2
|
||||
static struct ll_merge_driver ll_merge_drv[] = {
|
||||
{ "binary", "built-in binary merge", ll_binary_merge },
|
||||
{ "text", "built-in 3-way text merge", ll_xdl_merge },
|
||||
{ "union", "built-in union merge", ll_union_merge },
|
||||
};
|
||||
|
||||
static void create_temp(mmfile_t *src, char *path)
|
||||
{
|
||||
int fd;
|
||||
|
||||
strcpy(path, ".merge_file_XXXXXX");
|
||||
fd = xmkstemp(path);
|
||||
if (write_in_full(fd, src->ptr, src->size) != src->size)
|
||||
die("unable to write temp-file");
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* User defined low-level merge driver support.
|
||||
*/
|
||||
static int ll_ext_merge(const struct ll_merge_driver *fn,
|
||||
const char *path,
|
||||
mmfile_t *orig,
|
||||
mmfile_t *src1, const char *name1,
|
||||
mmfile_t *src2, const char *name2,
|
||||
mmbuffer_t *result)
|
||||
{
|
||||
char temp[3][50];
|
||||
char cmdbuf[2048];
|
||||
struct interp table[] = {
|
||||
{ "%O" },
|
||||
{ "%A" },
|
||||
{ "%B" },
|
||||
};
|
||||
struct child_process child;
|
||||
const char *args[20];
|
||||
int status, fd, i;
|
||||
struct stat st;
|
||||
|
||||
if (fn->cmdline == NULL)
|
||||
die("custom merge driver %s lacks command line.", fn->name);
|
||||
|
||||
result->ptr = NULL;
|
||||
result->size = 0;
|
||||
create_temp(orig, temp[0]);
|
||||
create_temp(src1, temp[1]);
|
||||
create_temp(src2, temp[2]);
|
||||
|
||||
interp_set_entry(table, 0, temp[0]);
|
||||
interp_set_entry(table, 1, temp[1]);
|
||||
interp_set_entry(table, 2, temp[2]);
|
||||
|
||||
output(1, "merging %s using %s", path,
|
||||
fn->description ? fn->description : fn->name);
|
||||
|
||||
interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
|
||||
|
||||
memset(&child, 0, sizeof(child));
|
||||
child.argv = args;
|
||||
args[0] = "sh";
|
||||
args[1] = "-c";
|
||||
args[2] = cmdbuf;
|
||||
args[3] = NULL;
|
||||
|
||||
status = run_command(&child);
|
||||
if (status < -ERR_RUN_COMMAND_FORK)
|
||||
; /* failure in run-command */
|
||||
else
|
||||
status = -status;
|
||||
fd = open(temp[1], O_RDONLY);
|
||||
if (fd < 0)
|
||||
goto bad;
|
||||
if (fstat(fd, &st))
|
||||
goto close_bad;
|
||||
result->size = st.st_size;
|
||||
result->ptr = xmalloc(result->size + 1);
|
||||
if (read_in_full(fd, result->ptr, result->size) != result->size) {
|
||||
free(result->ptr);
|
||||
result->ptr = NULL;
|
||||
result->size = 0;
|
||||
}
|
||||
close_bad:
|
||||
close(fd);
|
||||
bad:
|
||||
for (i = 0; i < 3; i++)
|
||||
unlink(temp[i]);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* merge.default and merge.driver configuration items
|
||||
*/
|
||||
static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
|
||||
static const char *default_ll_merge;
|
||||
|
||||
static int read_merge_config(const char *var, const char *value)
|
||||
{
|
||||
struct ll_merge_driver *fn;
|
||||
const char *ep, *name;
|
||||
int namelen;
|
||||
|
||||
if (!strcmp(var, "merge.default")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
default_ll_merge = strdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are not interested in anything but "merge.<name>.variable";
|
||||
* especially, we do not want to look at variables such as
|
||||
* "merge.summary", "merge.tool", and "merge.verbosity".
|
||||
*/
|
||||
if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Find existing one as we might be processing merge.<name>.var2
|
||||
* after seeing merge.<name>.var1.
|
||||
*/
|
||||
name = var + 6;
|
||||
namelen = ep - name;
|
||||
for (fn = ll_user_merge; fn; fn = fn->next)
|
||||
if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
|
||||
break;
|
||||
if (!fn) {
|
||||
fn = xcalloc(1, sizeof(struct ll_merge_driver));
|
||||
fn->name = xmemdupz(name, namelen);
|
||||
fn->fn = ll_ext_merge;
|
||||
*ll_user_merge_tail = fn;
|
||||
ll_user_merge_tail = &(fn->next);
|
||||
}
|
||||
|
||||
ep++;
|
||||
|
||||
if (!strcmp("name", ep)) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
fn->description = strdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp("driver", ep)) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
/*
|
||||
* merge.<name>.driver specifies the command line:
|
||||
*
|
||||
* command-line
|
||||
*
|
||||
* The command-line will be interpolated with the following
|
||||
* tokens and is given to the shell:
|
||||
*
|
||||
* %O - temporary file name for the merge base.
|
||||
* %A - temporary file name for our version.
|
||||
* %B - temporary file name for the other branches' version.
|
||||
*
|
||||
* The external merge driver should write the results in the
|
||||
* file named by %A, and signal that it has done with zero exit
|
||||
* status.
|
||||
*/
|
||||
fn->cmdline = strdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp("recursive", ep)) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
fn->recursive = strdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void initialize_ll_merge(void)
|
||||
{
|
||||
if (ll_user_merge_tail)
|
||||
return;
|
||||
ll_user_merge_tail = &ll_user_merge;
|
||||
git_config(read_merge_config);
|
||||
}
|
||||
|
||||
static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
|
||||
{
|
||||
struct ll_merge_driver *fn;
|
||||
const char *name;
|
||||
int i;
|
||||
|
||||
initialize_ll_merge();
|
||||
|
||||
if (ATTR_TRUE(merge_attr))
|
||||
return &ll_merge_drv[LL_TEXT_MERGE];
|
||||
else if (ATTR_FALSE(merge_attr))
|
||||
return &ll_merge_drv[LL_BINARY_MERGE];
|
||||
else if (ATTR_UNSET(merge_attr)) {
|
||||
if (!default_ll_merge)
|
||||
return &ll_merge_drv[LL_TEXT_MERGE];
|
||||
else
|
||||
name = default_ll_merge;
|
||||
}
|
||||
else
|
||||
name = merge_attr;
|
||||
|
||||
for (fn = ll_user_merge; fn; fn = fn->next)
|
||||
if (!strcmp(fn->name, name))
|
||||
return fn;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
|
||||
if (!strcmp(ll_merge_drv[i].name, name))
|
||||
return &ll_merge_drv[i];
|
||||
|
||||
/* default to the 3-way */
|
||||
return &ll_merge_drv[LL_TEXT_MERGE];
|
||||
}
|
||||
|
||||
static const char *git_path_check_merge(const char *path)
|
||||
{
|
||||
static struct git_attr_check attr_merge_check;
|
||||
|
||||
if (!attr_merge_check.attr)
|
||||
attr_merge_check.attr = git_attr("merge", 5);
|
||||
|
||||
if (git_checkattr(path, 1, &attr_merge_check))
|
||||
return NULL;
|
||||
return attr_merge_check.value;
|
||||
}
|
||||
|
||||
static int ll_merge(mmbuffer_t *result_buf,
|
||||
struct diff_filespec *o,
|
||||
struct diff_filespec *a,
|
||||
struct diff_filespec *b,
|
||||
const char *branch1,
|
||||
const char *branch2)
|
||||
static int merge_3way(mmbuffer_t *result_buf,
|
||||
struct diff_filespec *o,
|
||||
struct diff_filespec *a,
|
||||
struct diff_filespec *b,
|
||||
const char *branch1,
|
||||
const char *branch2)
|
||||
{
|
||||
mmfile_t orig, src1, src2;
|
||||
char *name1, *name2;
|
||||
int merge_status;
|
||||
const char *ll_driver_name;
|
||||
const struct ll_merge_driver *driver;
|
||||
|
||||
name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
|
||||
name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
|
||||
@@ -990,14 +635,9 @@ static int ll_merge(mmbuffer_t *result_buf,
|
||||
fill_mm(a->sha1, &src1);
|
||||
fill_mm(b->sha1, &src2);
|
||||
|
||||
ll_driver_name = git_path_check_merge(a->path);
|
||||
driver = find_ll_merge_driver(ll_driver_name);
|
||||
|
||||
if (index_only && driver->recursive)
|
||||
driver = find_ll_merge_driver(driver->recursive);
|
||||
merge_status = driver->fn(driver, a->path,
|
||||
&orig, &src1, name1, &src2, name2,
|
||||
result_buf);
|
||||
merge_status = ll_merge(result_buf, a->path, &orig,
|
||||
&src1, name1, &src2, name2,
|
||||
index_only);
|
||||
|
||||
free(name1);
|
||||
free(name2);
|
||||
@@ -1028,9 +668,20 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
|
||||
if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
|
||||
result.merge = 1;
|
||||
|
||||
result.mode = a->mode == o->mode ? b->mode: a->mode;
|
||||
/*
|
||||
* Merge modes
|
||||
*/
|
||||
if (a->mode == b->mode || a->mode == o->mode)
|
||||
result.mode = b->mode;
|
||||
else {
|
||||
result.mode = a->mode;
|
||||
if (b->mode != o->mode) {
|
||||
result.clean = 0;
|
||||
result.merge = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (sha_eq(a->sha1, o->sha1))
|
||||
if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, o->sha1))
|
||||
hashcpy(result.sha, b->sha1);
|
||||
else if (sha_eq(b->sha1, o->sha1))
|
||||
hashcpy(result.sha, a->sha1);
|
||||
@@ -1038,8 +689,8 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
|
||||
mmbuffer_t result_buf;
|
||||
int merge_status;
|
||||
|
||||
merge_status = ll_merge(&result_buf, o, a, b,
|
||||
branch1, branch2);
|
||||
merge_status = merge_3way(&result_buf, o, a, b,
|
||||
branch1, branch2);
|
||||
|
||||
if ((merge_status < 0) || !result_buf.ptr)
|
||||
die("Failed to execute internal merge");
|
||||
@@ -1496,12 +1147,12 @@ static int process_entry(const char *path, struct stage_data *entry,
|
||||
return clean_merge;
|
||||
}
|
||||
|
||||
static int merge_trees(struct tree *head,
|
||||
struct tree *merge,
|
||||
struct tree *common,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct tree **result)
|
||||
int merge_trees(struct tree *head,
|
||||
struct tree *merge,
|
||||
struct tree *common,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct tree **result)
|
||||
{
|
||||
int code, clean;
|
||||
|
||||
@@ -1523,7 +1174,7 @@ static int merge_trees(struct tree *head,
|
||||
sha1_to_hex(head->object.sha1),
|
||||
sha1_to_hex(merge->object.sha1));
|
||||
|
||||
if (unmerged_index()) {
|
||||
if (unmerged_cache()) {
|
||||
struct path_list *entries, *re_head, *re_merge;
|
||||
int i;
|
||||
path_list_clear(¤t_file_set, 1);
|
||||
@@ -1553,7 +1204,7 @@ static int merge_trees(struct tree *head,
|
||||
clean = 1;
|
||||
|
||||
if (index_only)
|
||||
*result = git_write_tree();
|
||||
*result = write_tree_from_memory();
|
||||
|
||||
return clean;
|
||||
}
|
||||
@@ -1573,12 +1224,12 @@ static struct commit_list *reverse_commit_list(struct commit_list *list)
|
||||
* Merge the commits h1 and h2, return the resulting virtual
|
||||
* commit object and a flag indicating the cleanness of the merge.
|
||||
*/
|
||||
static int merge(struct commit *h1,
|
||||
struct commit *h2,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct commit_list *ca,
|
||||
struct commit **result)
|
||||
int merge_recursive(struct commit *h1,
|
||||
struct commit *h2,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct commit_list *ca,
|
||||
struct commit **result)
|
||||
{
|
||||
struct commit_list *iter;
|
||||
struct commit *merged_common_ancestors;
|
||||
@@ -1623,11 +1274,11 @@ static int merge(struct commit *h1,
|
||||
* "conflicts" were already resolved.
|
||||
*/
|
||||
discard_cache();
|
||||
merge(merged_common_ancestors, iter->item,
|
||||
"Temporary merge branch 1",
|
||||
"Temporary merge branch 2",
|
||||
NULL,
|
||||
&merged_common_ancestors);
|
||||
merge_recursive(merged_common_ancestors, iter->item,
|
||||
"Temporary merge branch 1",
|
||||
"Temporary merge branch 2",
|
||||
NULL,
|
||||
&merged_common_ancestors);
|
||||
call_depth--;
|
||||
|
||||
if (!merged_common_ancestors)
|
||||
@@ -1698,7 +1349,7 @@ static int merge_config(const char *var, const char *value)
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
static const char *bases[20];
|
||||
static unsigned bases_count = 0;
|
||||
@@ -1752,7 +1403,7 @@ int main(int argc, char *argv[])
|
||||
struct commit *ancestor = get_ref(bases[i]);
|
||||
ca = commit_list_insert(ancestor, &ca);
|
||||
}
|
||||
clean = merge(h1, h2, branch1, branch2, ca, &result);
|
||||
clean = merge_recursive(h1, h2, branch1, branch2, ca, &result);
|
||||
|
||||
if (active_cache_changed &&
|
||||
(write_cache(index_fd, active_cache, active_nr) ||
|
||||
@@ -19,7 +19,6 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
||||
int count, int base_name)
|
||||
{
|
||||
int i;
|
||||
int len = prefix ? strlen(prefix) : 0;
|
||||
const char **result = xmalloc((count + 1) * sizeof(const char *));
|
||||
memcpy(result, pathspec, count * sizeof(const char *));
|
||||
result[count] = NULL;
|
||||
@@ -33,11 +32,8 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
||||
if (last_slash)
|
||||
result[i] = last_slash + 1;
|
||||
}
|
||||
result[i] = prefix_path(prefix, len, result[i]);
|
||||
if (!result[i])
|
||||
exit(1); /* error already given */
|
||||
}
|
||||
return result;
|
||||
return get_pathspec(prefix, result);
|
||||
}
|
||||
|
||||
static void show_list(const char *label, struct path_list *list)
|
||||
|
||||
@@ -125,7 +125,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
|
||||
}
|
||||
|
||||
/* returns a static buffer */
|
||||
static const char *get_rev_name(struct object *o)
|
||||
static const char *get_rev_name(const struct object *o)
|
||||
{
|
||||
static char buffer[1024];
|
||||
struct rev_name *n;
|
||||
@@ -151,6 +151,26 @@ static const char *get_rev_name(struct object *o)
|
||||
}
|
||||
}
|
||||
|
||||
static void show_name(const struct object *obj,
|
||||
const char *caller_name,
|
||||
int always, int allow_undefined, int name_only)
|
||||
{
|
||||
const char *name;
|
||||
const unsigned char *sha1 = obj->sha1;
|
||||
|
||||
if (!name_only)
|
||||
printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
|
||||
name = get_rev_name(obj);
|
||||
if (name)
|
||||
printf("%s\n", name);
|
||||
else if (allow_undefined)
|
||||
printf("undefined\n");
|
||||
else if (always)
|
||||
printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
|
||||
else
|
||||
die("cannot describe '%s'", sha1_to_hex(sha1));
|
||||
}
|
||||
|
||||
static char const * const name_rev_usage[] = {
|
||||
"git-name-rev [options] ( --all | --stdin | <commit>... )",
|
||||
NULL
|
||||
@@ -159,7 +179,7 @@ static char const * const name_rev_usage[] = {
|
||||
int cmd_name_rev(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct object_array revs = { 0, 0, NULL };
|
||||
int all = 0, transform_stdin = 0, allow_undefined = 1;
|
||||
int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
|
||||
struct name_ref_data data = { 0, 0, NULL };
|
||||
struct option opts[] = {
|
||||
OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
|
||||
@@ -170,6 +190,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
|
||||
OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
|
||||
OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
|
||||
OPT_BOOLEAN(0, "always", &always,
|
||||
"show abbreviated commit object as fallback"),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
@@ -258,35 +280,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
|
||||
int i, max;
|
||||
|
||||
max = get_max_object_index();
|
||||
for (i = 0; i < max; i++) {
|
||||
struct object * obj = get_indexed_object(i);
|
||||
const char *name;
|
||||
if (!obj)
|
||||
continue;
|
||||
if (!data.name_only)
|
||||
printf("%s ", sha1_to_hex(obj->sha1));
|
||||
name = get_rev_name(obj);
|
||||
if (name)
|
||||
printf("%s\n", name);
|
||||
else if (allow_undefined)
|
||||
printf("undefined\n");
|
||||
else
|
||||
die("cannot describe '%s'", sha1_to_hex(obj->sha1));
|
||||
}
|
||||
for (i = 0; i < max; i++)
|
||||
show_name(get_indexed_object(i), NULL,
|
||||
always, allow_undefined, data.name_only);
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < revs.nr; i++) {
|
||||
const char *name;
|
||||
if (!data.name_only)
|
||||
printf("%s ", revs.objects[i].name);
|
||||
name = get_rev_name(revs.objects[i].item);
|
||||
if (name)
|
||||
printf("%s\n", name);
|
||||
else if (allow_undefined)
|
||||
printf("undefined\n");
|
||||
else
|
||||
die("cannot describe '%s'", sha1_to_hex(revs.objects[i].item->sha1));
|
||||
}
|
||||
for (i = 0; i < revs.nr; i++)
|
||||
show_name(revs.objects[i].item, revs.objects[i].name,
|
||||
always, allow_undefined, data.name_only);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -8,14 +8,17 @@
|
||||
#include "tree.h"
|
||||
#include "delta.h"
|
||||
#include "pack.h"
|
||||
#include "pack-revindex.h"
|
||||
#include "csum-file.h"
|
||||
#include "tree-walk.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "list-objects.h"
|
||||
#include "progress.h"
|
||||
#include "refs.h"
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
#include "thread-utils.h"
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
@@ -25,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
|
||||
[--window=N] [--window-memory=N] [--depth=N] \n\
|
||||
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
|
||||
[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
|
||||
[--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
|
||||
[--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
|
||||
[<ref-list | <object-list]";
|
||||
|
||||
struct object_entry {
|
||||
struct pack_idx_entry idx;
|
||||
@@ -61,7 +65,7 @@ static struct pack_idx_entry **written_list;
|
||||
static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
|
||||
|
||||
static int non_empty;
|
||||
static int no_reuse_delta, no_reuse_object, keep_unreachable;
|
||||
static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
|
||||
static int local;
|
||||
static int incremental;
|
||||
static int allow_ofs_delta;
|
||||
@@ -91,158 +95,12 @@ static unsigned long window_memory_limit = 0;
|
||||
static int *object_ix;
|
||||
static int object_ix_hashsz;
|
||||
|
||||
/*
|
||||
* Pack index for existing packs give us easy access to the offsets into
|
||||
* corresponding pack file where each object's data starts, but the entries
|
||||
* do not store the size of the compressed representation (uncompressed
|
||||
* size is easily available by examining the pack entry header). It is
|
||||
* also rather expensive to find the sha1 for an object given its offset.
|
||||
*
|
||||
* We build a hashtable of existing packs (pack_revindex), and keep reverse
|
||||
* index here -- pack index file is sorted by object name mapping to offset;
|
||||
* this pack_revindex[].revindex array is a list of offset/index_nr pairs
|
||||
* ordered by offset, so if you know the offset of an object, next offset
|
||||
* is where its packed representation ends and the index_nr can be used to
|
||||
* get the object sha1 from the main index.
|
||||
*/
|
||||
struct revindex_entry {
|
||||
off_t offset;
|
||||
unsigned int nr;
|
||||
};
|
||||
struct pack_revindex {
|
||||
struct packed_git *p;
|
||||
struct revindex_entry *revindex;
|
||||
};
|
||||
static struct pack_revindex *pack_revindex;
|
||||
static int pack_revindex_hashsz;
|
||||
|
||||
/*
|
||||
* stats
|
||||
*/
|
||||
static uint32_t written, written_delta;
|
||||
static uint32_t reused, reused_delta;
|
||||
|
||||
static int pack_revindex_ix(struct packed_git *p)
|
||||
{
|
||||
unsigned long ui = (unsigned long)p;
|
||||
int i;
|
||||
|
||||
ui = ui ^ (ui >> 16); /* defeat structure alignment */
|
||||
i = (int)(ui % pack_revindex_hashsz);
|
||||
while (pack_revindex[i].p) {
|
||||
if (pack_revindex[i].p == p)
|
||||
return i;
|
||||
if (++i == pack_revindex_hashsz)
|
||||
i = 0;
|
||||
}
|
||||
return -1 - i;
|
||||
}
|
||||
|
||||
static void prepare_pack_ix(void)
|
||||
{
|
||||
int num;
|
||||
struct packed_git *p;
|
||||
for (num = 0, p = packed_git; p; p = p->next)
|
||||
num++;
|
||||
if (!num)
|
||||
return;
|
||||
pack_revindex_hashsz = num * 11;
|
||||
pack_revindex = xcalloc(sizeof(*pack_revindex), pack_revindex_hashsz);
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
num = pack_revindex_ix(p);
|
||||
num = - 1 - num;
|
||||
pack_revindex[num].p = p;
|
||||
}
|
||||
/* revindex elements are lazily initialized */
|
||||
}
|
||||
|
||||
static int cmp_offset(const void *a_, const void *b_)
|
||||
{
|
||||
const struct revindex_entry *a = a_;
|
||||
const struct revindex_entry *b = b_;
|
||||
return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ordered list of offsets of objects in the pack.
|
||||
*/
|
||||
static void prepare_pack_revindex(struct pack_revindex *rix)
|
||||
{
|
||||
struct packed_git *p = rix->p;
|
||||
int num_ent = p->num_objects;
|
||||
int i;
|
||||
const char *index = p->index_data;
|
||||
|
||||
rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
|
||||
index += 4 * 256;
|
||||
|
||||
if (p->index_version > 1) {
|
||||
const uint32_t *off_32 =
|
||||
(uint32_t *)(index + 8 + p->num_objects * (20 + 4));
|
||||
const uint32_t *off_64 = off_32 + p->num_objects;
|
||||
for (i = 0; i < num_ent; i++) {
|
||||
uint32_t off = ntohl(*off_32++);
|
||||
if (!(off & 0x80000000)) {
|
||||
rix->revindex[i].offset = off;
|
||||
} else {
|
||||
rix->revindex[i].offset =
|
||||
((uint64_t)ntohl(*off_64++)) << 32;
|
||||
rix->revindex[i].offset |=
|
||||
ntohl(*off_64++);
|
||||
}
|
||||
rix->revindex[i].nr = i;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < num_ent; i++) {
|
||||
uint32_t hl = *((uint32_t *)(index + 24 * i));
|
||||
rix->revindex[i].offset = ntohl(hl);
|
||||
rix->revindex[i].nr = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* This knows the pack format -- the 20-byte trailer
|
||||
* follows immediately after the last object data.
|
||||
*/
|
||||
rix->revindex[num_ent].offset = p->pack_size - 20;
|
||||
rix->revindex[num_ent].nr = -1;
|
||||
qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
|
||||
}
|
||||
|
||||
static struct revindex_entry * find_packed_object(struct packed_git *p,
|
||||
off_t ofs)
|
||||
{
|
||||
int num;
|
||||
int lo, hi;
|
||||
struct pack_revindex *rix;
|
||||
struct revindex_entry *revindex;
|
||||
num = pack_revindex_ix(p);
|
||||
if (num < 0)
|
||||
die("internal error: pack revindex uninitialized");
|
||||
rix = &pack_revindex[num];
|
||||
if (!rix->revindex)
|
||||
prepare_pack_revindex(rix);
|
||||
revindex = rix->revindex;
|
||||
lo = 0;
|
||||
hi = p->num_objects + 1;
|
||||
do {
|
||||
int mi = (lo + hi) / 2;
|
||||
if (revindex[mi].offset == ofs) {
|
||||
return revindex + mi;
|
||||
}
|
||||
else if (ofs < revindex[mi].offset)
|
||||
hi = mi;
|
||||
else
|
||||
lo = mi + 1;
|
||||
} while (lo < hi);
|
||||
die("internal error: pack revindex corrupt");
|
||||
}
|
||||
|
||||
static const unsigned char *find_packed_object_name(struct packed_git *p,
|
||||
off_t ofs)
|
||||
{
|
||||
struct revindex_entry *entry = find_packed_object(p, ofs);
|
||||
return nth_packed_object_sha1(p, entry->nr);
|
||||
}
|
||||
|
||||
static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
|
||||
{
|
||||
@@ -509,7 +367,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
}
|
||||
hdrlen = encode_header(obj_type, entry->size, header);
|
||||
offset = entry->in_pack_offset;
|
||||
revidx = find_packed_object(p, offset);
|
||||
revidx = find_pack_revindex(p, offset);
|
||||
datalen = revidx[1].offset - offset;
|
||||
if (!pack_to_stdout && p->index_version > 1 &&
|
||||
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
|
||||
@@ -596,6 +454,7 @@ static void write_pack_file(void)
|
||||
struct pack_header hdr;
|
||||
int do_progress = progress >> pack_to_stdout;
|
||||
uint32_t nr_remaining = nr_result;
|
||||
time_t last_mtime = 0;
|
||||
|
||||
if (do_progress)
|
||||
progress_state = start_progress("Writing objects", nr_result);
|
||||
@@ -646,6 +505,7 @@ static void write_pack_file(void)
|
||||
|
||||
if (!pack_to_stdout) {
|
||||
mode_t mode = umask(0);
|
||||
struct stat st;
|
||||
char *idx_tmp_name, tmpname[PATH_MAX];
|
||||
|
||||
umask(mode);
|
||||
@@ -653,6 +513,7 @@ static void write_pack_file(void)
|
||||
|
||||
idx_tmp_name = write_idx_file(NULL, written_list,
|
||||
nr_written, sha1);
|
||||
|
||||
snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
|
||||
base_name, sha1_to_hex(sha1));
|
||||
if (adjust_perm(pack_tmp_name, mode))
|
||||
@@ -661,6 +522,28 @@ static void write_pack_file(void)
|
||||
if (rename(pack_tmp_name, tmpname))
|
||||
die("unable to rename temporary pack file: %s",
|
||||
strerror(errno));
|
||||
|
||||
/*
|
||||
* Packs are runtime accessed in their mtime
|
||||
* order since newer packs are more likely to contain
|
||||
* younger objects. So if we are creating multiple
|
||||
* packs then we should modify the mtime of later ones
|
||||
* to preserve this property.
|
||||
*/
|
||||
if (stat(tmpname, &st) < 0) {
|
||||
warning("failed to stat %s: %s",
|
||||
tmpname, strerror(errno));
|
||||
} else if (!last_mtime) {
|
||||
last_mtime = st.st_mtime;
|
||||
} else {
|
||||
struct utimbuf utb;
|
||||
utb.actime = st.st_atime;
|
||||
utb.modtime = --last_mtime;
|
||||
if (utime(tmpname, &utb) < 0)
|
||||
warning("failed utime() on %s: %s",
|
||||
tmpname, strerror(errno));
|
||||
}
|
||||
|
||||
snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
|
||||
base_name, sha1_to_hex(sha1));
|
||||
if (adjust_perm(idx_tmp_name, mode))
|
||||
@@ -669,6 +552,7 @@ static void write_pack_file(void)
|
||||
if (rename(idx_tmp_name, tmpname))
|
||||
die("unable to rename temporary index file: %s",
|
||||
strerror(errno));
|
||||
|
||||
free(idx_tmp_name);
|
||||
free(pack_tmp_name);
|
||||
puts(sha1_to_hex(sha1));
|
||||
@@ -1161,8 +1045,11 @@ static void check_object(struct object_entry *entry)
|
||||
die("delta base offset out of bound for %s",
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
ofs = entry->in_pack_offset - ofs;
|
||||
if (!no_reuse_delta && !entry->preferred_base)
|
||||
base_ref = find_packed_object_name(p, ofs);
|
||||
if (!no_reuse_delta && !entry->preferred_base) {
|
||||
struct revindex_entry *revidx;
|
||||
revidx = find_pack_revindex(p, ofs);
|
||||
base_ref = nth_packed_object_sha1(p, revidx->nr);
|
||||
}
|
||||
entry->in_pack_header_size = used + used_0;
|
||||
break;
|
||||
}
|
||||
@@ -1239,9 +1126,11 @@ static void get_object_details(void)
|
||||
sorted_by_offset[i] = objects + i;
|
||||
qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
|
||||
|
||||
prepare_pack_ix();
|
||||
init_pack_revindex();
|
||||
|
||||
for (i = 0; i < nr_objects; i++)
|
||||
check_object(sorted_by_offset[i]);
|
||||
|
||||
free(sorted_by_offset);
|
||||
}
|
||||
|
||||
@@ -1428,8 +1317,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
* accounting lock. Compiler will optimize the strangeness
|
||||
* away when THREADED_DELTA_SEARCH is not defined.
|
||||
*/
|
||||
if (trg_entry->delta_data)
|
||||
free(trg_entry->delta_data);
|
||||
free(trg_entry->delta_data);
|
||||
cache_lock();
|
||||
if (trg_entry->delta_data) {
|
||||
delta_cache_size -= trg_entry->delta_size;
|
||||
@@ -1770,6 +1658,18 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
|
||||
#define ll_find_deltas(l, s, w, d, p) find_deltas(l, &s, w, d, p)
|
||||
#endif
|
||||
|
||||
static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
unsigned char peeled[20];
|
||||
|
||||
if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
|
||||
!peel_ref(path, peeled) && /* peelable? */
|
||||
!is_null_sha1(peeled) && /* annotated tag? */
|
||||
locate_object_entry(peeled)) /* object packed? */
|
||||
add_object_entry(sha1, OBJ_TAG, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prepare_pack(int window, int depth)
|
||||
{
|
||||
struct object_entry **delta_list;
|
||||
@@ -1852,11 +1752,11 @@ static int git_pack_config(const char *k, const char *v)
|
||||
}
|
||||
if (!strcmp(k, "pack.threads")) {
|
||||
delta_search_threads = git_config_int(k, v);
|
||||
if (delta_search_threads < 1)
|
||||
if (delta_search_threads < 0)
|
||||
die("invalid number of threads specified (%d)",
|
||||
delta_search_threads);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
if (delta_search_threads != 1)
|
||||
warning("no threads support, ignoring %s", k);
|
||||
#endif
|
||||
return 0;
|
||||
@@ -2013,7 +1913,6 @@ static void get_object_list(int ac, const char **av)
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
save_commit_buffer = 0;
|
||||
track_object_refs = 0;
|
||||
setup_revisions(ac, av, &revs, NULL);
|
||||
|
||||
while (fgets(line, sizeof(line), stdin) != NULL) {
|
||||
@@ -2122,10 +2021,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
if (!prefixcmp(arg, "--threads=")) {
|
||||
char *end;
|
||||
delta_search_threads = strtoul(arg+10, &end, 0);
|
||||
if (!arg[10] || *end || delta_search_threads < 1)
|
||||
if (!arg[10] || *end || delta_search_threads < 0)
|
||||
usage(pack_usage);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
if (delta_search_threads != 1)
|
||||
warning("no threads support, "
|
||||
"ignoring %s", arg);
|
||||
#endif
|
||||
@@ -2174,6 +2073,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
keep_unreachable = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--include-tag", arg)) {
|
||||
include_tag = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--unpacked", arg) ||
|
||||
!prefixcmp(arg, "--unpacked=") ||
|
||||
!strcmp("--reflog", arg) ||
|
||||
@@ -2235,6 +2138,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
if (!pack_to_stdout && thin)
|
||||
die("--thin cannot be used to build an indexable pack.");
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
if (!delta_search_threads) /* --threads=0 means autodetect */
|
||||
delta_search_threads = online_cpus();
|
||||
#endif
|
||||
|
||||
prepare_packed_git();
|
||||
|
||||
if (progress)
|
||||
@@ -2245,6 +2153,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
rp_av[rp_ac] = NULL;
|
||||
get_object_list(rp_ac, rp_av);
|
||||
}
|
||||
if (include_tag && nr_result)
|
||||
for_each_ref(add_ref_tag, NULL);
|
||||
stop_progress(&progress_state);
|
||||
|
||||
if (non_empty && !nr_result)
|
||||
|
||||
@@ -44,15 +44,6 @@ static void set_refspecs(const char **refs, int nr)
|
||||
strcat(tag, refs[i]);
|
||||
ref = tag;
|
||||
}
|
||||
if (!strcmp("HEAD", ref)) {
|
||||
unsigned char sha1_dummy[20];
|
||||
ref = resolve_ref(ref, sha1_dummy, 1, NULL);
|
||||
if (!ref)
|
||||
die("HEAD cannot be resolved.");
|
||||
if (prefixcmp(ref, "refs/heads/"))
|
||||
die("HEAD cannot be resolved to branch.");
|
||||
ref = xstrdup(ref + 11);
|
||||
}
|
||||
add_refspec(ref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,16 +13,15 @@
|
||||
#include "dir.h"
|
||||
#include "builtin.h"
|
||||
|
||||
#define MAX_TREES 8
|
||||
static int nr_trees;
|
||||
static struct tree *trees[MAX_TREES];
|
||||
static struct tree *trees[MAX_UNPACK_TREES];
|
||||
|
||||
static int list_tree(unsigned char *sha1)
|
||||
{
|
||||
struct tree *tree;
|
||||
|
||||
if (nr_trees >= MAX_TREES)
|
||||
die("I cannot read more than %d trees", MAX_TREES);
|
||||
if (nr_trees >= MAX_UNPACK_TREES)
|
||||
die("I cannot read more than %d trees", MAX_UNPACK_TREES);
|
||||
tree = parse_tree_indirect(sha1);
|
||||
if (!tree)
|
||||
return -1;
|
||||
@@ -41,6 +40,7 @@ static int read_cache_unmerged(void)
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce)) {
|
||||
remove_index_entry(ce);
|
||||
if (last && !strcmp(ce->name, last->name))
|
||||
continue;
|
||||
cache_tree_invalidate_path(active_cache_tree, ce->name);
|
||||
@@ -96,11 +96,13 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
{
|
||||
int i, newfd, stage = 0;
|
||||
unsigned char sha1[20];
|
||||
struct tree_desc t[MAX_TREES];
|
||||
struct tree_desc t[MAX_UNPACK_TREES];
|
||||
struct unpack_trees_options opts;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.head_idx = -1;
|
||||
opts.src_index = &the_index;
|
||||
opts.dst_index = &the_index;
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
@@ -219,27 +221,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
if ((opts.dir && !opts.update))
|
||||
die("--exclude-per-directory is meaningless unless -u");
|
||||
|
||||
if (opts.prefix) {
|
||||
int pfxlen = strlen(opts.prefix);
|
||||
int pos;
|
||||
if (opts.prefix[pfxlen-1] != '/')
|
||||
die("prefix must end with /");
|
||||
if (stage != 2)
|
||||
die("binding merge takes only one tree");
|
||||
pos = cache_name_pos(opts.prefix, pfxlen);
|
||||
if (0 <= pos)
|
||||
die("corrupt index file");
|
||||
pos = -pos-1;
|
||||
if (pos < active_nr &&
|
||||
!strncmp(active_cache[pos]->name, opts.prefix, pfxlen))
|
||||
die("subdirectory '%s' already exists.", opts.prefix);
|
||||
pos = cache_name_pos(opts.prefix, pfxlen-1);
|
||||
if (0 <= pos)
|
||||
die("file '%.*s' already exists.",
|
||||
pfxlen-1, opts.prefix);
|
||||
opts.pos = -1 - pos;
|
||||
}
|
||||
|
||||
if (opts.merge) {
|
||||
if (stage < 2)
|
||||
die("just how do you expect me to merge %d trees?", stage-1);
|
||||
@@ -268,7 +249,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
parse_tree(tree);
|
||||
init_tree_desc(t+i, tree->buffer, tree->size);
|
||||
}
|
||||
unpack_trees(nr_trees, t, &opts);
|
||||
if (unpack_trees(nr_trees, t, &opts))
|
||||
return 128;
|
||||
|
||||
/*
|
||||
* When reading only one tree (either the most basic form,
|
||||
|
||||
107
builtin-reflog.c
107
builtin-reflog.c
@@ -14,6 +14,8 @@
|
||||
|
||||
static const char reflog_expire_usage[] =
|
||||
"git-reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
|
||||
static const char reflog_delete_usage[] =
|
||||
"git-reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
|
||||
|
||||
static unsigned long default_reflog_expire;
|
||||
static unsigned long default_reflog_expire_unreachable;
|
||||
@@ -22,9 +24,12 @@ struct cmd_reflog_expire_cb {
|
||||
struct rev_info revs;
|
||||
int dry_run;
|
||||
int stalefix;
|
||||
int rewrite;
|
||||
int updateref;
|
||||
int verbose;
|
||||
unsigned long expire_total;
|
||||
unsigned long expire_unreachable;
|
||||
int recno;
|
||||
};
|
||||
|
||||
struct expire_reflog_cb {
|
||||
@@ -32,6 +37,7 @@ struct expire_reflog_cb {
|
||||
const char *ref;
|
||||
struct commit *ref_commit;
|
||||
struct cmd_reflog_expire_cb *cmd;
|
||||
unsigned char last_kept_sha1[20];
|
||||
};
|
||||
|
||||
struct collected_reflog {
|
||||
@@ -213,6 +219,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
if (timestamp < cb->cmd->expire_total)
|
||||
goto prune;
|
||||
|
||||
if (cb->cmd->rewrite)
|
||||
osha1 = cb->last_kept_sha1;
|
||||
|
||||
old = new = NULL;
|
||||
if (cb->cmd->stalefix &&
|
||||
(!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
|
||||
@@ -230,6 +239,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
goto prune;
|
||||
}
|
||||
|
||||
if (cb->cmd->recno && --(cb->cmd->recno) == 0)
|
||||
goto prune;
|
||||
|
||||
if (cb->newlog) {
|
||||
char sign = (tz < 0) ? '-' : '+';
|
||||
int zone = (tz < 0) ? (-tz) : tz;
|
||||
@@ -237,6 +249,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
sha1_to_hex(osha1), sha1_to_hex(nsha1),
|
||||
email, timestamp, sign, zone,
|
||||
message);
|
||||
hashcpy(cb->last_kept_sha1, nsha1);
|
||||
}
|
||||
if (cb->cmd->verbose)
|
||||
printf("keep %s", message);
|
||||
@@ -276,13 +289,24 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
|
||||
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
|
||||
finish:
|
||||
if (cb.newlog) {
|
||||
if (fclose(cb.newlog))
|
||||
if (fclose(cb.newlog)) {
|
||||
status |= error("%s: %s", strerror(errno),
|
||||
newlog_path);
|
||||
if (rename(newlog_path, log_file)) {
|
||||
unlink(newlog_path);
|
||||
} else if (cmd->updateref &&
|
||||
(write_in_full(lock->lock_fd,
|
||||
sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
|
||||
write_in_full(lock->lock_fd, "\n", 1) != 1 ||
|
||||
close_ref(lock) < 0)) {
|
||||
status |= error("Couldn't write %s",
|
||||
lock->lk->filename);
|
||||
unlink(newlog_path);
|
||||
} else if (rename(newlog_path, log_file)) {
|
||||
status |= error("cannot rename %s to %s",
|
||||
newlog_path, log_file);
|
||||
unlink(newlog_path);
|
||||
} else if (cmd->updateref && commit_ref(lock)) {
|
||||
status |= error("Couldn't set %s", lock->ref_name);
|
||||
}
|
||||
}
|
||||
free(newlog_path);
|
||||
@@ -357,6 +381,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
|
||||
cb.expire_unreachable = approxidate(arg + 21);
|
||||
else if (!strcmp(arg, "--stale-fix"))
|
||||
cb.stalefix = 1;
|
||||
else if (!strcmp(arg, "--rewrite"))
|
||||
cb.rewrite = 1;
|
||||
else if (!strcmp(arg, "--updateref"))
|
||||
cb.updateref = 1;
|
||||
else if (!strcmp(arg, "--all"))
|
||||
do_all = 1;
|
||||
else if (!strcmp(arg, "--verbose"))
|
||||
@@ -405,6 +433,78 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
const char *email, unsigned long timestamp, int tz,
|
||||
const char *message, void *cb_data)
|
||||
{
|
||||
struct cmd_reflog_expire_cb *cb = cb_data;
|
||||
if (!cb->expire_total || timestamp < cb->expire_total)
|
||||
cb->recno++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct cmd_reflog_expire_cb cb;
|
||||
int i, status = 0;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
|
||||
cb.dry_run = 1;
|
||||
else if (!strcmp(arg, "--rewrite"))
|
||||
cb.rewrite = 1;
|
||||
else if (!strcmp(arg, "--updateref"))
|
||||
cb.updateref = 1;
|
||||
else if (!strcmp(arg, "--verbose"))
|
||||
cb.verbose = 1;
|
||||
else if (!strcmp(arg, "--")) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
else if (arg[0] == '-')
|
||||
usage(reflog_delete_usage);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (argc - i < 1)
|
||||
return error("Nothing to delete?");
|
||||
|
||||
for ( ; i < argc; i++) {
|
||||
const char *spec = strstr(argv[i], "@{");
|
||||
unsigned char sha1[20];
|
||||
char *ep, *ref;
|
||||
int recno;
|
||||
|
||||
if (!spec) {
|
||||
status |= error("Not a reflog: %s", argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dwim_ref(argv[i], spec - argv[i], sha1, &ref)) {
|
||||
status |= error("%s points nowhere!", argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
recno = strtoul(spec + 2, &ep, 10);
|
||||
if (*ep == '}') {
|
||||
cb.recno = -recno;
|
||||
for_each_reflog_ent(ref, count_reflog_ent, &cb);
|
||||
} else {
|
||||
cb.expire_total = approxidate(spec + 2);
|
||||
for_each_reflog_ent(ref, count_reflog_ent, &cb);
|
||||
cb.expire_total = 0;
|
||||
}
|
||||
|
||||
status |= expire_reflog(ref, sha1, 0, &cb);
|
||||
free(ref);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* main "reflog"
|
||||
*/
|
||||
@@ -424,6 +524,9 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
|
||||
if (!strcmp(argv[1], "expire"))
|
||||
return cmd_reflog_expire(argc - 1, argv + 1, prefix);
|
||||
|
||||
if (!strcmp(argv[1], "delete"))
|
||||
return cmd_reflog_delete(argc - 1, argv + 1, prefix);
|
||||
|
||||
/* Not a recognized reflog command..*/
|
||||
usage(reflog_usage);
|
||||
}
|
||||
|
||||
605
builtin-remote.c
Normal file
605
builtin-remote.c
Normal file
@@ -0,0 +1,605 @@
|
||||
#include "cache.h"
|
||||
#include "parse-options.h"
|
||||
#include "transport.h"
|
||||
#include "remote.h"
|
||||
#include "path-list.h"
|
||||
#include "strbuf.h"
|
||||
#include "run-command.h"
|
||||
#include "refs.h"
|
||||
|
||||
static const char * const builtin_remote_usage[] = {
|
||||
"git remote",
|
||||
"git remote add <name> <url>",
|
||||
"git remote rm <name>",
|
||||
"git remote show <name>",
|
||||
"git remote prune <name>",
|
||||
"git remote update [group]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int verbose;
|
||||
|
||||
static inline int postfixcmp(const char *string, const char *postfix)
|
||||
{
|
||||
int len1 = strlen(string), len2 = strlen(postfix);
|
||||
if (len1 < len2)
|
||||
return 1;
|
||||
return strcmp(string + len1 - len2, postfix);
|
||||
}
|
||||
|
||||
static inline const char *skip_prefix(const char *name, const char *prefix)
|
||||
{
|
||||
return !name ? "" :
|
||||
prefixcmp(name, prefix) ? name : name + strlen(prefix);
|
||||
}
|
||||
|
||||
static int opt_parse_track(const struct option *opt, const char *arg, int not)
|
||||
{
|
||||
struct path_list *list = opt->value;
|
||||
if (not)
|
||||
path_list_clear(list, 0);
|
||||
else
|
||||
path_list_append(arg, list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_remote(const char *name)
|
||||
{
|
||||
const char *argv[] = { "fetch", name, NULL };
|
||||
printf("Updating %s\n", name);
|
||||
if (run_command_v_opt(argv, RUN_GIT_CMD))
|
||||
return error("Could not fetch %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add(int argc, const char **argv)
|
||||
{
|
||||
int fetch = 0, mirror = 0;
|
||||
struct path_list track = { NULL, 0, 0 };
|
||||
const char *master = NULL;
|
||||
struct remote *remote;
|
||||
struct strbuf buf, buf2;
|
||||
const char *name, *url;
|
||||
int i;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_GROUP("add specific options"),
|
||||
OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
|
||||
OPT_CALLBACK('t', "track", &track, "branch",
|
||||
"branch(es) to track", opt_parse_track),
|
||||
OPT_STRING('m', "master", &master, "branch", "master branch"),
|
||||
OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
|
||||
if (argc < 2)
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
name = argv[0];
|
||||
url = argv[1];
|
||||
|
||||
remote = remote_get(name);
|
||||
if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
|
||||
remote->fetch_refspec_nr))
|
||||
die("remote %s already exists.", name);
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&buf2, 0);
|
||||
|
||||
strbuf_addf(&buf, "remote.%s.url", name);
|
||||
if (git_config_set(buf.buf, url))
|
||||
return 1;
|
||||
|
||||
if (track.nr == 0)
|
||||
path_list_append("*", &track);
|
||||
for (i = 0; i < track.nr; i++) {
|
||||
struct path_list_item *item = track.items + i;
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "remote.%s.fetch", name);
|
||||
|
||||
strbuf_reset(&buf2);
|
||||
if (mirror)
|
||||
strbuf_addf(&buf2, "refs/%s:refs/%s",
|
||||
item->path, item->path);
|
||||
else
|
||||
strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
|
||||
item->path, name, item->path);
|
||||
if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fetch && fetch_remote(name))
|
||||
return 1;
|
||||
|
||||
if (master) {
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
|
||||
|
||||
strbuf_reset(&buf2);
|
||||
strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
|
||||
|
||||
if (create_symref(buf.buf, buf2.buf, "remote add"))
|
||||
return error("Could not setup master '%s'", master);
|
||||
}
|
||||
|
||||
strbuf_release(&buf);
|
||||
strbuf_release(&buf2);
|
||||
path_list_clear(&track, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct branch_info {
|
||||
char *remote;
|
||||
struct path_list merge;
|
||||
};
|
||||
|
||||
static struct path_list branch_list;
|
||||
|
||||
static int config_read_branches(const char *key, const char *value)
|
||||
{
|
||||
if (!prefixcmp(key, "branch.")) {
|
||||
char *name;
|
||||
struct path_list_item *item;
|
||||
struct branch_info *info;
|
||||
enum { REMOTE, MERGE } type;
|
||||
|
||||
key += 7;
|
||||
if (!postfixcmp(key, ".remote")) {
|
||||
name = xstrndup(key, strlen(key) - 7);
|
||||
type = REMOTE;
|
||||
} else if (!postfixcmp(key, ".merge")) {
|
||||
name = xstrndup(key, strlen(key) - 6);
|
||||
type = MERGE;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
item = path_list_insert(name, &branch_list);
|
||||
|
||||
if (!item->util)
|
||||
item->util = xcalloc(sizeof(struct branch_info), 1);
|
||||
info = item->util;
|
||||
if (type == REMOTE) {
|
||||
if (info->remote)
|
||||
warning("more than one branch.%s", key);
|
||||
info->remote = xstrdup(value);
|
||||
} else {
|
||||
char *space = strchr(value, ' ');
|
||||
value = skip_prefix(value, "refs/heads/");
|
||||
while (space) {
|
||||
char *merge;
|
||||
merge = xstrndup(value, space - value);
|
||||
path_list_append(merge, &info->merge);
|
||||
value = skip_prefix(space + 1, "refs/heads/");
|
||||
space = strchr(value, ' ');
|
||||
}
|
||||
path_list_append(xstrdup(value), &info->merge);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_branches(void)
|
||||
{
|
||||
if (branch_list.nr)
|
||||
return;
|
||||
git_config(config_read_branches);
|
||||
sort_path_list(&branch_list);
|
||||
}
|
||||
|
||||
struct ref_states {
|
||||
struct remote *remote;
|
||||
struct strbuf remote_prefix;
|
||||
struct path_list new, stale, tracked;
|
||||
};
|
||||
|
||||
static int handle_one_branch(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct ref_states *states = cb_data;
|
||||
struct refspec refspec;
|
||||
|
||||
memset(&refspec, 0, sizeof(refspec));
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec)) {
|
||||
struct path_list_item *item;
|
||||
const char *name = skip_prefix(refspec.src, "refs/heads/");
|
||||
if (unsorted_path_list_has_path(&states->tracked, name) ||
|
||||
unsorted_path_list_has_path(&states->new,
|
||||
name))
|
||||
return 0;
|
||||
item = path_list_append(name, &states->stale);
|
||||
item->util = xstrdup(refname);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ref_states(const struct ref *ref, struct ref_states *states)
|
||||
{
|
||||
struct ref *fetch_map = NULL, **tail = &fetch_map;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < states->remote->fetch_refspec_nr; i++)
|
||||
if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
|
||||
die("Could not get fetch map for refspec %s",
|
||||
states->remote->fetch_refspec[i]);
|
||||
|
||||
states->new.strdup_paths = states->tracked.strdup_paths = 1;
|
||||
for (ref = fetch_map; ref; ref = ref->next) {
|
||||
struct path_list *target = &states->tracked;
|
||||
unsigned char sha1[20];
|
||||
void *util = NULL;
|
||||
|
||||
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
|
||||
target = &states->new;
|
||||
else {
|
||||
target = &states->tracked;
|
||||
if (hashcmp(sha1, ref->new_sha1))
|
||||
util = &states;
|
||||
}
|
||||
path_list_append(skip_prefix(ref->name, "refs/heads/"),
|
||||
target)->util = util;
|
||||
}
|
||||
free_refs(fetch_map);
|
||||
|
||||
strbuf_addf(&states->remote_prefix,
|
||||
"refs/remotes/%s/", states->remote->name);
|
||||
for_each_ref(handle_one_branch, states);
|
||||
sort_path_list(&states->stale);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct branches_for_remote {
|
||||
const char *prefix;
|
||||
struct path_list *branches;
|
||||
};
|
||||
|
||||
static int add_branch_for_removal(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct branches_for_remote *branches = cb_data;
|
||||
|
||||
if (!prefixcmp(refname, branches->prefix)) {
|
||||
struct path_list_item *item;
|
||||
|
||||
/* make sure that symrefs are deleted */
|
||||
if (flags & REF_ISSYMREF)
|
||||
return unlink(git_path(refname));
|
||||
|
||||
item = path_list_append(refname, branches->branches);
|
||||
item->util = xmalloc(20);
|
||||
hashcpy(item->util, sha1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_branches(struct path_list *branches)
|
||||
{
|
||||
int i, result = 0;
|
||||
for (i = 0; i < branches->nr; i++) {
|
||||
struct path_list_item *item = branches->items + i;
|
||||
const char *refname = item->path;
|
||||
unsigned char *sha1 = item->util;
|
||||
|
||||
if (delete_ref(refname, sha1))
|
||||
result |= error("Could not remove branch %s", refname);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int rm(int argc, const char **argv)
|
||||
{
|
||||
struct option options[] = {
|
||||
OPT_END()
|
||||
};
|
||||
struct remote *remote;
|
||||
struct strbuf buf;
|
||||
struct path_list branches = { NULL, 0, 0, 1 };
|
||||
struct branches_for_remote cb_data = { NULL, &branches };
|
||||
int i;
|
||||
|
||||
if (argc != 2)
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
remote = remote_get(argv[1]);
|
||||
if (!remote)
|
||||
die("No such remote: %s", argv[1]);
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_addf(&buf, "remote.%s", remote->name);
|
||||
if (git_config_rename_section(buf.buf, NULL) < 1)
|
||||
return error("Could not remove config section '%s'", buf.buf);
|
||||
|
||||
read_branches();
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct path_list_item *item = branch_list.items + i;
|
||||
struct branch_info *info = item->util;
|
||||
if (info->remote && !strcmp(info->remote, remote->name)) {
|
||||
const char *keys[] = { "remote", "merge", NULL }, **k;
|
||||
for (k = keys; *k; k++) {
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "branch.%s.%s",
|
||||
item->path, *k);
|
||||
if (git_config_set(buf.buf, NULL)) {
|
||||
strbuf_release(&buf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot just pass a function to for_each_ref() which deletes
|
||||
* the branches one by one, since for_each_ref() relies on cached
|
||||
* refs, which are invalidated when deleting a branch.
|
||||
*/
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "refs/remotes/%s/", remote->name);
|
||||
cb_data.prefix = buf.buf;
|
||||
i = for_each_ref(add_branch_for_removal, &cb_data);
|
||||
strbuf_release(&buf);
|
||||
|
||||
if (!i)
|
||||
i = remove_branches(&branches);
|
||||
path_list_clear(&branches, 1);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void show_list(const char *title, struct path_list *list)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!list->nr)
|
||||
return;
|
||||
|
||||
printf(title, list->nr > 1 ? "es" : "");
|
||||
printf("\n ");
|
||||
for (i = 0; i < list->nr; i++)
|
||||
printf("%s%s", i ? " " : "", list->items[i].path);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int show_or_prune(int argc, const char **argv, int prune)
|
||||
{
|
||||
int dry_run = 0, result = 0;
|
||||
struct option options[] = {
|
||||
OPT_GROUP("show specific options"),
|
||||
OPT__DRY_RUN(&dry_run),
|
||||
OPT_END()
|
||||
};
|
||||
struct ref_states states;
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
|
||||
if (argc < 1)
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
memset(&states, 0, sizeof(states));
|
||||
for (; argc; argc--, argv++) {
|
||||
struct transport *transport;
|
||||
const struct ref *ref;
|
||||
struct strbuf buf;
|
||||
int i, got_states;
|
||||
|
||||
states.remote = remote_get(*argv);
|
||||
if (!states.remote)
|
||||
return error("No such remote: %s", *argv);
|
||||
transport = transport_get(NULL, states.remote->url_nr > 0 ?
|
||||
states.remote->url[0] : NULL);
|
||||
ref = transport_get_remote_refs(transport);
|
||||
transport_disconnect(transport);
|
||||
|
||||
read_branches();
|
||||
got_states = get_ref_states(ref, &states);
|
||||
if (got_states)
|
||||
result = error("Error getting local info for '%s'",
|
||||
states.remote->name);
|
||||
|
||||
if (prune) {
|
||||
struct strbuf buf;
|
||||
int prefix_len;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
if (states.remote->fetch_refspec_nr == 1 &&
|
||||
states.remote->fetch->pattern &&
|
||||
!strcmp(states.remote->fetch->src,
|
||||
states.remote->fetch->dst))
|
||||
/* handle --mirror remote */
|
||||
strbuf_addstr(&buf, "refs/heads/");
|
||||
else
|
||||
strbuf_addf(&buf, "refs/remotes/%s/", *argv);
|
||||
prefix_len = buf.len;
|
||||
|
||||
for (i = 0; i < states.stale.nr; i++) {
|
||||
strbuf_setlen(&buf, prefix_len);
|
||||
strbuf_addstr(&buf, states.stale.items[i].path);
|
||||
result |= delete_ref(buf.buf, NULL);
|
||||
}
|
||||
|
||||
strbuf_release(&buf);
|
||||
goto cleanup_states;
|
||||
}
|
||||
|
||||
printf("* remote %s\n URL: %s\n", *argv,
|
||||
states.remote->url_nr > 0 ?
|
||||
states.remote->url[0] : "(no URL)");
|
||||
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct path_list_item *branch = branch_list.items + i;
|
||||
struct branch_info *info = branch->util;
|
||||
int j;
|
||||
|
||||
if (!info->merge.nr || strcmp(*argv, info->remote))
|
||||
continue;
|
||||
printf(" Remote branch%s merged with 'git pull' "
|
||||
"while on branch %s\n ",
|
||||
info->merge.nr > 1 ? "es" : "",
|
||||
branch->path);
|
||||
for (j = 0; j < info->merge.nr; j++)
|
||||
printf(" %s", info->merge.items[j].path);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (got_states)
|
||||
continue;
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_addf(&buf, " New remote branch%%s (next fetch will "
|
||||
"store in remotes/%s)", states.remote->name);
|
||||
show_list(buf.buf, &states.new);
|
||||
strbuf_release(&buf);
|
||||
show_list(" Stale tracking branch%s (use 'git remote prune')",
|
||||
&states.stale);
|
||||
show_list(" Tracked remote branch%s",
|
||||
&states.tracked);
|
||||
|
||||
if (states.remote->push_refspec_nr) {
|
||||
printf(" Local branch%s pushed with 'git push'\n ",
|
||||
states.remote->push_refspec_nr > 1 ?
|
||||
"es" : "");
|
||||
for (i = 0; i < states.remote->push_refspec_nr; i++) {
|
||||
struct refspec *spec = states.remote->push + i;
|
||||
printf(" %s%s%s%s", spec->force ? "+" : "",
|
||||
skip_prefix(spec->src, "refs/heads/"),
|
||||
spec->dst ? ":" : "",
|
||||
skip_prefix(spec->dst, "refs/heads/"));
|
||||
}
|
||||
}
|
||||
cleanup_states:
|
||||
/* NEEDSWORK: free remote */
|
||||
path_list_clear(&states.new, 0);
|
||||
path_list_clear(&states.stale, 0);
|
||||
path_list_clear(&states.tracked, 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int get_one_remote_for_update(struct remote *remote, void *priv)
|
||||
{
|
||||
struct path_list *list = priv;
|
||||
if (!remote->skip_default_update)
|
||||
path_list_append(xstrdup(remote->name), list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct remote_group {
|
||||
const char *name;
|
||||
struct path_list *list;
|
||||
} remote_group;
|
||||
|
||||
static int get_remote_group(const char *key, const char *value)
|
||||
{
|
||||
if (!prefixcmp(key, "remotes.") &&
|
||||
!strcmp(key + 8, remote_group.name)) {
|
||||
/* split list by white space */
|
||||
int space = strcspn(value, " \t\n");
|
||||
while (*value) {
|
||||
if (space > 1)
|
||||
path_list_append(xstrndup(value, space),
|
||||
remote_group.list);
|
||||
value += space + (value[space] != '\0');
|
||||
space = strcspn(value, " \t\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update(int argc, const char **argv)
|
||||
{
|
||||
int i, result = 0;
|
||||
struct path_list list = { NULL, 0, 0, 0 };
|
||||
static const char *default_argv[] = { NULL, "default", NULL };
|
||||
|
||||
if (argc < 2) {
|
||||
argc = 2;
|
||||
argv = default_argv;
|
||||
}
|
||||
|
||||
remote_group.list = &list;
|
||||
for (i = 1; i < argc; i++) {
|
||||
remote_group.name = argv[i];
|
||||
result = git_config(get_remote_group);
|
||||
}
|
||||
|
||||
if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default"))
|
||||
result = for_each_remote(get_one_remote_for_update, &list);
|
||||
|
||||
for (i = 0; i < list.nr; i++)
|
||||
result |= fetch_remote(list.items[i].path);
|
||||
|
||||
/* all names were strdup()ed or strndup()ed */
|
||||
list.strdup_paths = 1;
|
||||
path_list_clear(&list, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int get_one_entry(struct remote *remote, void *priv)
|
||||
{
|
||||
struct path_list *list = priv;
|
||||
|
||||
path_list_append(remote->name, list)->util = remote->url_nr ?
|
||||
(void *)remote->url[0] : NULL;
|
||||
if (remote->url_nr > 1)
|
||||
warning("Remote %s has more than one URL", remote->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_all(void)
|
||||
{
|
||||
struct path_list list = { NULL, 0, 0 };
|
||||
int result = for_each_remote(get_one_entry, &list);
|
||||
|
||||
if (!result) {
|
||||
int i;
|
||||
|
||||
sort_path_list(&list);
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
struct path_list_item *item = list.items + i;
|
||||
printf("%s%s%s\n", item->path,
|
||||
verbose ? "\t" : "",
|
||||
verbose && item->util ?
|
||||
(const char *)item->util : "");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int cmd_remote(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct option options[] = {
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT_END()
|
||||
};
|
||||
int result;
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (argc < 1)
|
||||
result = show_all();
|
||||
else if (!strcmp(argv[0], "add"))
|
||||
result = add(argc, argv);
|
||||
else if (!strcmp(argv[0], "rm"))
|
||||
result = rm(argc, argv);
|
||||
else if (!strcmp(argv[0], "show"))
|
||||
result = show_or_prune(argc, argv, 0);
|
||||
else if (!strcmp(argv[0], "prune"))
|
||||
result = show_or_prune(argc, argv, 1);
|
||||
else if (!strcmp(argv[0], "update"))
|
||||
result = update(argc, argv);
|
||||
else {
|
||||
error("Unknown subcommand: %s", argv[0]);
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
}
|
||||
|
||||
return result ? 1 : 0;
|
||||
}
|
||||
@@ -267,23 +267,6 @@ static int diff_two(const char *file1, const char *label1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_file(const char *src, const char *dest)
|
||||
{
|
||||
FILE *in, *out;
|
||||
char buffer[32768];
|
||||
int count;
|
||||
|
||||
if (!(in = fopen(src, "r")))
|
||||
return error("Could not open %s", src);
|
||||
if (!(out = fopen(dest, "w")))
|
||||
return error("Could not open %s", dest);
|
||||
while ((count = fread(buffer, 1, sizeof(buffer), in)))
|
||||
fwrite(buffer, 1, count, out);
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_plain_rerere(struct path_list *rr, int fd)
|
||||
{
|
||||
struct path_list conflict = { NULL, 0, 0, 1 };
|
||||
@@ -343,7 +326,7 @@ static int do_plain_rerere(struct path_list *rr, int fd)
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "Recorded resolution for '%s'.\n", path);
|
||||
copy_file(path, rr_path(name, "postimage"));
|
||||
copy_file(rr_path(name, "postimage"), path, 0666);
|
||||
tail_optimization:
|
||||
if (i < rr->nr - 1)
|
||||
memmove(rr->items + i,
|
||||
|
||||
@@ -16,9 +16,14 @@
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "tree.h"
|
||||
#include "branch.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char builtin_reset_usage[] =
|
||||
"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
|
||||
static const char * const git_reset_usage[] = {
|
||||
"git-reset [--mixed | --soft | --hard] [-q] [<commit>]",
|
||||
"git-reset [--mixed] <commit> [--] <paths>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *args_to_str(const char **argv)
|
||||
{
|
||||
@@ -44,18 +49,6 @@ static inline int is_merge(void)
|
||||
return !access(git_path("MERGE_HEAD"), F_OK);
|
||||
}
|
||||
|
||||
static int unmerged_files(void)
|
||||
{
|
||||
int i;
|
||||
read_cache();
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -74,14 +67,10 @@ static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
|
||||
|
||||
static void print_new_head_line(struct commit *commit)
|
||||
{
|
||||
const char *hex, *dots = "...", *body;
|
||||
const char *hex, *body;
|
||||
|
||||
hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
|
||||
if (!hex) {
|
||||
hex = sha1_to_hex(commit->object.sha1);
|
||||
dots = "";
|
||||
}
|
||||
printf("HEAD is now at %s%s", hex, dots);
|
||||
printf("HEAD is now at %s", hex);
|
||||
body = strstr(commit->buffer, "\n\n");
|
||||
if (body) {
|
||||
const char *eol;
|
||||
@@ -180,40 +169,31 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
|
||||
|
||||
int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0;
|
||||
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
|
||||
const char *rev = "HEAD";
|
||||
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
|
||||
*old_orig = NULL, sha1_old_orig[20];
|
||||
struct commit *commit;
|
||||
char *reflog_action, msg[1024];
|
||||
const struct option options[] = {
|
||||
OPT_SET_INT(0, "mixed", &reset_type,
|
||||
"reset HEAD and index", MIXED),
|
||||
OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
|
||||
OPT_SET_INT(0, "hard", &reset_type,
|
||||
"reset HEAD, index and working tree", HARD),
|
||||
OPT_BOOLEAN('q', NULL, &quiet,
|
||||
"disable showing new HEAD in hard reset"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
argc = parse_options(argc, argv, options, git_reset_usage,
|
||||
PARSE_OPT_KEEP_DASHDASH);
|
||||
reflog_action = args_to_str(argv);
|
||||
setenv("GIT_REFLOG_ACTION", reflog_action, 0);
|
||||
|
||||
while (i < argc) {
|
||||
if (!strcmp(argv[i], "--mixed")) {
|
||||
reset_type = MIXED;
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--soft")) {
|
||||
reset_type = SOFT;
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--hard")) {
|
||||
reset_type = HARD;
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-q")) {
|
||||
quiet = 1;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < argc && argv[i][0] != '-')
|
||||
if (i < argc && strcmp(argv[i], "--"))
|
||||
rev = argv[i++];
|
||||
|
||||
if (get_sha1(rev, sha1))
|
||||
@@ -226,8 +206,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (i < argc && !strcmp(argv[i], "--"))
|
||||
i++;
|
||||
else if (i < argc && argv[i][0] == '-')
|
||||
usage(builtin_reset_usage);
|
||||
|
||||
/* git reset tree [--] paths... can be used to
|
||||
* load chosen paths from the tree into the index without
|
||||
@@ -250,7 +228,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
* at all, but requires them in a good order. Other resets reset
|
||||
* the index file to the tree object we are switching to. */
|
||||
if (reset_type == SOFT) {
|
||||
if (is_merge() || unmerged_files())
|
||||
if (is_merge() || read_cache() < 0 || unmerged_cache())
|
||||
die("Cannot do a soft reset in the middle of a merge.");
|
||||
}
|
||||
else if (reset_index_file(sha1, (reset_type == HARD)))
|
||||
@@ -282,10 +260,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
break;
|
||||
}
|
||||
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
unlink(git_path("SQUASH_MSG"));
|
||||
remove_branch_state();
|
||||
|
||||
free(reflog_action);
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ static const char rev_list_usage[] =
|
||||
" --no-merges\n"
|
||||
" --remove-empty\n"
|
||||
" --all\n"
|
||||
" --branches\n"
|
||||
" --tags\n"
|
||||
" --remotes\n"
|
||||
" --stdin\n"
|
||||
" --quiet\n"
|
||||
" ordering output:\n"
|
||||
@@ -607,7 +610,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
usage(rev_list_usage);
|
||||
|
||||
save_commit_buffer = revs.verbose_header || revs.grep_filter;
|
||||
track_object_refs = 0;
|
||||
if (bisect_list)
|
||||
revs.limited = 1;
|
||||
|
||||
|
||||
@@ -315,25 +315,31 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
|
||||
s = strchr(sb.buf, ' ');
|
||||
if (!s || *sb.buf == ' ') {
|
||||
o->type = OPTION_GROUP;
|
||||
o->help = xstrdup(skipspaces(s));
|
||||
o->help = xstrdup(skipspaces(sb.buf));
|
||||
continue;
|
||||
}
|
||||
|
||||
o->type = OPTION_CALLBACK;
|
||||
o->help = xstrdup(skipspaces(s));
|
||||
o->value = &parsed;
|
||||
o->flags = PARSE_OPT_NOARG;
|
||||
o->callback = &parseopt_dump;
|
||||
switch (s[-1]) {
|
||||
case '=':
|
||||
s--;
|
||||
break;
|
||||
case '?':
|
||||
o->flags = PARSE_OPT_OPTARG;
|
||||
s--;
|
||||
break;
|
||||
default:
|
||||
o->flags = PARSE_OPT_NOARG;
|
||||
break;
|
||||
while (s > sb.buf && strchr("*=?!", s[-1])) {
|
||||
switch (*--s) {
|
||||
case '=':
|
||||
o->flags &= ~PARSE_OPT_NOARG;
|
||||
break;
|
||||
case '?':
|
||||
o->flags &= ~PARSE_OPT_NOARG;
|
||||
o->flags |= PARSE_OPT_OPTARG;
|
||||
break;
|
||||
case '!':
|
||||
o->flags |= PARSE_OPT_NONEG;
|
||||
break;
|
||||
case '*':
|
||||
o->flags |= PARSE_OPT_HIDDEN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (s - sb.buf == 1) /* short option only */
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "utf8.h"
|
||||
#include "parse-options.h"
|
||||
#include "cache-tree.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
|
||||
/*
|
||||
* This implements the builtins revert and cherry-pick.
|
||||
@@ -246,6 +248,17 @@ static char *help_msg(const unsigned char *sha1)
|
||||
return helpbuf;
|
||||
}
|
||||
|
||||
static int index_is_dirty(void)
|
||||
{
|
||||
struct rev_info rev;
|
||||
init_revisions(&rev, NULL);
|
||||
setup_revisions(0, NULL, &rev, "HEAD");
|
||||
DIFF_OPT_SET(&rev.diffopt, QUIET);
|
||||
DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
|
||||
run_diff_index(&rev, 1);
|
||||
return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
|
||||
}
|
||||
|
||||
static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
{
|
||||
unsigned char head[20];
|
||||
@@ -274,12 +287,11 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
if (write_cache_as_tree(head, 0, NULL))
|
||||
die ("Your index file is unmerged.");
|
||||
} else {
|
||||
struct wt_status s;
|
||||
|
||||
if (get_sha1("HEAD", head))
|
||||
die ("You do not have a valid HEAD");
|
||||
wt_status_prepare(&s);
|
||||
if (s.commitable)
|
||||
if (read_cache() < 0)
|
||||
die("could not read the index");
|
||||
if (index_is_dirty())
|
||||
die ("Dirty index: cannot %s", me);
|
||||
discard_cache();
|
||||
}
|
||||
@@ -397,8 +409,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
else
|
||||
return execl_git_cmd("commit", "-n", "-F", defmsg, NULL);
|
||||
}
|
||||
if (reencoded_message)
|
||||
free(reencoded_message);
|
||||
free(reencoded_message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ static int pack_objects(int fd, struct ref *refs)
|
||||
refs = refs->next;
|
||||
}
|
||||
|
||||
close(po.in);
|
||||
if (finish_command(&po))
|
||||
return error("pack-objects died with strange error");
|
||||
return 0;
|
||||
@@ -263,9 +264,7 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
|
||||
|
||||
static const char *status_abbrev(unsigned char sha1[20])
|
||||
{
|
||||
const char *abbrev;
|
||||
abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
|
||||
return abbrev ? abbrev : sha1_to_hex(sha1);
|
||||
return find_unique_abbrev(sha1, DEFAULT_ABBREV);
|
||||
}
|
||||
|
||||
static void print_ok_ref_status(struct ref *ref)
|
||||
@@ -403,12 +402,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
|
||||
if (!remote_tail)
|
||||
remote_tail = &remote_refs;
|
||||
if (match_refs(local_refs, remote_refs, &remote_tail,
|
||||
nr_refspec, refspec, flags))
|
||||
nr_refspec, refspec, flags)) {
|
||||
close(out);
|
||||
return -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");
|
||||
close(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -495,12 +497,11 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
|
||||
|
||||
packet_flush(out);
|
||||
if (new_refs && !args.dry_run) {
|
||||
if (pack_objects(out, remote_refs) < 0) {
|
||||
close(out);
|
||||
if (pack_objects(out, remote_refs) < 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
close(out);
|
||||
else
|
||||
close(out);
|
||||
|
||||
if (expect_status_report)
|
||||
ret = receive_status(in, remote_refs);
|
||||
@@ -648,7 +649,7 @@ int send_pack(struct send_pack_args *my_args,
|
||||
conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
|
||||
ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
/* do_send_pack always closes fd[1] */
|
||||
ret |= finish_connect(conn);
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
#include "revision.h"
|
||||
#include "utf8.h"
|
||||
#include "mailmap.h"
|
||||
#include "shortlog.h"
|
||||
|
||||
static const char shortlog_usage[] =
|
||||
"git-shortlog [-n] [-s] [-e] [<commit-id>... ]";
|
||||
|
||||
static char *common_repo_prefix;
|
||||
static int email;
|
||||
|
||||
static int compare_by_number(const void *a1, const void *a2)
|
||||
{
|
||||
const struct path_list_item *i1 = a1, *i2 = a2;
|
||||
@@ -26,13 +24,11 @@ static int compare_by_number(const void *a1, const void *a2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct path_list mailmap = {NULL, 0, 0, 0};
|
||||
|
||||
static void insert_one_record(struct path_list *list,
|
||||
static void insert_one_record(struct shortlog *log,
|
||||
const char *author,
|
||||
const char *oneline)
|
||||
{
|
||||
const char *dot3 = common_repo_prefix;
|
||||
const char *dot3 = log->common_repo_prefix;
|
||||
char *buffer, *p;
|
||||
struct path_list_item *item;
|
||||
struct path_list *onelines;
|
||||
@@ -47,7 +43,7 @@ static void insert_one_record(struct path_list *list,
|
||||
eoemail = strchr(boemail, '>');
|
||||
if (!eoemail)
|
||||
return;
|
||||
if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) {
|
||||
if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
|
||||
while (author < boemail && isspace(*author))
|
||||
author++;
|
||||
for (len = 0;
|
||||
@@ -61,24 +57,25 @@ static void insert_one_record(struct path_list *list,
|
||||
else
|
||||
len = strlen(namebuf);
|
||||
|
||||
if (email) {
|
||||
if (log->email) {
|
||||
size_t room = sizeof(namebuf) - len - 1;
|
||||
int maillen = eoemail - boemail + 1;
|
||||
snprintf(namebuf + len, room, " %.*s", maillen, boemail);
|
||||
}
|
||||
|
||||
buffer = xstrdup(namebuf);
|
||||
item = path_list_insert(buffer, list);
|
||||
item = path_list_insert(buffer, &log->list);
|
||||
if (item->util == NULL)
|
||||
item->util = xcalloc(1, sizeof(struct path_list));
|
||||
else
|
||||
free(buffer);
|
||||
|
||||
/* Skip any leading whitespace, including any blank lines. */
|
||||
while (*oneline && isspace(*oneline))
|
||||
oneline++;
|
||||
eol = strchr(oneline, '\n');
|
||||
if (!eol)
|
||||
eol = oneline + strlen(oneline);
|
||||
while (*oneline && isspace(*oneline) && *oneline != '\n')
|
||||
oneline++;
|
||||
if (!prefixcmp(oneline, "[PATCH")) {
|
||||
char *eob = strchr(oneline, ']');
|
||||
if (eob && (!eol || eob < eol))
|
||||
@@ -114,7 +111,7 @@ static void insert_one_record(struct path_list *list,
|
||||
onelines->items[onelines->nr++].path = buffer;
|
||||
}
|
||||
|
||||
static void read_from_stdin(struct path_list *list)
|
||||
static void read_from_stdin(struct shortlog *log)
|
||||
{
|
||||
char author[1024], oneline[1024];
|
||||
|
||||
@@ -128,39 +125,43 @@ static void read_from_stdin(struct path_list *list)
|
||||
while (fgets(oneline, sizeof(oneline), stdin) &&
|
||||
oneline[0] == '\n')
|
||||
; /* discard blanks */
|
||||
insert_one_record(list, author + 8, oneline);
|
||||
insert_one_record(log, author + 8, oneline);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_from_rev(struct rev_info *rev, struct path_list *list)
|
||||
void shortlog_add_commit(struct shortlog *log, struct commit *commit)
|
||||
{
|
||||
const char *author = NULL, *buffer;
|
||||
|
||||
buffer = commit->buffer;
|
||||
while (*buffer && *buffer != '\n') {
|
||||
const char *eol = strchr(buffer, '\n');
|
||||
|
||||
if (eol == NULL)
|
||||
eol = buffer + strlen(buffer);
|
||||
else
|
||||
eol++;
|
||||
|
||||
if (!prefixcmp(buffer, "author "))
|
||||
author = buffer + 7;
|
||||
buffer = eol;
|
||||
}
|
||||
if (!author)
|
||||
die("Missing author: %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
if (*buffer)
|
||||
buffer++;
|
||||
insert_one_record(log, author, !*buffer ? "<none>" : buffer);
|
||||
}
|
||||
|
||||
static void get_from_rev(struct rev_info *rev, struct shortlog *log)
|
||||
{
|
||||
struct commit *commit;
|
||||
|
||||
if (prepare_revision_walk(rev))
|
||||
die("revision walk setup failed");
|
||||
while ((commit = get_revision(rev)) != NULL) {
|
||||
const char *author = NULL, *buffer;
|
||||
|
||||
buffer = commit->buffer;
|
||||
while (*buffer && *buffer != '\n') {
|
||||
const char *eol = strchr(buffer, '\n');
|
||||
|
||||
if (eol == NULL)
|
||||
eol = buffer + strlen(buffer);
|
||||
else
|
||||
eol++;
|
||||
|
||||
if (!prefixcmp(buffer, "author "))
|
||||
author = buffer + 7;
|
||||
buffer = eol;
|
||||
}
|
||||
if (!author)
|
||||
die("Missing author: %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
if (*buffer)
|
||||
buffer++;
|
||||
insert_one_record(list, author, !*buffer ? "<none>" : buffer);
|
||||
}
|
||||
while ((commit = get_revision(rev)) != NULL)
|
||||
shortlog_add_commit(log, commit);
|
||||
}
|
||||
|
||||
static int parse_uint(char const **arg, int comma)
|
||||
@@ -212,29 +213,40 @@ static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
|
||||
die(wrap_arg_usage);
|
||||
}
|
||||
|
||||
void shortlog_init(struct shortlog *log)
|
||||
{
|
||||
memset(log, 0, sizeof(*log));
|
||||
|
||||
read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
|
||||
|
||||
log->list.strdup_paths = 1;
|
||||
log->wrap = DEFAULT_WRAPLEN;
|
||||
log->in1 = DEFAULT_INDENT1;
|
||||
log->in2 = DEFAULT_INDENT2;
|
||||
}
|
||||
|
||||
int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct shortlog log;
|
||||
struct rev_info rev;
|
||||
struct path_list list = { NULL, 0, 0, 1 };
|
||||
int i, j, sort_by_number = 0, summary = 0;
|
||||
int wrap_lines = 0;
|
||||
int wrap = DEFAULT_WRAPLEN;
|
||||
int in1 = DEFAULT_INDENT1;
|
||||
int in2 = DEFAULT_INDENT2;
|
||||
int nongit;
|
||||
|
||||
prefix = setup_git_directory_gently(&nongit);
|
||||
shortlog_init(&log);
|
||||
|
||||
/* since -n is a shadowed rev argument, parse our args first */
|
||||
while (argc > 1) {
|
||||
if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
|
||||
sort_by_number = 1;
|
||||
log.sort_by_number = 1;
|
||||
else if (!strcmp(argv[1], "-s") ||
|
||||
!strcmp(argv[1], "--summary"))
|
||||
summary = 1;
|
||||
log.summary = 1;
|
||||
else if (!strcmp(argv[1], "-e") ||
|
||||
!strcmp(argv[1], "--email"))
|
||||
email = 1;
|
||||
log.email = 1;
|
||||
else if (!prefixcmp(argv[1], "-w")) {
|
||||
wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &in1, &in2, &wrap);
|
||||
log.wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
|
||||
}
|
||||
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
|
||||
usage(shortlog_usage);
|
||||
@@ -248,34 +260,38 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
if (argc > 1)
|
||||
die ("unrecognized argument: %s", argv[1]);
|
||||
|
||||
read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
|
||||
|
||||
/* assume HEAD if from a tty */
|
||||
if (!rev.pending.nr && isatty(0))
|
||||
if (!nongit && !rev.pending.nr && isatty(0))
|
||||
add_head_to_pending(&rev);
|
||||
if (rev.pending.nr == 0) {
|
||||
read_from_stdin(&list);
|
||||
read_from_stdin(&log);
|
||||
}
|
||||
else
|
||||
get_from_rev(&rev, &list);
|
||||
get_from_rev(&rev, &log);
|
||||
|
||||
if (sort_by_number)
|
||||
qsort(list.items, list.nr, sizeof(struct path_list_item),
|
||||
shortlog_output(&log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shortlog_output(struct shortlog *log)
|
||||
{
|
||||
int i, j;
|
||||
if (log->sort_by_number)
|
||||
qsort(log->list.items, log->list.nr, sizeof(struct path_list_item),
|
||||
compare_by_number);
|
||||
for (i = 0; i < log->list.nr; i++) {
|
||||
struct path_list *onelines = log->list.items[i].util;
|
||||
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
struct path_list *onelines = list.items[i].util;
|
||||
|
||||
if (summary) {
|
||||
printf("%6d\t%s\n", onelines->nr, list.items[i].path);
|
||||
if (log->summary) {
|
||||
printf("%6d\t%s\n", onelines->nr, log->list.items[i].path);
|
||||
} else {
|
||||
printf("%s (%d):\n", list.items[i].path, onelines->nr);
|
||||
printf("%s (%d):\n", log->list.items[i].path, onelines->nr);
|
||||
for (j = onelines->nr - 1; j >= 0; j--) {
|
||||
const char *msg = onelines->items[j].path;
|
||||
|
||||
if (wrap_lines) {
|
||||
int col = print_wrapped_text(msg, in1, in2, wrap);
|
||||
if (col != wrap)
|
||||
if (log->wrap_lines) {
|
||||
int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
|
||||
if (col != log->wrap)
|
||||
putchar('\n');
|
||||
}
|
||||
else
|
||||
@@ -287,13 +303,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
onelines->strdup_paths = 1;
|
||||
path_list_clear(onelines, 1);
|
||||
free(onelines);
|
||||
list.items[i].util = NULL;
|
||||
log->list.items[i].util = NULL;
|
||||
}
|
||||
|
||||
list.strdup_paths = 1;
|
||||
path_list_clear(&list, 1);
|
||||
mailmap.strdup_paths = 1;
|
||||
path_list_clear(&mailmap, 1);
|
||||
|
||||
return 0;
|
||||
log->list.strdup_paths = 1;
|
||||
path_list_clear(&log->list, 1);
|
||||
log->mailmap.strdup_paths = 1;
|
||||
path_list_clear(&log->mailmap, 1);
|
||||
}
|
||||
|
||||
@@ -50,12 +50,15 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
|
||||
size_t len = strlen(editor);
|
||||
int i = 0;
|
||||
const char *args[6];
|
||||
struct strbuf arg0;
|
||||
|
||||
strbuf_init(&arg0, 0);
|
||||
if (strcspn(editor, "$ \t'") != len) {
|
||||
/* there are specials */
|
||||
strbuf_addf(&arg0, "%s \"$@\"", editor);
|
||||
args[i++] = "sh";
|
||||
args[i++] = "-c";
|
||||
args[i++] = "$0 \"$@\"";
|
||||
args[i++] = arg0.buf;
|
||||
}
|
||||
args[i++] = editor;
|
||||
args[i++] = path;
|
||||
@@ -63,6 +66,7 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
|
||||
|
||||
if (run_command_v_opt_cd_env(args, 0, NULL, env))
|
||||
die("There was a problem with the editor %s.", editor);
|
||||
strbuf_release(&arg0);
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
@@ -226,12 +230,13 @@ static int do_sign(struct strbuf *buffer)
|
||||
|
||||
if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
|
||||
close(gpg.in);
|
||||
close(gpg.out);
|
||||
finish_command(&gpg);
|
||||
return error("gpg did not accept the tag data");
|
||||
}
|
||||
close(gpg.in);
|
||||
gpg.close_in = 0;
|
||||
len = strbuf_read(buffer, gpg.out, 1024);
|
||||
close(gpg.out);
|
||||
|
||||
if (finish_command(&gpg) || !len || len < 0)
|
||||
return error("gpg failed to sign the tag");
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "tag.h"
|
||||
#include "tree.h"
|
||||
#include "progress.h"
|
||||
#include "decorate.h"
|
||||
|
||||
static int dry_run, quiet, recover, has_errors;
|
||||
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
|
||||
@@ -18,6 +19,18 @@ static unsigned int offset, len;
|
||||
static off_t consumed_bytes;
|
||||
static SHA_CTX ctx;
|
||||
|
||||
struct obj_buffer {
|
||||
char *buffer;
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
static struct decoration obj_decorate;
|
||||
|
||||
static struct obj_buffer *lookup_object_buffer(struct object *base)
|
||||
{
|
||||
return lookup_decoration(&obj_decorate, base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure at least "min" bytes are available in the buffer, and
|
||||
* return the pointer to the buffer.
|
||||
@@ -189,6 +202,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
||||
void *delta_data, *base;
|
||||
unsigned long base_size;
|
||||
unsigned char base_sha1[20];
|
||||
struct object *obj;
|
||||
|
||||
if (type == OBJ_REF_DELTA) {
|
||||
hashcpy(base_sha1, fill(20));
|
||||
@@ -252,6 +266,15 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
||||
}
|
||||
}
|
||||
|
||||
obj = lookup_object(base_sha1);
|
||||
if (obj) {
|
||||
struct obj_buffer *obj_buf = lookup_object_buffer(obj);
|
||||
if (obj_buf) {
|
||||
resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base = read_sha1_file(base_sha1, &type, &base_size);
|
||||
if (!base) {
|
||||
error("failed to read delta-pack base object %s",
|
||||
|
||||
@@ -40,8 +40,8 @@ static int verify_one_pack(const char *path, int verbose)
|
||||
if (!pack)
|
||||
return error("packfile %s not found.", arg);
|
||||
|
||||
install_packed_git(pack);
|
||||
err = verify_pack(pack, verbose);
|
||||
free(pack);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -45,14 +45,12 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
|
||||
memset(&gpg, 0, sizeof(gpg));
|
||||
gpg.argv = args_gpg;
|
||||
gpg.in = -1;
|
||||
gpg.out = 1;
|
||||
args_gpg[2] = path;
|
||||
if (start_command(&gpg))
|
||||
return error("could not run gpg.");
|
||||
|
||||
write_in_full(gpg.in, buf, len);
|
||||
close(gpg.in);
|
||||
gpg.close_in = 0;
|
||||
ret = finish_command(&gpg);
|
||||
|
||||
unlink(path);
|
||||
|
||||
@@ -18,6 +18,7 @@ extern int cmd_blame(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_branch(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_bundle(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_checkout(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
|
||||
@@ -56,6 +57,7 @@ extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mv(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
|
||||
@@ -65,6 +67,7 @@ extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_push(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_reflog(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_remote(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_config(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_rerere(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_reset(int argc, const char **argv, const char *prefix);
|
||||
|
||||
6
bundle.c
6
bundle.c
@@ -333,10 +333,12 @@ int create_bundle(struct bundle_header *header, const char *path,
|
||||
write_or_die(rls.in, sha1_to_hex(object->sha1), 40);
|
||||
write_or_die(rls.in, "\n", 1);
|
||||
}
|
||||
close(rls.in);
|
||||
if (finish_command(&rls))
|
||||
return error ("pack-objects died");
|
||||
|
||||
return bundle_to_stdout ? close(bundle_fd) : commit_lock_file(&lock);
|
||||
if (!bundle_to_stdout)
|
||||
commit_lock_file(&lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unbundle(struct bundle_header *header, int bundle_fd)
|
||||
|
||||
71
cache.h
71
cache.h
@@ -110,7 +110,6 @@ struct ondisk_cache_entry {
|
||||
};
|
||||
|
||||
struct cache_entry {
|
||||
struct cache_entry *next;
|
||||
unsigned int ce_ctime;
|
||||
unsigned int ce_mtime;
|
||||
unsigned int ce_dev;
|
||||
@@ -121,6 +120,7 @@ struct cache_entry {
|
||||
unsigned int ce_size;
|
||||
unsigned int ce_flags;
|
||||
unsigned char sha1[20];
|
||||
struct cache_entry *next;
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
|
||||
@@ -133,7 +133,39 @@ struct cache_entry {
|
||||
#define CE_UPDATE (0x10000)
|
||||
#define CE_REMOVE (0x20000)
|
||||
#define CE_UPTODATE (0x40000)
|
||||
#define CE_UNHASHED (0x80000)
|
||||
|
||||
#define CE_HASHED (0x100000)
|
||||
#define CE_UNHASHED (0x200000)
|
||||
|
||||
/*
|
||||
* Copy the sha1 and stat state of a cache entry from one to
|
||||
* another. But we never change the name, or the hash state!
|
||||
*/
|
||||
#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
|
||||
static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
|
||||
{
|
||||
unsigned int state = dst->ce_flags & CE_STATE_MASK;
|
||||
|
||||
/* Don't copy hash chain and name */
|
||||
memcpy(dst, src, offsetof(struct cache_entry, next));
|
||||
|
||||
/* Restore the hash state */
|
||||
dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't actually *remove* it, we can just mark it invalid so that
|
||||
* we won't find it in lookups.
|
||||
*
|
||||
* Not only would we have to search the lists (simple enough), but
|
||||
* we'd also have to rehash other hash buckets in case this makes the
|
||||
* hash bucket empty (common). So it's much better to just mark
|
||||
* it.
|
||||
*/
|
||||
static inline void remove_index_entry(struct cache_entry *ce)
|
||||
{
|
||||
ce->ce_flags |= CE_UNHASHED;
|
||||
}
|
||||
|
||||
static inline unsigned create_ce_flags(size_t len, unsigned stage)
|
||||
{
|
||||
@@ -220,6 +252,7 @@ extern struct index_state the_index;
|
||||
#define read_cache_from(path) read_index_from(&the_index, (path))
|
||||
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
|
||||
#define discard_cache() discard_index(&the_index)
|
||||
#define unmerged_cache() unmerged_index(&the_index)
|
||||
#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
|
||||
#define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
|
||||
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
|
||||
@@ -241,6 +274,7 @@ enum object_type {
|
||||
/* 5 for future expansion */
|
||||
OBJ_OFS_DELTA = 6,
|
||||
OBJ_REF_DELTA = 7,
|
||||
OBJ_ANY,
|
||||
OBJ_MAX,
|
||||
};
|
||||
|
||||
@@ -312,11 +346,12 @@ extern void verify_non_filename(const char *prefix, const char *name);
|
||||
/* Initialize and use the cache information */
|
||||
extern int read_index(struct index_state *);
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int write_index(struct index_state *, int newfd);
|
||||
extern int write_index(const struct index_state *, int newfd);
|
||||
extern int discard_index(struct index_state *);
|
||||
extern int unmerged_index(const struct index_state *);
|
||||
extern int verify_path(const char *path);
|
||||
extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
|
||||
extern int index_name_pos(struct index_state *, const char *name, int namelen);
|
||||
extern int index_name_pos(const struct index_state *, const char *name, int namelen);
|
||||
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
|
||||
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
|
||||
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
|
||||
@@ -333,8 +368,8 @@ extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
|
||||
#define CE_MATCH_IGNORE_VALID 01
|
||||
/* do not check the contents but report dirty on racily-clean entries */
|
||||
#define CE_MATCH_RACY_IS_DIRTY 02
|
||||
extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
|
||||
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
|
||||
extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
|
||||
extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
|
||||
|
||||
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
||||
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
|
||||
@@ -391,6 +426,15 @@ enum safe_crlf {
|
||||
|
||||
extern enum safe_crlf safe_crlf;
|
||||
|
||||
enum branch_track {
|
||||
BRANCH_TRACK_NEVER = 0,
|
||||
BRANCH_TRACK_REMOTE,
|
||||
BRANCH_TRACK_ALWAYS,
|
||||
BRANCH_TRACK_EXPLICIT,
|
||||
};
|
||||
|
||||
extern enum branch_track git_branch_track;
|
||||
|
||||
#define GIT_REPO_VERSION 0
|
||||
extern int repository_format_version;
|
||||
extern int check_repository_format(void);
|
||||
@@ -441,11 +485,7 @@ int safe_create_leading_directories(char *path);
|
||||
char *enter_repo(char *path, int strict);
|
||||
static inline int is_absolute_path(const char *path)
|
||||
{
|
||||
#ifndef __MINGW32__
|
||||
return path[0] == '/';
|
||||
#else
|
||||
return path[0] == '/' || (path[0] && path[1] == ':');
|
||||
#endif
|
||||
return path[0] == '/' || has_dos_drive_prefix(path);
|
||||
}
|
||||
|
||||
static inline int is_dev_null(const char *str)
|
||||
@@ -507,6 +547,7 @@ extern int create_symref(const char *ref, const char *refs_heads_master, const c
|
||||
extern int validate_headref(const char *ref);
|
||||
|
||||
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
|
||||
extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
|
||||
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
|
||||
|
||||
extern void *read_object_with_reference(const unsigned char *sha1,
|
||||
@@ -514,6 +555,9 @@ extern void *read_object_with_reference(const unsigned char *sha1,
|
||||
unsigned long *size,
|
||||
unsigned char *sha1_ret);
|
||||
|
||||
extern struct object *peel_to_type(const char *name, int namelen,
|
||||
struct object *o, enum object_type);
|
||||
|
||||
enum date_mode {
|
||||
DATE_NORMAL = 0,
|
||||
DATE_RELATIVE,
|
||||
@@ -681,6 +725,7 @@ extern const char *git_log_output_encoding;
|
||||
/* IO helper functions */
|
||||
extern void maybe_flush_or_die(FILE *, const char *);
|
||||
extern int copy_fd(int ifd, int ofd);
|
||||
extern int copy_file(const char *dst, const char *src, int mode);
|
||||
extern int read_in_full(int fd, void *buf, size_t count);
|
||||
extern int write_in_full(int fd, const void *buf, size_t count);
|
||||
extern void write_or_die(int fd, const void *buf, size_t count);
|
||||
@@ -734,6 +779,7 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i
|
||||
#define WS_TRAILING_SPACE 01
|
||||
#define WS_SPACE_BEFORE_TAB 02
|
||||
#define WS_INDENT_WITH_NON_TAB 04
|
||||
#define WS_CR_AT_EOL 010
|
||||
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
|
||||
extern unsigned whitespace_rule_cfg;
|
||||
extern unsigned whitespace_rule(const char *);
|
||||
@@ -742,10 +788,13 @@ extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
|
||||
FILE *stream, const char *set,
|
||||
const char *reset, const char *ws);
|
||||
extern char *whitespace_error_string(unsigned ws);
|
||||
extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
|
||||
|
||||
/* ls-files */
|
||||
int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
|
||||
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
|
||||
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
|
||||
13
commit.c
13
commit.c
@@ -193,7 +193,7 @@ static void prepare_commit_graft(void)
|
||||
commit_graft_prepared = 1;
|
||||
}
|
||||
|
||||
static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
|
||||
struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
|
||||
{
|
||||
int pos;
|
||||
prepare_commit_graft();
|
||||
@@ -290,17 +290,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
|
||||
}
|
||||
item->date = parse_commit_date(bufptr, tail);
|
||||
|
||||
if (track_object_refs) {
|
||||
unsigned i = 0;
|
||||
struct commit_list *p;
|
||||
struct object_refs *refs = alloc_object_refs(n_refs);
|
||||
if (item->tree)
|
||||
refs->ref[i++] = &item->tree->object;
|
||||
for (p = item->parents; p; p = p->next)
|
||||
refs->ref[i++] = &p->item->object;
|
||||
set_object_refs(&item->object, refs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
18
commit.h
18
commit.h
@@ -70,7 +70,22 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
|
||||
struct strbuf *,
|
||||
int abbrev, const char *subject,
|
||||
const char *after_subject, enum date_mode,
|
||||
int non_ascii_present);
|
||||
int need_8bit_cte);
|
||||
void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding);
|
||||
void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int need_8bit_cte);
|
||||
void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent);
|
||||
|
||||
|
||||
/** Removes the first commit from a list sorted by date, and adds all
|
||||
* of its parents.
|
||||
@@ -101,6 +116,7 @@ struct commit_graft {
|
||||
struct commit_graft *read_graft_line(char *buf, int len);
|
||||
int register_commit_graft(struct commit_graft *, int);
|
||||
int read_graft_file(const char *graft_file);
|
||||
struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
|
||||
|
||||
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
|
||||
|
||||
|
||||
0
compat/fnmatch.c
Executable file → Normal file
0
compat/fnmatch.c
Executable file → Normal file
@@ -330,16 +330,13 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
|
||||
#undef getcwd
|
||||
char *mingw_getcwd(char *pointer, int len)
|
||||
{
|
||||
int i;
|
||||
char *ret = getcwd(pointer, len);
|
||||
if (!ret)
|
||||
return ret;
|
||||
if (pointer[0] != 0 && pointer[1] == ':') {
|
||||
int i;
|
||||
for (i = 2; pointer[i]; i++)
|
||||
/* Thanks, Bill. You'll burn in hell for that. */
|
||||
if (pointer[i] == '\\')
|
||||
pointer[i] = '/';
|
||||
}
|
||||
for (i = 0; pointer[i]; i++)
|
||||
if (pointer[i] == '\\')
|
||||
pointer[i] = '/';
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -850,40 +847,6 @@ int mingw_rename(const char *pold, const char *pnew)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef vsnprintf
|
||||
/* Note that the size parameter specifies the available space, i.e.
|
||||
* includes the trailing NUL byte; but Windows's vsnprintf expects the
|
||||
* number of characters to write without the trailing NUL.
|
||||
*/
|
||||
|
||||
/* This is out of line because it uses alloca() behind the scenes,
|
||||
* which must not be called in a loop (alloca() reclaims the allocations
|
||||
* only at function exit).
|
||||
*/
|
||||
static int try_vsnprintf(size_t size, const char *fmt, va_list args)
|
||||
{
|
||||
char buf[size]; /* gcc-ism */
|
||||
return vsnprintf(buf, size-1, fmt, args);
|
||||
}
|
||||
|
||||
int mingw_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||
{
|
||||
int len;
|
||||
if (size > 0) {
|
||||
len = vsnprintf(buf, size-1, fmt, args);
|
||||
if (len >= 0)
|
||||
return len;
|
||||
}
|
||||
/* ouch, buffer too small; need to compute the size */
|
||||
if (size < 250)
|
||||
size = 250;
|
||||
do {
|
||||
size *= 4;
|
||||
len = try_vsnprintf(size, fmt, args);
|
||||
} while (len < 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
struct passwd *getpwuid(int uid)
|
||||
{
|
||||
static char user_name[100];
|
||||
|
||||
203
compat/mingw.h
Normal file
203
compat/mingw.h
Normal file
@@ -0,0 +1,203 @@
|
||||
#include <winsock2.h>
|
||||
|
||||
/*
|
||||
* things that are not available in header files
|
||||
*/
|
||||
|
||||
typedef int pid_t;
|
||||
#define hstrerror strerror
|
||||
|
||||
#define S_IFLNK 0120000 /* Symbolic link */
|
||||
#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
|
||||
#define S_ISSOCK(x) 0
|
||||
#define S_IRGRP 0
|
||||
#define S_IWGRP 0
|
||||
#define S_IXGRP 0
|
||||
#define S_ISGID 0
|
||||
#define S_IROTH 0
|
||||
#define S_IXOTH 0
|
||||
|
||||
#define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */
|
||||
#define WEXITSTATUS(x) ((x) & 0xff)
|
||||
#define WIFSIGNALED(x) ((unsigned)(x) > 259)
|
||||
|
||||
#define SIGKILL 0
|
||||
#define SIGCHLD 0
|
||||
#define SIGPIPE 0
|
||||
#define SIGALRM 100
|
||||
|
||||
#define F_GETFD 1
|
||||
#define F_SETFD 2
|
||||
#define FD_CLOEXEC 0x1
|
||||
|
||||
struct passwd {
|
||||
char *pw_name;
|
||||
char *pw_gecos;
|
||||
char *pw_dir;
|
||||
};
|
||||
|
||||
struct pollfd {
|
||||
int fd; /* file descriptor */
|
||||
short events; /* requested events */
|
||||
short revents; /* returned events */
|
||||
};
|
||||
#define POLLIN 1
|
||||
#define POLLHUP 2
|
||||
|
||||
typedef void (__cdecl *sig_handler_t)(int);
|
||||
struct sigaction {
|
||||
sig_handler_t sa_handler;
|
||||
unsigned sa_flags;
|
||||
};
|
||||
#define sigemptyset(x) (void)0
|
||||
#define SA_RESTART 0
|
||||
|
||||
struct itimerval {
|
||||
struct timeval it_value, it_interval;
|
||||
};
|
||||
#define ITIMER_REAL 0
|
||||
|
||||
/*
|
||||
* trivial stubs
|
||||
*/
|
||||
|
||||
static inline int readlink(const char *path, char *buf, size_t bufsiz)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int symlink(const char *oldpath, const char *newpath)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int link(const char *oldpath, const char *newpath)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int fchmod(int fildes, mode_t mode)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int fork(void)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline unsigned int alarm(unsigned int seconds)
|
||||
{ return 0; }
|
||||
static inline int fsync(int fd)
|
||||
{ return 0; }
|
||||
static inline int getppid(void)
|
||||
{ return 1; }
|
||||
static inline void sync(void)
|
||||
{}
|
||||
static inline int getuid()
|
||||
{ return 1; }
|
||||
static inline struct passwd *getpwnam(const char *name)
|
||||
{ return NULL; }
|
||||
static inline int fcntl(int fd, int cmd, long arg)
|
||||
{
|
||||
if (cmd == F_GETFD || cmd == F_SETFD)
|
||||
return 0;
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* simple adaptors
|
||||
*/
|
||||
|
||||
static inline int mingw_mkdir(const char *path, int mode)
|
||||
{
|
||||
return mkdir(path);
|
||||
}
|
||||
#define mkdir mingw_mkdir
|
||||
|
||||
static inline int mingw_unlink(const char *pathname)
|
||||
{
|
||||
/* read-only files cannot be removed */
|
||||
chmod(pathname, 0666);
|
||||
return unlink(pathname);
|
||||
}
|
||||
#define unlink mingw_unlink
|
||||
|
||||
static inline int waitpid(pid_t pid, unsigned *status, unsigned options)
|
||||
{
|
||||
if (options == 0)
|
||||
return _cwait(status, pid, 0);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* implementations of missing functions
|
||||
*/
|
||||
|
||||
int pipe(int filedes[2]);
|
||||
unsigned int sleep (unsigned int seconds);
|
||||
int mkstemp(char *template);
|
||||
int gettimeofday(struct timeval *tv, void *tz);
|
||||
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
|
||||
struct tm *gmtime_r(const time_t *timep, struct tm *result);
|
||||
struct tm *localtime_r(const time_t *timep, struct tm *result);
|
||||
int getpagesize(void); /* defined in MinGW's libgcc.a */
|
||||
struct passwd *getpwuid(int uid);
|
||||
int setitimer(int type, struct itimerval *in, struct itimerval *out);
|
||||
int sigaction(int sig, struct sigaction *in, struct sigaction *out);
|
||||
|
||||
/*
|
||||
* replacements of existing functions
|
||||
*/
|
||||
|
||||
int mingw_open (const char *filename, int oflags, ...);
|
||||
#define open mingw_open
|
||||
|
||||
char *mingw_getcwd(char *pointer, int len);
|
||||
#define getcwd mingw_getcwd
|
||||
|
||||
struct hostent *mingw_gethostbyname(const char *host);
|
||||
#define gethostbyname mingw_gethostbyname
|
||||
|
||||
int mingw_socket(int domain, int type, int protocol);
|
||||
#define socket mingw_socket
|
||||
|
||||
int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
|
||||
#define connect mingw_connect
|
||||
|
||||
int mingw_rename(const char*, const char*);
|
||||
#define rename mingw_rename
|
||||
|
||||
/* Use mingw_lstat() instead of lstat()/stat() and
|
||||
* mingw_fstat() instead of fstat() on Windows.
|
||||
* struct stat is redefined because it lacks the st_blocks member.
|
||||
*/
|
||||
struct mingw_stat {
|
||||
unsigned st_mode;
|
||||
time_t st_mtime, st_atime, st_ctime;
|
||||
unsigned st_dev, st_ino, st_uid, st_gid;
|
||||
size_t st_size;
|
||||
size_t st_blocks;
|
||||
};
|
||||
int mingw_lstat(const char *file_name, struct mingw_stat *buf);
|
||||
int mingw_fstat(int fd, struct mingw_stat *buf);
|
||||
#define fstat mingw_fstat
|
||||
#define lstat mingw_lstat
|
||||
#define stat mingw_stat
|
||||
static inline int mingw_stat(const char *file_name, struct mingw_stat *buf)
|
||||
{ return mingw_lstat(file_name, buf); }
|
||||
|
||||
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env);
|
||||
void mingw_execvp(const char *cmd, char *const *argv);
|
||||
#define execvp mingw_execvp
|
||||
|
||||
static inline unsigned int git_ntohl(unsigned int x)
|
||||
{ return (unsigned int)ntohl(x); }
|
||||
#define ntohl git_ntohl
|
||||
|
||||
sig_handler_t mingw_signal(int sig, sig_handler_t handler);
|
||||
#define signal mingw_signal
|
||||
|
||||
/*
|
||||
* git specific compatibility
|
||||
*/
|
||||
|
||||
#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
|
||||
#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
|
||||
#define PATH_SEP ';'
|
||||
#define PRIuMAX "I64u"
|
||||
|
||||
/*
|
||||
* helpers
|
||||
*/
|
||||
|
||||
char **copy_environ(void);
|
||||
void free_environ(char **env);
|
||||
char **env_setenv(char **env, const char *name);
|
||||
49
compat/snprintf.c
Normal file
49
compat/snprintf.c
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "../git-compat-util.h"
|
||||
|
||||
/*
|
||||
* The size parameter specifies the available space, i.e. includes
|
||||
* the trailing NUL byte; but Windows's vsnprintf expects the
|
||||
* number of characters to write without the trailing NUL.
|
||||
*/
|
||||
#ifndef SNPRINTF_SIZE_CORR
|
||||
#define SNPRINTF_SIZE_CORR 0
|
||||
#endif
|
||||
|
||||
#undef vsnprintf
|
||||
int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
|
||||
{
|
||||
char *s;
|
||||
int ret;
|
||||
|
||||
ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
|
||||
if (ret != -1)
|
||||
return ret;
|
||||
|
||||
s = NULL;
|
||||
if (maxsize < 128)
|
||||
maxsize = 128;
|
||||
|
||||
while (ret == -1) {
|
||||
maxsize *= 4;
|
||||
str = realloc(s, maxsize);
|
||||
if (! str)
|
||||
break;
|
||||
s = str;
|
||||
ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
|
||||
}
|
||||
free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int git_snprintf(char *str, size_t maxsize, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
ret = git_vsnprintf(str, maxsize, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
8
config.c
8
config.c
@@ -471,6 +471,14 @@ int git_default_config(const char *var, const char *value)
|
||||
whitespace_rule_cfg = parse_whitespace_rule(value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "branch.autosetupmerge")) {
|
||||
if (value && !strcasecmp(value, "always")) {
|
||||
git_branch_track = BRANCH_TRACK_ALWAYS;
|
||||
return 0;
|
||||
}
|
||||
git_branch_track = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user