mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -35,6 +35,7 @@ git-diff-files
|
||||
git-diff-index
|
||||
git-diff-tree
|
||||
git-describe
|
||||
git-fast-export
|
||||
git-fast-import
|
||||
git-fetch
|
||||
git-fetch--tool
|
||||
@@ -109,7 +110,6 @@ git-rev-list
|
||||
git-rev-parse
|
||||
git-revert
|
||||
git-rm
|
||||
git-runstatus
|
||||
git-send-email
|
||||
git-send-pack
|
||||
git-sh-setup
|
||||
|
||||
@@ -79,16 +79,16 @@ man7: $(DOC_MAN7)
|
||||
info: git.info
|
||||
|
||||
install: man
|
||||
$(INSTALL) -d -m755 $(DESTDIR)$(man1dir)
|
||||
$(INSTALL) -d -m755 $(DESTDIR)$(man5dir)
|
||||
$(INSTALL) -d -m755 $(DESTDIR)$(man7dir)
|
||||
$(INSTALL) -m644 $(DOC_MAN1) $(DESTDIR)$(man1dir)
|
||||
$(INSTALL) -m644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
|
||||
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(man5dir)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(man7dir)
|
||||
$(INSTALL) -m 644 $(DOC_MAN1) $(DESTDIR)$(man1dir)
|
||||
$(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
|
||||
$(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
|
||||
|
||||
install-info: info
|
||||
$(INSTALL) -d -m755 $(DESTDIR)$(infodir)
|
||||
$(INSTALL) -m644 git.info $(DESTDIR)$(infodir)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(infodir)
|
||||
$(INSTALL) -m 644 git.info $(DESTDIR)$(infodir)
|
||||
if test -r $(DESTDIR)$(infodir)/dir; then \
|
||||
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\
|
||||
else \
|
||||
@@ -122,9 +122,9 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
|
||||
|
||||
$(cmds_txt): cmd-list.made
|
||||
|
||||
cmd-list.made: cmd-list.perl $(MAN1_TXT)
|
||||
cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
|
||||
$(RM) $@
|
||||
perl ./cmd-list.perl
|
||||
perl ./cmd-list.perl ../command-list.txt
|
||||
date >$@
|
||||
|
||||
git.7 git.html: git.txt
|
||||
|
||||
45
Documentation/RelNotes-1.5.3.7.txt
Normal file
45
Documentation/RelNotes-1.5.3.7.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
GIT v1.5.3.7 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.3.6
|
||||
--------------------
|
||||
|
||||
* git-send-email added 8-bit contents to the payload without
|
||||
marking it as 8-bit in a CTE header.
|
||||
|
||||
* "git-bundle create a.bndl HEAD" dereferenced the symref and
|
||||
did not record the ref as 'HEAD'; this prevented a bundle
|
||||
from being used as a normal source of git-clone.
|
||||
|
||||
* The code to reject nonsense command line of the form
|
||||
"git-commit -a paths..." and "git-commit --interactive
|
||||
paths..." were broken.
|
||||
|
||||
* Adding a signature that is not ASCII-only to an original
|
||||
commit that is ASCII-only would make the result non-ASCII.
|
||||
"git-format-patch -s" did not mark such a message correctly
|
||||
with MIME encoding header.
|
||||
|
||||
* git-add sometimes did not mark the resulting index entry
|
||||
stat-clean. This affected only cases when adding the
|
||||
contents with the same length as the previously staged
|
||||
contents, and the previous staging made the index entry
|
||||
"racily clean".
|
||||
|
||||
* git-commit did not honor GIT_INDEX_FILE the user had in the
|
||||
environment.
|
||||
|
||||
* When checking out a revision, git-checkout did not report where the
|
||||
updated HEAD is if you happened to have a file called HEAD in the
|
||||
work tree.
|
||||
|
||||
* "git-rev-list --objects" mishandled a tree that points at a
|
||||
submodule.
|
||||
|
||||
* "git cvsimport" was not ready for packed refs that "git gc" can
|
||||
produce and gave incorrect results.
|
||||
|
||||
* Many scripted Porcelains were confused when you happened to have a
|
||||
file called "HEAD" in your work tree.
|
||||
|
||||
Also it contains updates to the user manual and documentation.
|
||||
@@ -1,129 +1,232 @@
|
||||
GIT v1.5.4 Release Notes
|
||||
========================
|
||||
|
||||
Removal
|
||||
-------
|
||||
|
||||
* "git svnimport" was removed in favor of "git svn". It is still there
|
||||
in the source tree (contrib/examples) but unsupported.
|
||||
|
||||
|
||||
Deprecation notices
|
||||
-------------------
|
||||
|
||||
* Next feature release of git (this change is scheduled for v1.5.5 but
|
||||
it could slip) will by default install dashed form of commands
|
||||
(e.g. "git-commit") outside of users' normal $PATH, and will install
|
||||
only selected commands ("git" itself, and "gitk") in $PATH. This
|
||||
implies:
|
||||
|
||||
- Using dashed form of git commands (e.g. "git-commit") from the
|
||||
command line has been informally deprecated since early 2006, but
|
||||
now it officially is, and will be removed in the future. Use
|
||||
dashless form (e.g. "git commit") instead.
|
||||
|
||||
- Using dashed from from your scripts, without first prepending the
|
||||
return value from "git --exec-path" to the scripts' PATH, has been
|
||||
informally deprecated since early 2006, but now it officially is.
|
||||
|
||||
- Use of dashed form with "PATH=$(git --exec-path):$PATH; export
|
||||
PATH" early in your script is not deprecated with this change.
|
||||
|
||||
Users are strongly encouraged to adjust their habits and scripts now
|
||||
to prepare for this.
|
||||
|
||||
* The post-receive hook was introduced in March 2007 to supersede
|
||||
post-update hook, primarily to overcome the command line length
|
||||
limitation of the latter. Use of post-update hook will be deprecated
|
||||
in future versions of git, perhaps in v1.5.5.
|
||||
|
||||
* "git lost-found" was deprecated in favor of "git fsck"'s --lost-found
|
||||
option, and will be removed in the future.
|
||||
|
||||
* "git peek-remote" is deprecated, as "git ls-remote" was written in C
|
||||
and works for all transports, and will be removed in the future.
|
||||
|
||||
|
||||
Updates since v1.5.3
|
||||
--------------------
|
||||
|
||||
* Comes with much improved gitk.
|
||||
|
||||
* Comes with git-gui 0.9.0 with i18n.
|
||||
* Comes with "git gui" 0.9.1 with i18n.
|
||||
|
||||
* git-lost-found was deprecated in favor of git-fsck's --lost-found
|
||||
option.
|
||||
* gitk is now merged as a subdirectory of git.git project, in
|
||||
preparation for its i18n.
|
||||
|
||||
* git-peek-remote is deprecated, as git-ls-remote was written in C and
|
||||
works for all transports.
|
||||
* progress display from many commands are a lot nicer to the eye.
|
||||
Transfer commands show throughput data.
|
||||
|
||||
* "progress display" from many commands are a lot nicer to the
|
||||
eye. Transfer commands show throughput data.
|
||||
* many commands that pay attention to per-directory .gitignore now do
|
||||
so lazily, which makes the usual case go much faster.
|
||||
|
||||
* git-reset is now built-in and its output can be squelched with -q.
|
||||
* Output processing for '--pretty=format:<user format>' has been
|
||||
optimized.
|
||||
|
||||
* git-send-email can optionally talk over ssmtp and use SMTP-AUTH.
|
||||
* Rename detection of diff family, while detecting exact matches, has
|
||||
been greatly optimized.
|
||||
|
||||
* git-rebase learned --whitespace option.
|
||||
* Rename detection of diff family tries to make more naturally looking
|
||||
pairing. Earlier if more than one identical rename sources were
|
||||
found in the preimage, they were picked pretty much at random.
|
||||
|
||||
* In git-rebase, when you decide not to replay a particular change
|
||||
after the command stopped with a conflict, you can say "git-rebase
|
||||
--skip" without first running "git reset --hard", as the command now
|
||||
runs it for you.
|
||||
* Value "true" for color.diff and color.status configuration used to
|
||||
mean "always" (even when the output is not going to a terminal).
|
||||
This has been corrected to mean the same thing as "auto".
|
||||
|
||||
* git-remote knows --mirror mode.
|
||||
|
||||
* git-merge can call the "post-merge" hook.
|
||||
|
||||
* git-pack-objects can optionally run deltification with multiple threads.
|
||||
|
||||
* git-archive can optionally substitute keywords in files marked with
|
||||
export-subst attribute.
|
||||
|
||||
* git-for-each-ref learned %(xxxdate:<dateformat>) syntax to
|
||||
show the various date fields in different formats.
|
||||
|
||||
* git-gc --auto is a low-impact way to automatically run a
|
||||
variant of git-repack that does not lose unreferenced objects
|
||||
(read: safer than the usual one) after the user accumulates
|
||||
too many loose objects.
|
||||
|
||||
* You need to explicitly set clean.requireForce to "false" to allow
|
||||
git-clean without -f to do any damage (lack of the configuration
|
||||
variable used to mean "do not require", but we now use the safer
|
||||
default).
|
||||
|
||||
* git-clean has been rewritten in C.
|
||||
|
||||
* git-push has been rewritten in C.
|
||||
|
||||
* git-push learned --dry-run option to show what would happen
|
||||
if a push is run.
|
||||
|
||||
* git-push does not update a tracking ref on the pushing side when the
|
||||
remote refused to update the corresponding ref.
|
||||
|
||||
* git-push learned --mirror option. This is to push the local refs
|
||||
one-to-one to the remote, and deletes refs from the remote that do
|
||||
not exist anymore in the repository on the pushing side.
|
||||
|
||||
* git-remote learned "rm" subcommand.
|
||||
|
||||
* git-rebase --interactive mode can now work on detached HEAD.
|
||||
|
||||
* git-cvsserver can be run via git-shell.
|
||||
|
||||
* git-am and git-rebase are far less verbose.
|
||||
|
||||
* git-pull learned to pass --[no-]ff option to underlying git-merge.
|
||||
* HTTP proxy can be specified per remote repository using
|
||||
remote.*.httpproxy configuration, or global http.proxy configuration
|
||||
variable.
|
||||
|
||||
* Various Perforce importer updates.
|
||||
|
||||
* "git log" learned --early-output option to help interactive
|
||||
GUI implementations.
|
||||
|
||||
* git-svnimport was removed in favor of git-svn.
|
||||
|
||||
* git-bisect learned "skip" action to mark untestable commits.
|
||||
|
||||
* git-format-patch learned "format.numbered" configuration variable
|
||||
to automatically turn --numbered option on when more than one
|
||||
commits are formatted.
|
||||
|
||||
* git-ls-files learned "--exclude-standard" to use the canned
|
||||
set of exclude files.
|
||||
|
||||
* git-rebase now detaches head during its operation, so after a
|
||||
successful "git rebase" operation, the reflog entry branch@{1}
|
||||
for the current branch points at the commit before the rebase
|
||||
was started.
|
||||
|
||||
* "git-tag -a -f existing" begins the editor session using the
|
||||
existing annotation message.
|
||||
|
||||
* "git cvsexportcommit" learned -w option to specify and switch
|
||||
to the CVS working directory.
|
||||
|
||||
* "git checkout" from a subdirectory learned to use "../path"
|
||||
to allow checking out a path outside the current directory
|
||||
without cd'ing up.
|
||||
|
||||
* "git send-email --dry-run" shows full headers for easier
|
||||
diagnosis.
|
||||
|
||||
* "git merge-ours" is built-in.
|
||||
|
||||
* "git svn" learned "info" subcommand.
|
||||
|
||||
* "git status" from a subdirectory now shows relative paths
|
||||
which makes copy-and-pasting for git-checkout/git-add/git-rm
|
||||
easier.
|
||||
|
||||
* Output processing for '--pretty=format:<user format>' has
|
||||
been optimized.
|
||||
|
||||
* Rename detection diff family, while detecting exact matches,
|
||||
has been greatly optimized.
|
||||
|
||||
* Example update and post-receive hooks have been improved.
|
||||
|
||||
* Any command that wants to take a commit object name can now use
|
||||
":/string" syntax to name a commit.
|
||||
|
||||
* "git reset" is now built-in and its output can be squelched with -q.
|
||||
|
||||
* "git send-email" can optionally talk over ssmtp and use SMTP-AUTH.
|
||||
|
||||
* "git rebase" learned --whitespace option.
|
||||
|
||||
* In "git rebase", when you decide not to replay a particular change
|
||||
after the command stopped with a conflict, you can say "git rebase
|
||||
--skip" without first running "git reset --hard", as the command now
|
||||
runs it for you.
|
||||
|
||||
* "git rebase --interactive" mode can now work on detached HEAD.
|
||||
|
||||
* "git rebase" now detaches head during its operation, so after a
|
||||
successful "git rebase" operation, the reflog entry branch@{1} for
|
||||
the current branch points at the commit before the rebase was
|
||||
started.
|
||||
|
||||
* "git rebase -i" also triggers rerere to help your repeated merges.
|
||||
|
||||
* "git merge" can call the "post-merge" hook.
|
||||
|
||||
* "git pack-objects" can optionally run deltification with multiple
|
||||
threads.
|
||||
|
||||
* "git archive" can optionally substitute keywords in files marked with
|
||||
export-subst attribute.
|
||||
|
||||
* "git cherry-pick" made a misguided attempt to repeat the original
|
||||
command line in the generated log message, when told to cherry-pick a
|
||||
commit by naming a tag that points at it. It does not anymore.
|
||||
|
||||
* "git for-each-ref" learned %(xxxdate:<dateformat>) syntax to show the
|
||||
various date fields in different formats.
|
||||
|
||||
* "git gc --auto" is a low-impact way to automatically run a variant of
|
||||
"git repack" that does not lose unreferenced objects (read: safer
|
||||
than the usual one) after the user accumulates too many loose
|
||||
objects.
|
||||
|
||||
* "git clean" has been rewritten in C.
|
||||
|
||||
* You need to explicitly set clean.requireForce to "false" to allow
|
||||
"git clean" without -f to do any damage (lack of the configuration
|
||||
variable used to mean "do not require -f option to lose untracked
|
||||
files", but we now use the safer default).
|
||||
|
||||
* "git push" learned --dry-run option to show what would happen if a
|
||||
push is run.
|
||||
|
||||
* "git push" does not update a tracking ref on the local side when the
|
||||
remote refused to update the corresponding ref.
|
||||
|
||||
* "git push" learned --mirror option. This is to push the local refs
|
||||
one-to-one to the remote, and deletes refs from the remote that do
|
||||
not exist anymore in the repository on the pushing side.
|
||||
|
||||
* "git push" can remove a corrupt ref at the remote site with the usual
|
||||
":ref" refspec.
|
||||
|
||||
* "git remote" knows --mirror mode. This is to set up configuration to
|
||||
push into a remote repository to store local branch heads to the same
|
||||
branch on the remote side, and remove branch heads locally removed
|
||||
from local repository at the same time. Suitable for pushing into a
|
||||
back-up repository.
|
||||
|
||||
* "git remote" learned "rm" subcommand.
|
||||
|
||||
* "git cvsserver" can be run via "git shell".
|
||||
|
||||
* "git am" and "git rebase" are far less verbose.
|
||||
|
||||
* "git pull" learned to pass --[no-]ff option to underlying "git
|
||||
merge".
|
||||
|
||||
* "git pull --rebase" is a different way to integrate what you fetched
|
||||
into your current branch.
|
||||
|
||||
* "git fast-export" produces datastream that can be fed to fast-import
|
||||
to reproduce the history recorded in a git repository.
|
||||
|
||||
* "git commit --allow-empty" allows you to create a single-parent
|
||||
commit that records the same tree as its parent, overriding the usual
|
||||
safety valve.
|
||||
|
||||
* "git commit --amend" can amend a merge that does not change the tree
|
||||
from its first parent.
|
||||
|
||||
* "git stash random-text" does not create a new stash anymore. It was
|
||||
a UI mistake. Use "git stash save random-text", or "git stash"
|
||||
(without extra args) for that.
|
||||
|
||||
* "git prune --expire <time>" can exempt young loose objects from
|
||||
getting pruned.
|
||||
|
||||
* "git branch --contains <commit>" can list branches that are
|
||||
descendants of a given commit.
|
||||
|
||||
* "git log" learned --early-output option to help interactive GUI
|
||||
implementations.
|
||||
|
||||
* "git bisect" learned "skip" action to mark untestable commits.
|
||||
|
||||
* "git format-patch" learned "format.numbered" configuration variable
|
||||
to automatically turn --numbered option on when more than one commits
|
||||
are formatted.
|
||||
|
||||
* "git ls-files" learned "--exclude-standard" to use the canned set of
|
||||
exclude files.
|
||||
|
||||
* "git tag -a -f existing" begins the editor session using the existing
|
||||
annotation message.
|
||||
|
||||
* "git tag -m one -m bar" (multiple -m options) behaves similarly to
|
||||
"git commit"; the parameters to -m options are formatted as separate
|
||||
paragraphs.
|
||||
|
||||
* "git cvsexportcommit" learned -w option to specify and switch to the
|
||||
CVS working directory.
|
||||
|
||||
* "git checkout" from a subdirectory learned to use "../path" to allow
|
||||
checking out a path outside the current directory without cd'ing up.
|
||||
|
||||
* "git send-email --dry-run" shows full headers for easier diagnosis.
|
||||
|
||||
* "git merge-ours" is now built-in.
|
||||
|
||||
* "git svn" learned "info" and "show-externals" subcommands.
|
||||
|
||||
* "git svn" run from a subdirectory failed to read settings from the
|
||||
.git/config.
|
||||
|
||||
* "git svn" learned --use-log-author option, which picks up more
|
||||
descriptive name from From: and Signed-off-by: lines in the commit
|
||||
message.
|
||||
|
||||
* "git status" from a subdirectory now shows relative paths which makes
|
||||
copy-and-pasting for git-checkout/git-add/git-rm easier.
|
||||
|
||||
* "git checkout" from and to detached HEAD leaves a bit more
|
||||
information in the reflog.
|
||||
|
||||
* In addition there are quite a few internal clean-ups. Notably
|
||||
|
||||
- many fork/exec have been replaced with run-command API,
|
||||
@@ -140,15 +243,14 @@ Fixes since v1.5.3
|
||||
All of the fixes in v1.5.3 maintenance series are included in
|
||||
this release, unless otherwise noted.
|
||||
|
||||
* git-svn talking with the SVN over http will correctly quote branch
|
||||
and project names.
|
||||
These fixes are only in v1.5.4 and not backported to v1.5.3 maintenance
|
||||
series.
|
||||
|
||||
* "git rev-list --objects A..B" choked when the lower boundary
|
||||
of the range involved a subproject. This fix is also queued
|
||||
for 'maint' (but not in there yet).
|
||||
* "git svn" talking with the SVN over http will correctly quote branch
|
||||
and project names.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.3.6-950-gda03a58
|
||||
O=v1.5.3.7-1003-gf38ca7c
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
|
||||
@@ -28,8 +28,8 @@ sub format_one {
|
||||
}
|
||||
if (my ($verify_name, $text) = ($description =~ /^($name) - (.*)/)) {
|
||||
print $out "gitlink:$name\[1\]::\n\t";
|
||||
if ($attr) {
|
||||
print $out "($attr) ";
|
||||
if ($attr =~ / deprecated /) {
|
||||
print $out "(deprecated) ";
|
||||
}
|
||||
print $out "$text.\n\n";
|
||||
}
|
||||
@@ -39,12 +39,13 @@ sub format_one {
|
||||
}
|
||||
|
||||
my %cmds = ();
|
||||
while (<DATA>) {
|
||||
for (sort <>) {
|
||||
next if /^#/;
|
||||
|
||||
chomp;
|
||||
my ($name, $cat, $attr) = /^(\S+)\s+(.*?)(?:\s+(.*))?$/;
|
||||
push @{$cmds{$cat}}, [$name, $attr];
|
||||
$attr = '' unless defined $attr;
|
||||
push @{$cmds{$cat}}, [$name, " $attr "];
|
||||
}
|
||||
|
||||
for my $cat (qw(ancillaryinterrogators
|
||||
@@ -71,133 +72,3 @@ for my $cat (qw(ancillaryinterrogators
|
||||
rename "$out+", "$out";
|
||||
}
|
||||
}
|
||||
|
||||
# The following list is sorted with "sort -d" to make it easier
|
||||
# to find entry in the resulting git.html manual page.
|
||||
__DATA__
|
||||
git-add mainporcelain
|
||||
git-am mainporcelain
|
||||
git-annotate ancillaryinterrogators
|
||||
git-apply plumbingmanipulators
|
||||
git-archimport foreignscminterface
|
||||
git-archive mainporcelain
|
||||
git-bisect mainporcelain
|
||||
git-blame ancillaryinterrogators
|
||||
git-branch mainporcelain
|
||||
git-bundle mainporcelain
|
||||
git-cat-file plumbinginterrogators
|
||||
git-check-attr purehelpers
|
||||
git-checkout mainporcelain
|
||||
git-checkout-index plumbingmanipulators
|
||||
git-check-ref-format purehelpers
|
||||
git-cherry ancillaryinterrogators
|
||||
git-cherry-pick mainporcelain
|
||||
git-citool mainporcelain
|
||||
git-clean mainporcelain
|
||||
git-clone mainporcelain
|
||||
git-commit mainporcelain
|
||||
git-commit-tree plumbingmanipulators
|
||||
git-config ancillarymanipulators
|
||||
git-count-objects ancillaryinterrogators
|
||||
git-cvsexportcommit foreignscminterface
|
||||
git-cvsimport foreignscminterface
|
||||
git-cvsserver foreignscminterface
|
||||
git-daemon synchingrepositories
|
||||
git-describe mainporcelain
|
||||
git-diff mainporcelain
|
||||
git-diff-files plumbinginterrogators
|
||||
git-diff-index plumbinginterrogators
|
||||
git-diff-tree plumbinginterrogators
|
||||
git-fast-import ancillarymanipulators
|
||||
git-fetch mainporcelain
|
||||
git-fetch-pack synchingrepositories
|
||||
git-filter-branch ancillarymanipulators
|
||||
git-fmt-merge-msg purehelpers
|
||||
git-for-each-ref plumbinginterrogators
|
||||
git-format-patch mainporcelain
|
||||
git-fsck ancillaryinterrogators
|
||||
git-gc mainporcelain
|
||||
git-get-tar-commit-id ancillaryinterrogators
|
||||
git-grep mainporcelain
|
||||
git-gui mainporcelain
|
||||
git-hash-object plumbingmanipulators
|
||||
git-http-fetch synchelpers
|
||||
git-http-push synchelpers
|
||||
git-imap-send foreignscminterface
|
||||
git-index-pack plumbingmanipulators
|
||||
git-init mainporcelain
|
||||
git-instaweb ancillaryinterrogators
|
||||
gitk mainporcelain
|
||||
git-log mainporcelain
|
||||
git-lost-found ancillarymanipulators deprecated
|
||||
git-ls-files plumbinginterrogators
|
||||
git-ls-remote plumbinginterrogators
|
||||
git-ls-tree plumbinginterrogators
|
||||
git-mailinfo purehelpers
|
||||
git-mailsplit purehelpers
|
||||
git-merge mainporcelain
|
||||
git-merge-base plumbinginterrogators
|
||||
git-merge-file plumbingmanipulators
|
||||
git-merge-index plumbingmanipulators
|
||||
git-merge-one-file purehelpers
|
||||
git-mergetool ancillarymanipulators
|
||||
git-merge-tree ancillaryinterrogators
|
||||
git-mktag plumbingmanipulators
|
||||
git-mktree plumbingmanipulators
|
||||
git-mv mainporcelain
|
||||
git-name-rev plumbinginterrogators
|
||||
git-pack-objects plumbingmanipulators
|
||||
git-pack-redundant plumbinginterrogators
|
||||
git-pack-refs ancillarymanipulators
|
||||
git-parse-remote synchelpers
|
||||
git-patch-id purehelpers
|
||||
git-peek-remote purehelpers deprecated
|
||||
git-prune ancillarymanipulators
|
||||
git-prune-packed plumbingmanipulators
|
||||
git-pull mainporcelain
|
||||
git-push mainporcelain
|
||||
git-quiltimport foreignscminterface
|
||||
git-read-tree plumbingmanipulators
|
||||
git-rebase mainporcelain
|
||||
git-receive-pack synchelpers
|
||||
git-reflog ancillarymanipulators
|
||||
git-relink ancillarymanipulators
|
||||
git-remote ancillarymanipulators
|
||||
git-repack ancillarymanipulators
|
||||
git-request-pull foreignscminterface
|
||||
git-rerere ancillaryinterrogators
|
||||
git-reset mainporcelain
|
||||
git-revert mainporcelain
|
||||
git-rev-list plumbinginterrogators
|
||||
git-rev-parse ancillaryinterrogators
|
||||
git-rm mainporcelain
|
||||
git-runstatus ancillaryinterrogators
|
||||
git-send-email foreignscminterface
|
||||
git-send-pack synchingrepositories
|
||||
git-shell synchelpers
|
||||
git-shortlog mainporcelain
|
||||
git-show mainporcelain
|
||||
git-show-branch ancillaryinterrogators
|
||||
git-show-index plumbinginterrogators
|
||||
git-show-ref plumbinginterrogators
|
||||
git-sh-setup purehelpers
|
||||
git-stash mainporcelain
|
||||
git-status mainporcelain
|
||||
git-stripspace purehelpers
|
||||
git-submodule mainporcelain
|
||||
git-svn foreignscminterface
|
||||
git-symbolic-ref plumbingmanipulators
|
||||
git-tag mainporcelain
|
||||
git-tar-tree plumbinginterrogators deprecated
|
||||
git-unpack-file plumbinginterrogators
|
||||
git-unpack-objects plumbingmanipulators
|
||||
git-update-index plumbingmanipulators
|
||||
git-update-ref plumbingmanipulators
|
||||
git-update-server-info synchingrepositories
|
||||
git-upload-archive synchelpers
|
||||
git-upload-pack synchelpers
|
||||
git-var plumbinginterrogators
|
||||
git-verify-pack plumbinginterrogators
|
||||
git-verify-tag ancillaryinterrogators
|
||||
git-whatchanged ancillaryinterrogators
|
||||
git-write-tree plumbingmanipulators
|
||||
|
||||
@@ -346,6 +346,13 @@ branch.<name>.mergeoptions::
|
||||
option values containing whitespace characters are currently not
|
||||
supported.
|
||||
|
||||
branch.<name>.rebase::
|
||||
When true, rebase the branch <name> on top of the fetched branch,
|
||||
instead of merging the default branch from the default remote.
|
||||
*NOTE*: this is a possibly dangerous operation; do *not* use
|
||||
it unless you understand the implications (see gitlink:git-rebase[1]
|
||||
for details).
|
||||
|
||||
clean.requireForce::
|
||||
A boolean to make git-clean do nothing unless given -f
|
||||
or -n. Defaults to true.
|
||||
@@ -500,7 +507,9 @@ gc.rerereunresolved::
|
||||
rerere.enabled::
|
||||
Activate recording of resolved conflicts, so that identical
|
||||
conflict hunks can be resolved automatically, should they
|
||||
be encountered again. See gitlink:git-rerere[1].
|
||||
be encountered again. gitlink:git-rerere[1] command is by
|
||||
default enabled, but can be disabled by setting this option to
|
||||
false.
|
||||
|
||||
gitcvs.enabled::
|
||||
Whether the CVS server interface is enabled for this repository.
|
||||
@@ -543,6 +552,11 @@ specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'
|
||||
is one of "ext" and "pserver") to make them apply only for the given
|
||||
access method.
|
||||
|
||||
http.proxy::
|
||||
Override the HTTP proxy, normally configured using the 'http_proxy'
|
||||
environment variable (see gitlink:curl[1]). This can be overridden
|
||||
on a per-remote basis; see remote.<name>.proxy
|
||||
|
||||
http.sslVerify::
|
||||
Whether to verify the SSL certificate when fetching or pushing
|
||||
over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment
|
||||
@@ -691,6 +705,11 @@ remote.<name>.url::
|
||||
The URL of a remote repository. See gitlink:git-fetch[1] or
|
||||
gitlink:git-push[1].
|
||||
|
||||
remote.<name>.proxy::
|
||||
For remotes that require curl (http, https and ftp), the URL to
|
||||
the proxy to use for that remote. Set to the empty string to
|
||||
disable proxying for that remote.
|
||||
|
||||
remote.<name>.fetch::
|
||||
The default set of "refspec" for gitlink:git-fetch[1]. See
|
||||
gitlink:git-fetch[1].
|
||||
|
||||
@@ -61,7 +61,14 @@ OPTIONS
|
||||
|
||||
-i, \--interactive::
|
||||
Add modified contents in the working tree interactively to
|
||||
the index.
|
||||
the index. Optional path arguments may be supplied to limit
|
||||
operation to a subset of the working tree. See ``Interactive
|
||||
mode'' for details.
|
||||
|
||||
-p, \--patch:
|
||||
Similar to Interactive mode but the initial command loop is
|
||||
bypassed and the 'patch' subcommand is invoked using each of
|
||||
the specified filepatterns before exiting.
|
||||
|
||||
-u::
|
||||
Update only files that git already knows about. This is similar
|
||||
@@ -210,6 +217,8 @@ patch::
|
||||
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
|
||||
s - split the current hunk into smaller hunks
|
||||
? - print help
|
||||
+
|
||||
After deciding the fate for all hunks, if there is any hunk
|
||||
that was chosen, the index is updated with the selected hunks.
|
||||
|
||||
@@ -10,6 +10,7 @@ SYNOPSIS
|
||||
[verse]
|
||||
'git-branch' [--color | --no-color] [-r | -a]
|
||||
[-v [--abbrev=<length> | --no-abbrev]]
|
||||
[--contains <commit>]
|
||||
'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
|
||||
'git-branch' (-m | -M) [<oldbranch>] <newbranch>
|
||||
'git-branch' (-d | -D) [-r] <branchname>...
|
||||
@@ -20,6 +21,9 @@ With no arguments given a list of existing branches
|
||||
will be shown, the current branch will be highlighted with an asterisk.
|
||||
Option `-r` causes the remote-tracking branches to be listed,
|
||||
and option `-a` shows both.
|
||||
With `--contains <commit>`, shows only the branches that
|
||||
contains the named commit (in other words, the branches whose
|
||||
tip commits are descendant of the named commit).
|
||||
|
||||
In its second form, a new branch named <branchname> will be created.
|
||||
It will start out with a head equal to the one given as <start-point>.
|
||||
|
||||
@@ -10,7 +10,7 @@ SYNOPSIS
|
||||
[verse]
|
||||
'git-commit' [-a | --interactive] [-s] [-v] [-u]
|
||||
[(-c | -C) <commit> | -F <file> | -m <msg> | --amend]
|
||||
[--no-verify] [-e] [--author <author>]
|
||||
[--allow-empty] [--no-verify] [-e] [--author <author>]
|
||||
[--] [[-i | -o ]<file>...]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -89,6 +89,12 @@ OPTIONS
|
||||
This option bypasses the pre-commit hook.
|
||||
See also link:hooks.html[hooks].
|
||||
|
||||
--allow-empty::
|
||||
Usually recording a commit that has the exact same tree as its
|
||||
sole parent commit is a mistake, and the command prevents you
|
||||
from making such a commit. This option bypasses the safety, and
|
||||
is primarily for use by foreign scm interface scripts.
|
||||
|
||||
-e|--edit::
|
||||
The message taken from file with `-F`, command line with
|
||||
`-m`, and from file with `-C` are usually used as the
|
||||
|
||||
@@ -20,6 +20,7 @@ SYNOPSIS
|
||||
'git-config' [<file-option>] --rename-section old_name new_name
|
||||
'git-config' [<file-option>] --remove-section name
|
||||
'git-config' [<file-option>] [-z|--null] -l | --list
|
||||
'git-config' [<file-option>] --get-color name [default]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -134,6 +135,12 @@ See also <<FILES>>.
|
||||
output without getting confused e.g. by values that
|
||||
contain line breaks.
|
||||
|
||||
--get-color name default::
|
||||
|
||||
Find the color configured for `name` (e.g. `color.diff.new`) and
|
||||
output it as the ANSI color escape sequence to the standard
|
||||
output. The optional `default` parameter is used instead, if
|
||||
there is no color configured for `name`.
|
||||
|
||||
[[FILES]]
|
||||
FILES
|
||||
@@ -292,6 +299,15 @@ To add a new proxy, without altering any of the existing ones, use
|
||||
% git config core.gitproxy '"proxy-command" for example.com'
|
||||
------------
|
||||
|
||||
An example to use customized color from the configuration in your
|
||||
script:
|
||||
|
||||
------------
|
||||
#!/bin/sh
|
||||
WS=$(git config --get-color color.diff.whitespace "blue reverse")
|
||||
RESET=$(git config --get-color "" "reset")
|
||||
echo "${WS}your whitespace color or blue reverse${RESET}"
|
||||
------------
|
||||
|
||||
include::config.txt[]
|
||||
|
||||
|
||||
83
Documentation/git-fast-export.txt
Normal file
83
Documentation/git-fast-export.txt
Normal file
@@ -0,0 +1,83 @@
|
||||
git-fast-export(1)
|
||||
==================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-fast-export - Git data exporter
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-fast-export [options]' | 'git-fast-import'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This program dumps the given revisions in a form suitable to be piped
|
||||
into gitlink:git-fast-import[1].
|
||||
|
||||
You can use it as a human readable bundle replacement (see
|
||||
gitlink:git-bundle[1]), or as a kind of an interactive
|
||||
gitlink:git-filter-branch[1].
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--progress=<n>::
|
||||
Insert 'progress' statements every <n> objects, to be shown by
|
||||
gitlink:git-fast-import[1] during import.
|
||||
|
||||
--signed-tags=(verbatim|warn|strip|abort)::
|
||||
Specify how to handle signed tags. Since any transformation
|
||||
after the export can change the tag names (which can also happen
|
||||
when excluding revisions) the signatures will not match.
|
||||
+
|
||||
When asking to 'abort' (which is the default), this program will die
|
||||
when encountering a signed tag. With 'strip', the tags will be made
|
||||
unsigned, with 'verbatim', they will be silently exported
|
||||
and with 'warn', they will be exported, but you will see a warning.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
-------------------------------------------------------------------
|
||||
$ git fast-export --all | (cd /empty/repository && git fast-import)
|
||||
-------------------------------------------------------------------
|
||||
|
||||
This will export the whole repository and import it into the existing
|
||||
empty repository. Except for reencoding commits that are not in
|
||||
UTF-8, it would be a one-to-one mirror.
|
||||
|
||||
-----------------------------------------------------
|
||||
$ git fast-export master~5..master |
|
||||
sed "s|refs/heads/master|refs/heads/other|" |
|
||||
git fast-import
|
||||
-----------------------------------------------------
|
||||
|
||||
This makes a new branch called 'other' from 'master~5..master'
|
||||
(i.e. if 'master' has linear history, it will take the last 5 commits).
|
||||
|
||||
Note that this assumes that none of the blobs and commit messages
|
||||
referenced by that revision range contains the string
|
||||
'refs/heads/master'.
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
Since gitlink:git-fast-import[1] cannot tag trees, you will not be
|
||||
able to export the linux-2.6.git repository completely, as it contains
|
||||
a tag referencing a tree instead of a commit.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Johannes E. Schindelin <johannes.schindelin@gmx.de>.
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Johannes E. Schindelin <johannes.schindelin@gmx.de>.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
48
Documentation/git-help.txt
Normal file
48
Documentation/git-help.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
git-help(1)
|
||||
===========
|
||||
|
||||
NAME
|
||||
----
|
||||
git-help - display help information about git
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git help' [-a|--all] [COMMAND]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
With no options and no COMMAND given, the synopsis of the 'git'
|
||||
command and a list of the most commonly used git commands are printed
|
||||
on the standard output.
|
||||
|
||||
If the option '--all' or '-a' is given, then all available commands are
|
||||
printed on the standard output.
|
||||
|
||||
If a git command is named, a manual page for that command is brought
|
||||
up. The 'man' program is used by default for this purpose.
|
||||
|
||||
Note that 'git --help ...' is identical as 'git help ...' because the
|
||||
former is internally converted into the latter.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-a|--all::
|
||||
|
||||
Prints all the available commands on the standard output. This
|
||||
option superseeds any other option.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano <gitster@pobox.com> and the git-list
|
||||
<git@vger.kernel.org>.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Initial documentation was part of the gitlink:git[7] man page.
|
||||
Christian Couder <chriscool@tuxfamily.org> extracted and rewrote it a
|
||||
little. Maintenance is done by the git-list <git@vger.kernel.org>.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
@@ -8,7 +8,7 @@ git-prune - Prune all unreachable objects from the object database
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-prune' [-n] [--] [<head>...]
|
||||
'git-prune' [-n] [--expire <expire>] [--] [<head>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -31,6 +31,9 @@ OPTIONS
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
\--expire <time>::
|
||||
Only expire loose objects older than <time>.
|
||||
|
||||
<head>...::
|
||||
In addition to objects
|
||||
reachable from any of our references, keep objects
|
||||
|
||||
@@ -33,6 +33,16 @@ include::urls-remotes.txt[]
|
||||
|
||||
include::merge-strategies.txt[]
|
||||
|
||||
\--rebase::
|
||||
Instead of a merge, perform a rebase after fetching.
|
||||
*NOTE:* This is a potentially _dangerous_ mode of operation.
|
||||
It rewrites history, which does not bode well when you
|
||||
published that history already. Do *not* use this option
|
||||
unless you have read gitlink:git-rebase[1] carefully.
|
||||
|
||||
\--no-rebase::
|
||||
Override earlier \--rebase.
|
||||
|
||||
DEFAULT BEHAVIOUR
|
||||
-----------------
|
||||
|
||||
|
||||
@@ -22,10 +22,6 @@ automerge results and corresponding hand-resolve results on the
|
||||
initial manual merge, and later by noticing the same automerge
|
||||
results and applying the previously recorded hand resolution.
|
||||
|
||||
[NOTE]
|
||||
You need to set the config variable rerere.enabled to enable this
|
||||
command.
|
||||
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
|
||||
@@ -85,7 +85,9 @@ Each pattern pair consists of the source side (before the colon)
|
||||
and the destination side (after the colon). The ref to be
|
||||
pushed is determined by finding a match that matches the source
|
||||
side, and where it is pushed is determined by using the
|
||||
destination side.
|
||||
destination side. The rules used to match a ref are the same
|
||||
rules used by gitlink:git-rev-parse[1] to resolve a symbolic ref
|
||||
name.
|
||||
|
||||
- It is an error if <src> does not match exactly one of the
|
||||
local refs.
|
||||
|
||||
@@ -65,7 +65,9 @@ OPTIONS
|
||||
Typing "git tag" without arguments, also lists all tags.
|
||||
|
||||
-m <msg>::
|
||||
Use the given tag message (instead of prompting)
|
||||
Use the given tag message (instead of prompting).
|
||||
If multiple `-m` options are given, there values are
|
||||
concatenated as separate paragraphs.
|
||||
|
||||
-F <file>::
|
||||
Take the tag message from the given file. Use '-' to
|
||||
|
||||
@@ -46,6 +46,7 @@ Documentation for older releases are available here:
|
||||
* link:v1.5.3/git.html[documentation for release 1.5.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.3.7.txt[1.5.3.7],
|
||||
link:RelNotes-1.5.3.6.txt[1.5.3.6],
|
||||
link:RelNotes-1.5.3.5.txt[1.5.3.5],
|
||||
link:RelNotes-1.5.3.4.txt[1.5.3.4],
|
||||
@@ -100,9 +101,9 @@ OPTIONS
|
||||
|
||||
--help::
|
||||
Prints the synopsis and a list of the most commonly used
|
||||
commands. If a git command is named this option will bring up
|
||||
the man-page for that command. If the option '--all' or '-a' is
|
||||
given then all available commands are printed.
|
||||
commands. If the option '--all' or '-a' is given then all
|
||||
available commands are printed. If a git command is named this
|
||||
option will bring up the manual page for that command.
|
||||
|
||||
--exec-path::
|
||||
Path to wherever your core git programs are installed.
|
||||
@@ -535,7 +536,7 @@ Authors
|
||||
-------
|
||||
* git's founding father is Linus Torvalds <torvalds@osdl.org>.
|
||||
* The current git nurse is Junio C Hamano <gitster@pobox.com>.
|
||||
* The git potty was written by Andres Ericsson <ae@op5.se>.
|
||||
* The git potty was written by Andreas Ericsson <ae@op5.se>.
|
||||
* General upbringing is handled by the git-list <git@vger.kernel.org>.
|
||||
|
||||
Documentation
|
||||
|
||||
40
Makefile
40
Makefile
@@ -213,7 +213,7 @@ BASIC_LDFLAGS =
|
||||
|
||||
SCRIPT_SH = \
|
||||
git-bisect.sh git-checkout.sh \
|
||||
git-clone.sh git-commit.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 \
|
||||
git-repack.sh git-request-pull.sh \
|
||||
@@ -233,7 +233,7 @@ SCRIPT_PERL = \
|
||||
|
||||
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
|
||||
git-status git-instaweb
|
||||
git-instaweb
|
||||
|
||||
# ... and all the rest that could be moved out of bindir to gitexecdir
|
||||
PROGRAMS = \
|
||||
@@ -258,7 +258,7 @@ EXTRA_PROGRAMS =
|
||||
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-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
|
||||
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
|
||||
|
||||
# what 'all' will build and 'install' will install, in gitexecdir
|
||||
@@ -268,9 +268,6 @@ ALL_PROGRAMS += git-merge-subtree$X
|
||||
|
||||
# what 'all' will build but not install in gitexecdir
|
||||
OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
|
||||
ifndef NO_TCLTK
|
||||
OTHER_PROGRAMS += gitk-wish
|
||||
endif
|
||||
|
||||
# Set paths to tools early so that they can be used for version tests.
|
||||
ifndef SHELL_PATH
|
||||
@@ -329,6 +326,7 @@ BUILTIN_OBJS = \
|
||||
builtin-checkout-index.o \
|
||||
builtin-check-ref-format.o \
|
||||
builtin-clean.o \
|
||||
builtin-commit.o \
|
||||
builtin-commit-tree.o \
|
||||
builtin-count-objects.o \
|
||||
builtin-describe.o \
|
||||
@@ -336,6 +334,7 @@ BUILTIN_OBJS = \
|
||||
builtin-diff-files.o \
|
||||
builtin-diff-index.o \
|
||||
builtin-diff-tree.o \
|
||||
builtin-fast-export.o \
|
||||
builtin-fetch.o \
|
||||
builtin-fetch-pack.o \
|
||||
builtin-fetch--tool.o \
|
||||
@@ -370,7 +369,6 @@ BUILTIN_OBJS = \
|
||||
builtin-rev-parse.o \
|
||||
builtin-revert.o \
|
||||
builtin-rm.o \
|
||||
builtin-runstatus.o \
|
||||
builtin-shortlog.o \
|
||||
builtin-show-branch.o \
|
||||
builtin-stripspace.o \
|
||||
@@ -445,6 +443,7 @@ ifeq ($(uname_O),Cygwin)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
|
||||
NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
|
||||
OLD_ICONV = UnfortunatelyYes
|
||||
# There are conflicting reports about this.
|
||||
# On some boxes NO_MMAP is needed, and not so elsewhere.
|
||||
# Try commenting this out if you suspect MMAP is more efficient
|
||||
@@ -808,6 +807,7 @@ endif
|
||||
all::
|
||||
ifndef NO_TCLTK
|
||||
$(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all
|
||||
$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
|
||||
endif
|
||||
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
|
||||
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) NOEXECTEMPL='$(NOEXECTEMPL)'
|
||||
@@ -815,12 +815,6 @@ endif
|
||||
strip: $(PROGRAMS) git$X
|
||||
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
|
||||
|
||||
gitk-wish: gitk GIT-GUI-VARS
|
||||
$(QUIET_GEN)$(RM) $@ $@+ && \
|
||||
sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \
|
||||
chmod +x $@+ && \
|
||||
mv -f $@+ $@
|
||||
|
||||
git.o: git.c common-cmds.h GIT-CFLAGS
|
||||
$(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
|
||||
$(ALL_CFLAGS) -c $(filter %.c,$^)
|
||||
@@ -837,7 +831,7 @@ git-merge-subtree$X: git-merge-recursive$X
|
||||
$(BUILT_INS): git$X
|
||||
$(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
|
||||
|
||||
common-cmds.h: ./generate-cmdlist.sh
|
||||
common-cmds.h: ./generate-cmdlist.sh command-list.txt
|
||||
|
||||
common-cmds.h: $(wildcard Documentation/git-*.txt)
|
||||
$(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
|
||||
@@ -873,9 +867,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
git-status: git-commit
|
||||
$(QUIET_GEN)cp $< $@+ && mv $@+ $@
|
||||
|
||||
gitweb/gitweb.cgi: gitweb/gitweb.perl
|
||||
$(QUIET_GEN)$(RM) $@ $@+ && \
|
||||
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
|
||||
@@ -1057,14 +1048,14 @@ remove-dashes:
|
||||
### Installation rules
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
|
||||
$(MAKE) -C perl prefix='$(prefix_SQ)' install
|
||||
ifndef NO_TCLTK
|
||||
$(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
|
||||
$(MAKE) -C gitk-git install
|
||||
$(MAKE) -C git-gui install
|
||||
endif
|
||||
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
|
||||
@@ -1157,7 +1148,7 @@ clean:
|
||||
$(MAKE) -C templates/ clean
|
||||
$(MAKE) -C t/ clean
|
||||
ifndef NO_TCLTK
|
||||
$(RM) gitk-wish
|
||||
$(MAKE) -C gitk-git clean
|
||||
$(MAKE) -C git-gui clean
|
||||
endif
|
||||
$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
|
||||
@@ -1179,7 +1170,7 @@ check-docs::
|
||||
esac ; \
|
||||
test -f "Documentation/$$v.txt" || \
|
||||
echo "no doc: $$v"; \
|
||||
sed -e '1,/^__DATA__/d' Documentation/cmd-list.perl | \
|
||||
sed -e '/^#/d' command-list.txt | \
|
||||
grep -q "^$$v[ ]" || \
|
||||
case "$$v" in \
|
||||
git) ;; \
|
||||
@@ -1187,9 +1178,9 @@ check-docs::
|
||||
esac ; \
|
||||
done; \
|
||||
( \
|
||||
sed -e '1,/^__DATA__/d' \
|
||||
sed -e '/^#/d' \
|
||||
-e 's/[ ].*//' \
|
||||
-e 's/^/listed /' Documentation/cmd-list.perl; \
|
||||
-e 's/^/listed /' command-list.txt; \
|
||||
ls -1 Documentation/git*txt | \
|
||||
sed -e 's|Documentation/|documented |' \
|
||||
-e 's/\.txt//'; \
|
||||
@@ -1198,6 +1189,7 @@ check-docs::
|
||||
case "$$how,$$cmd" in \
|
||||
*,git-citool | \
|
||||
*,git-gui | \
|
||||
*,git-help | \
|
||||
documented,gitattributes | \
|
||||
documented,gitignore | \
|
||||
documented,gitmodules | \
|
||||
|
||||
@@ -19,7 +19,7 @@ static const char * const builtin_add_usage[] = {
|
||||
"git-add [options] [--] <filepattern>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int patch_interactive = 0, add_interactive = 0;
|
||||
static int take_worktree_changes;
|
||||
|
||||
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
||||
@@ -105,12 +105,12 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
}
|
||||
}
|
||||
|
||||
void add_files_to_cache(int verbose, const char *prefix, const char **files)
|
||||
void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)
|
||||
{
|
||||
struct rev_info rev;
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
rev.prune_data = get_pathspec(prefix, files);
|
||||
rev.prune_data = pathspec;
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
rev.diffopt.format_callback_data = &verbose;
|
||||
@@ -135,11 +135,40 @@ static void refresh(int verbose, const char **pathspec)
|
||||
free(seen);
|
||||
}
|
||||
|
||||
int interactive_add(void)
|
||||
static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
const char *argv[2] = { "add--interactive", NULL };
|
||||
const char **pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
return run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
return pathspec;
|
||||
}
|
||||
|
||||
int interactive_add(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int status, ac;
|
||||
const char **args;
|
||||
const char **pathspec = NULL;
|
||||
|
||||
if (argc) {
|
||||
pathspec = validate_pathspec(argc, argv, prefix);
|
||||
if (!pathspec)
|
||||
return -1;
|
||||
}
|
||||
|
||||
args = xcalloc(sizeof(const char *), (argc + 4));
|
||||
ac = 0;
|
||||
args[ac++] = "add--interactive";
|
||||
if (patch_interactive)
|
||||
args[ac++] = "--patch";
|
||||
args[ac++] = "--";
|
||||
if (argc) {
|
||||
memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
|
||||
ac += argc;
|
||||
}
|
||||
args[ac] = NULL;
|
||||
|
||||
status = run_command_v_opt(args, RUN_GIT_CMD);
|
||||
free(args);
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct lock_file lock_file;
|
||||
@@ -148,13 +177,13 @@ static const char ignore_error[] =
|
||||
"The following paths are ignored by one of your .gitignore files:\n";
|
||||
|
||||
static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
|
||||
static int add_interactive = 0;
|
||||
|
||||
static struct option builtin_add_options[] = {
|
||||
OPT__DRY_RUN(&show_only),
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT_GROUP(""),
|
||||
OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
|
||||
OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
|
||||
OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
|
||||
OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
|
||||
OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
|
||||
@@ -163,26 +192,27 @@ static struct option builtin_add_options[] = {
|
||||
|
||||
int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, newfd, orig_argc = argc;
|
||||
int i, newfd;
|
||||
const char **pathspec;
|
||||
struct dir_struct dir;
|
||||
|
||||
argc = parse_options(argc, argv, builtin_add_options,
|
||||
builtin_add_usage, 0);
|
||||
if (add_interactive) {
|
||||
if (add_interactive != 1 || orig_argc != 2)
|
||||
die("add --interactive does not take any parameters");
|
||||
exit(interactive_add());
|
||||
}
|
||||
if (patch_interactive)
|
||||
add_interactive = 1;
|
||||
if (add_interactive)
|
||||
exit(interactive_add(argc, argv, prefix));
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
|
||||
if (take_worktree_changes) {
|
||||
const char **pathspec;
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
add_files_to_cache(verbose, prefix, argv);
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
add_files_to_cache(verbose, prefix, pathspec);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
||||
@@ -184,9 +184,30 @@ struct ref_item {
|
||||
struct ref_list {
|
||||
int index, alloc, maxwidth;
|
||||
struct ref_item *list;
|
||||
struct commit_list *with_commit;
|
||||
int kinds;
|
||||
};
|
||||
|
||||
static int has_commit(const unsigned char *sha1, struct commit_list *with_commit)
|
||||
{
|
||||
struct commit *commit;
|
||||
|
||||
if (!with_commit)
|
||||
return 1;
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
if (!commit)
|
||||
return 0;
|
||||
while (with_commit) {
|
||||
struct commit *other;
|
||||
|
||||
other = with_commit->item;
|
||||
with_commit = with_commit->next;
|
||||
if (in_merge_bases(other, &commit, 1))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct ref_list *ref_list = (struct ref_list*)(cb_data);
|
||||
@@ -206,6 +227,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
|
||||
refname += 10;
|
||||
}
|
||||
|
||||
/* Filter with with_commit if specified */
|
||||
if (!has_commit(sha1, ref_list->with_commit))
|
||||
return 0;
|
||||
|
||||
/* Don't add types the caller doesn't want */
|
||||
if ((kind & ref_list->kinds) == 0)
|
||||
return 0;
|
||||
@@ -296,19 +321,20 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
}
|
||||
}
|
||||
|
||||
static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
|
||||
static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
|
||||
{
|
||||
int i;
|
||||
struct ref_list ref_list;
|
||||
|
||||
memset(&ref_list, 0, sizeof(ref_list));
|
||||
ref_list.kinds = kinds;
|
||||
ref_list.with_commit = with_commit;
|
||||
for_each_ref(append_ref, &ref_list);
|
||||
|
||||
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
|
||||
|
||||
detached = (detached && (kinds & REF_LOCAL_BRANCH));
|
||||
if (detached) {
|
||||
if (detached && has_commit(head_sha1, with_commit)) {
|
||||
struct ref_item item;
|
||||
item.name = xstrdup("(no branch)");
|
||||
item.kind = REF_LOCAL_BRANCH;
|
||||
@@ -505,12 +531,29 @@ static void rename_branch(const char *oldname, const char *newname, int force)
|
||||
die("Branch is renamed, but update of config-file failed");
|
||||
}
|
||||
|
||||
static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct commit *commit;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
if (get_sha1(arg, sha1))
|
||||
die("malformed object name %s", arg);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit)
|
||||
die("no such commit %s", arg);
|
||||
commit_list_insert(commit, opt->value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 kinds = REF_LOCAL_BRANCH;
|
||||
struct commit_list *with_commit = NULL;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_GROUP("Generic options"),
|
||||
@@ -519,6 +562,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
|
||||
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
|
||||
REF_REMOTE_BRANCH),
|
||||
OPT_CALLBACK(0, "contains", &with_commit, "commit",
|
||||
"print only branches that contain the commit",
|
||||
opt_parse_with_commit),
|
||||
{
|
||||
OPTION_CALLBACK, 0, "with", &with_commit, "commit",
|
||||
"print only branches that contain the commit",
|
||||
PARSE_OPT_HIDDEN, opt_parse_with_commit,
|
||||
},
|
||||
OPT__ABBREV(&abbrev),
|
||||
|
||||
OPT_GROUP("Specific git-branch actions:"),
|
||||
@@ -554,7 +605,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
if (delete)
|
||||
return delete_branches(argc, argv, delete > 1, kinds);
|
||||
else if (argc == 0)
|
||||
print_ref_list(kinds, detached, verbose, abbrev);
|
||||
print_ref_list(kinds, detached, verbose, abbrev, with_commit);
|
||||
else if (rename && (argc == 1))
|
||||
rename_branch(head, argv[0], rename > 1);
|
||||
else if (rename && (argc == 2))
|
||||
|
||||
851
builtin-commit.c
Normal file
851
builtin-commit.c
Normal file
@@ -0,0 +1,851 @@
|
||||
/*
|
||||
* Builtin "git commit"
|
||||
*
|
||||
* Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
|
||||
* Based on git-commit.sh by Junio C Hamano and Linus Torvalds
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
#include "cache-tree.h"
|
||||
#include "dir.h"
|
||||
#include "builtin.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "commit.h"
|
||||
#include "revision.h"
|
||||
#include "wt-status.h"
|
||||
#include "run-command.h"
|
||||
#include "refs.h"
|
||||
#include "log-tree.h"
|
||||
#include "strbuf.h"
|
||||
#include "utf8.h"
|
||||
#include "parse-options.h"
|
||||
#include "path-list.h"
|
||||
|
||||
static const char * const builtin_commit_usage[] = {
|
||||
"git-commit [options] [--] <filepattern>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * const builtin_status_usage[] = {
|
||||
"git-status [options] [--] <filepattern>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
static unsigned char head_sha1[20], merge_head_sha1[20];
|
||||
static char *use_message_buffer;
|
||||
static const char commit_editmsg[] = "COMMIT_EDITMSG";
|
||||
static struct lock_file index_lock; /* real index */
|
||||
static struct lock_file false_lock; /* used only for partial commits */
|
||||
static enum {
|
||||
COMMIT_AS_IS = 1,
|
||||
COMMIT_NORMAL,
|
||||
COMMIT_PARTIAL,
|
||||
} commit_style;
|
||||
|
||||
static char *logfile, *force_author, *template_file;
|
||||
static char *edit_message, *use_message;
|
||||
static int all, edit_flag, also, interactive, only, amend, signoff;
|
||||
static int quiet, verbose, untracked_files, no_verify, allow_empty;
|
||||
|
||||
static int no_edit, initial_commit, in_merge;
|
||||
const char *only_include_assumed;
|
||||
struct strbuf message;
|
||||
|
||||
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct strbuf *buf = opt->value;
|
||||
if (unset)
|
||||
strbuf_setlen(buf, 0);
|
||||
else {
|
||||
strbuf_addstr(buf, arg);
|
||||
strbuf_addch(buf, '\n');
|
||||
strbuf_addch(buf, '\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct option builtin_commit_options[] = {
|
||||
OPT__QUIET(&quiet),
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT_GROUP("Commit message options"),
|
||||
|
||||
OPT_STRING('F', "file", &logfile, "FILE", "read log from file"),
|
||||
OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
|
||||
OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
|
||||
OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
|
||||
OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
|
||||
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by: header"),
|
||||
OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
|
||||
OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
|
||||
|
||||
OPT_GROUP("Commit contents options"),
|
||||
OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
|
||||
OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
|
||||
OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
|
||||
OPT_BOOLEAN('o', "only", &only, ""),
|
||||
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
|
||||
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
|
||||
OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
|
||||
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
|
||||
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static void rollback_index_files(void)
|
||||
{
|
||||
switch (commit_style) {
|
||||
case COMMIT_AS_IS:
|
||||
break; /* nothing to do */
|
||||
case COMMIT_NORMAL:
|
||||
rollback_lock_file(&index_lock);
|
||||
break;
|
||||
case COMMIT_PARTIAL:
|
||||
rollback_lock_file(&index_lock);
|
||||
rollback_lock_file(&false_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void commit_index_files(void)
|
||||
{
|
||||
switch (commit_style) {
|
||||
case COMMIT_AS_IS:
|
||||
break; /* nothing to do */
|
||||
case COMMIT_NORMAL:
|
||||
commit_lock_file(&index_lock);
|
||||
break;
|
||||
case COMMIT_PARTIAL:
|
||||
commit_lock_file(&index_lock);
|
||||
rollback_lock_file(&false_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a union of paths in the index and the named tree (typically, "HEAD"),
|
||||
* and return the paths that match the given pattern in list.
|
||||
*/
|
||||
static int list_paths(struct path_list *list, const char *with_tree,
|
||||
const char *prefix, const char **pattern)
|
||||
{
|
||||
int i;
|
||||
char *m;
|
||||
|
||||
for (i = 0; pattern[i]; i++)
|
||||
;
|
||||
m = xcalloc(1, i);
|
||||
|
||||
if (with_tree)
|
||||
overlay_tree_on_cache(with_tree, prefix);
|
||||
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce->ce_flags & htons(CE_UPDATE))
|
||||
continue;
|
||||
if (!pathspec_match(pattern, m, ce->name, 0))
|
||||
continue;
|
||||
path_list_insert(ce->name, list);
|
||||
}
|
||||
|
||||
return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
|
||||
}
|
||||
|
||||
static void add_remove_files(struct path_list *list)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < list->nr; i++) {
|
||||
struct path_list_item *p = &(list->items[i]);
|
||||
if (file_exists(p->path))
|
||||
add_file_to_cache(p->path, 0);
|
||||
else
|
||||
remove_file_from_cache(p->path);
|
||||
}
|
||||
}
|
||||
|
||||
static char *prepare_index(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int fd;
|
||||
struct tree *tree;
|
||||
struct path_list partial;
|
||||
const char **pathspec = NULL;
|
||||
|
||||
if (interactive) {
|
||||
interactive_add(argc, argv, prefix);
|
||||
commit_style = COMMIT_AS_IS;
|
||||
return get_index_file();
|
||||
}
|
||||
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
|
||||
if (*argv)
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
/*
|
||||
* Non partial, non as-is commit.
|
||||
*
|
||||
* (1) get the real index;
|
||||
* (2) update the_index as necessary;
|
||||
* (3) write the_index out to the real index (still locked);
|
||||
* (4) return the name of the locked index file.
|
||||
*
|
||||
* The caller should run hooks on the locked real index, and
|
||||
* (A) if all goes well, commit the real index;
|
||||
* (B) on failure, rollback the real index.
|
||||
*/
|
||||
if (all || (also && pathspec && *pathspec)) {
|
||||
int fd = hold_locked_index(&index_lock, 1);
|
||||
add_files_to_cache(0, also ? prefix : NULL, pathspec);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
if (write_cache(fd, active_cache, active_nr) || close(fd))
|
||||
die("unable to write new_index file");
|
||||
commit_style = COMMIT_NORMAL;
|
||||
return index_lock.filename;
|
||||
}
|
||||
|
||||
/*
|
||||
* As-is commit.
|
||||
*
|
||||
* (1) return the name of the real index file.
|
||||
*
|
||||
* The caller should run hooks on the real index, and run
|
||||
* hooks on the real index, and create commit from the_index.
|
||||
* We still need to refresh the index here.
|
||||
*/
|
||||
if (!pathspec || !*pathspec) {
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
close(fd) || commit_locked_index(&index_lock))
|
||||
die("unable to write new_index file");
|
||||
commit_style = COMMIT_AS_IS;
|
||||
return get_index_file();
|
||||
}
|
||||
|
||||
/*
|
||||
* A partial commit.
|
||||
*
|
||||
* (0) find the set of affected paths;
|
||||
* (1) get lock on the real index file;
|
||||
* (2) update the_index with the given paths;
|
||||
* (3) write the_index out to the real index (still locked);
|
||||
* (4) get lock on the false index file;
|
||||
* (5) reset the_index from HEAD;
|
||||
* (6) update the_index the same way as (2);
|
||||
* (7) write the_index out to the false index file;
|
||||
* (8) return the name of the false index file (still locked);
|
||||
*
|
||||
* The caller should run hooks on the locked false index, and
|
||||
* create commit from it. Then
|
||||
* (A) if all goes well, commit the real index;
|
||||
* (B) on failure, rollback the real index;
|
||||
* In either case, rollback the false index.
|
||||
*/
|
||||
commit_style = COMMIT_PARTIAL;
|
||||
|
||||
if (file_exists(git_path("MERGE_HEAD")))
|
||||
die("cannot do a partial commit during a merge.");
|
||||
|
||||
memset(&partial, 0, sizeof(partial));
|
||||
partial.strdup_paths = 1;
|
||||
if (list_paths(&partial, initial_commit ? NULL : "HEAD", prefix, pathspec))
|
||||
exit(1);
|
||||
|
||||
discard_cache();
|
||||
if (read_cache() < 0)
|
||||
die("cannot read the index");
|
||||
|
||||
fd = hold_locked_index(&index_lock, 1);
|
||||
add_remove_files(&partial);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
if (write_cache(fd, active_cache, active_nr) || close(fd))
|
||||
die("unable to write new_index file");
|
||||
|
||||
fd = hold_lock_file_for_update(&false_lock,
|
||||
git_path("next-index-%d", getpid()), 1);
|
||||
discard_cache();
|
||||
if (!initial_commit) {
|
||||
tree = parse_tree_indirect(head_sha1);
|
||||
if (!tree)
|
||||
die("failed to unpack HEAD tree object");
|
||||
if (read_tree(tree, 0, NULL))
|
||||
die("failed to read HEAD tree object");
|
||||
}
|
||||
add_remove_files(&partial);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
if (write_cache(fd, active_cache, active_nr) || close(fd))
|
||||
die("unable to write temporary index file");
|
||||
return false_lock.filename;
|
||||
}
|
||||
|
||||
static int run_status(FILE *fp, const char *index_file, const char *prefix)
|
||||
{
|
||||
struct wt_status s;
|
||||
|
||||
wt_status_prepare(&s);
|
||||
s.prefix = prefix;
|
||||
|
||||
if (amend) {
|
||||
s.amend = 1;
|
||||
s.reference = "HEAD^1";
|
||||
}
|
||||
s.verbose = verbose;
|
||||
s.untracked = untracked_files;
|
||||
s.index_file = index_file;
|
||||
s.fp = fp;
|
||||
|
||||
wt_status_print(&s);
|
||||
|
||||
return s.commitable;
|
||||
}
|
||||
|
||||
static const char sign_off_header[] = "Signed-off-by: ";
|
||||
|
||||
static int prepare_log_message(const char *index_file, const char *prefix)
|
||||
{
|
||||
struct stat statbuf;
|
||||
int commitable, saved_color_setting;
|
||||
struct strbuf sb;
|
||||
char *buffer;
|
||||
FILE *fp;
|
||||
|
||||
strbuf_init(&sb, 0);
|
||||
if (message.len) {
|
||||
strbuf_addbuf(&sb, &message);
|
||||
} else if (logfile && !strcmp(logfile, "-")) {
|
||||
if (isatty(0))
|
||||
fprintf(stderr, "(reading log message from standard input)\n");
|
||||
if (strbuf_read(&sb, 0, 0) < 0)
|
||||
die("could not read log from standard input");
|
||||
} else if (logfile) {
|
||||
if (strbuf_read_file(&sb, logfile, 0) < 0)
|
||||
die("could not read log file '%s': %s",
|
||||
logfile, strerror(errno));
|
||||
} else if (use_message) {
|
||||
buffer = strstr(use_message_buffer, "\n\n");
|
||||
if (!buffer || buffer[2] == '\0')
|
||||
die("commit has empty message");
|
||||
strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
|
||||
} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
|
||||
if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
|
||||
die("could not read MERGE_MSG: %s", strerror(errno));
|
||||
} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
|
||||
if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
|
||||
die("could not read SQUASH_MSG: %s", strerror(errno));
|
||||
} else if (template_file && !stat(template_file, &statbuf)) {
|
||||
if (strbuf_read_file(&sb, template_file, 0) < 0)
|
||||
die("could not read %s: %s",
|
||||
template_file, strerror(errno));
|
||||
}
|
||||
|
||||
fp = fopen(git_path(commit_editmsg), "w");
|
||||
if (fp == NULL)
|
||||
die("could not open %s", git_path(commit_editmsg));
|
||||
|
||||
stripspace(&sb, 0);
|
||||
|
||||
if (signoff) {
|
||||
struct strbuf sob;
|
||||
int i;
|
||||
|
||||
strbuf_init(&sob, 0);
|
||||
strbuf_addstr(&sob, sign_off_header);
|
||||
strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
|
||||
getenv("GIT_COMMITTER_EMAIL")));
|
||||
strbuf_addch(&sob, '\n');
|
||||
for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
|
||||
; /* do nothing */
|
||||
if (prefixcmp(sb.buf + i, sob.buf)) {
|
||||
if (prefixcmp(sb.buf + i, sign_off_header))
|
||||
strbuf_addch(&sb, '\n');
|
||||
strbuf_addbuf(&sb, &sob);
|
||||
}
|
||||
strbuf_release(&sob);
|
||||
}
|
||||
|
||||
if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
|
||||
die("could not write commit template: %s", strerror(errno));
|
||||
|
||||
strbuf_release(&sb);
|
||||
|
||||
if (no_edit) {
|
||||
struct rev_info rev;
|
||||
unsigned char sha1[40];
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (!active_nr && read_cache() < 0)
|
||||
die("Cannot read index");
|
||||
|
||||
if (get_sha1("HEAD", sha1) != 0)
|
||||
return !!active_nr;
|
||||
|
||||
init_revisions(&rev, "");
|
||||
rev.abbrev = 0;
|
||||
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 /* cached */);
|
||||
|
||||
return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
|
||||
}
|
||||
|
||||
if (in_merge && !no_edit)
|
||||
fprintf(fp,
|
||||
"#\n"
|
||||
"# It looks like you may be committing a MERGE.\n"
|
||||
"# If this is not correct, please remove the file\n"
|
||||
"# %s\n"
|
||||
"# and try again.\n"
|
||||
"#\n",
|
||||
git_path("MERGE_HEAD"));
|
||||
|
||||
fprintf(fp,
|
||||
"\n"
|
||||
"# Please enter the commit message for your changes.\n"
|
||||
"# (Comment lines starting with '#' will not be included)\n");
|
||||
if (only_include_assumed)
|
||||
fprintf(fp, "# %s\n", only_include_assumed);
|
||||
|
||||
saved_color_setting = wt_status_use_color;
|
||||
wt_status_use_color = 0;
|
||||
commitable = run_status(fp, index_file, prefix);
|
||||
wt_status_use_color = saved_color_setting;
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return commitable;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out if the message starting at position 'start' in the strbuf
|
||||
* contains only whitespace and Signed-off-by lines.
|
||||
*/
|
||||
static int message_is_empty(struct strbuf *sb, int start)
|
||||
{
|
||||
struct strbuf tmpl;
|
||||
const char *nl;
|
||||
int eol, i;
|
||||
|
||||
/* See if the template is just a prefix of the message. */
|
||||
strbuf_init(&tmpl, 0);
|
||||
if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
|
||||
stripspace(&tmpl, 1);
|
||||
if (start + tmpl.len <= sb->len &&
|
||||
memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
|
||||
start += tmpl.len;
|
||||
}
|
||||
strbuf_release(&tmpl);
|
||||
|
||||
/* Check if the rest is just whitespace and Signed-of-by's. */
|
||||
for (i = start; i < sb->len; i++) {
|
||||
nl = memchr(sb->buf + i, '\n', sb->len - i);
|
||||
if (nl)
|
||||
eol = nl - sb->buf;
|
||||
else
|
||||
eol = sb->len;
|
||||
|
||||
if (strlen(sign_off_header) <= eol - i &&
|
||||
!prefixcmp(sb->buf + i, sign_off_header)) {
|
||||
i = eol;
|
||||
continue;
|
||||
}
|
||||
while (i < eol)
|
||||
if (!isspace(sb->buf[i++]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void determine_author_info(struct strbuf *sb)
|
||||
{
|
||||
char *name, *email, *date;
|
||||
|
||||
name = getenv("GIT_AUTHOR_NAME");
|
||||
email = getenv("GIT_AUTHOR_EMAIL");
|
||||
date = getenv("GIT_AUTHOR_DATE");
|
||||
|
||||
if (use_message) {
|
||||
const char *a, *lb, *rb, *eol;
|
||||
|
||||
a = strstr(use_message_buffer, "\nauthor ");
|
||||
if (!a)
|
||||
die("invalid commit: %s", use_message);
|
||||
|
||||
lb = strstr(a + 8, " <");
|
||||
rb = strstr(a + 8, "> ");
|
||||
eol = strchr(a + 8, '\n');
|
||||
if (!lb || !rb || !eol)
|
||||
die("invalid commit: %s", use_message);
|
||||
|
||||
name = xstrndup(a + 8, lb - (a + 8));
|
||||
email = xstrndup(lb + 2, rb - (lb + 2));
|
||||
date = xstrndup(rb + 2, eol - (rb + 2));
|
||||
}
|
||||
|
||||
if (force_author) {
|
||||
const char *lb = strstr(force_author, " <");
|
||||
const char *rb = strchr(force_author, '>');
|
||||
|
||||
if (!lb || !rb)
|
||||
die("malformed --author parameter");
|
||||
name = xstrndup(force_author, lb - force_author);
|
||||
email = xstrndup(lb + 2, rb - (lb + 2));
|
||||
}
|
||||
|
||||
strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, 1));
|
||||
}
|
||||
|
||||
static int parse_and_validate_options(int argc, const char *argv[],
|
||||
const char * const usage[])
|
||||
{
|
||||
int f = 0;
|
||||
|
||||
argc = parse_options(argc, argv, builtin_commit_options, usage, 0);
|
||||
|
||||
if (logfile || message.len || use_message)
|
||||
no_edit = 1;
|
||||
if (edit_flag)
|
||||
no_edit = 0;
|
||||
|
||||
if (get_sha1("HEAD", head_sha1))
|
||||
initial_commit = 1;
|
||||
|
||||
if (!get_sha1("MERGE_HEAD", merge_head_sha1))
|
||||
in_merge = 1;
|
||||
|
||||
/* Sanity check options */
|
||||
if (amend && initial_commit)
|
||||
die("You have nothing to amend.");
|
||||
if (amend && in_merge)
|
||||
die("You are in the middle of a merge -- cannot amend.");
|
||||
|
||||
if (use_message)
|
||||
f++;
|
||||
if (edit_message)
|
||||
f++;
|
||||
if (logfile)
|
||||
f++;
|
||||
if (f > 1)
|
||||
die("Only one of -c/-C/-F can be used.");
|
||||
if (message.len && f > 0)
|
||||
die("Option -m cannot be combined with -c/-C/-F.");
|
||||
if (edit_message)
|
||||
use_message = edit_message;
|
||||
if (amend)
|
||||
use_message = "HEAD";
|
||||
if (use_message) {
|
||||
unsigned char sha1[20];
|
||||
static char utf8[] = "UTF-8";
|
||||
const char *out_enc;
|
||||
char *enc, *end;
|
||||
struct commit *commit;
|
||||
|
||||
if (get_sha1(use_message, sha1))
|
||||
die("could not lookup commit %s", use_message);
|
||||
commit = lookup_commit(sha1);
|
||||
if (!commit || parse_commit(commit))
|
||||
die("could not parse commit %s", use_message);
|
||||
|
||||
enc = strstr(commit->buffer, "\nencoding");
|
||||
if (enc) {
|
||||
end = strchr(enc + 10, '\n');
|
||||
enc = xstrndup(enc + 10, end - (enc + 10));
|
||||
} else {
|
||||
enc = utf8;
|
||||
}
|
||||
out_enc = git_commit_encoding ? git_commit_encoding : utf8;
|
||||
|
||||
if (strcmp(out_enc, enc))
|
||||
use_message_buffer =
|
||||
reencode_string(commit->buffer, out_enc, enc);
|
||||
|
||||
/*
|
||||
* If we failed to reencode the buffer, just copy it
|
||||
* byte for byte so the user can try to fix it up.
|
||||
* This also handles the case where input and output
|
||||
* encodings are identical.
|
||||
*/
|
||||
if (use_message_buffer == NULL)
|
||||
use_message_buffer = xstrdup(commit->buffer);
|
||||
if (enc != utf8)
|
||||
free(enc);
|
||||
}
|
||||
|
||||
if (!!also + !!only + !!all + !!interactive > 1)
|
||||
die("Only one of --include/--only/--all/--interactive can be used.");
|
||||
if (argc == 0 && (also || (only && !amend)))
|
||||
die("No paths with --include/--only does not make sense.");
|
||||
if (argc == 0 && only && amend)
|
||||
only_include_assumed = "Clever... amending the last one with dirty index.";
|
||||
if (argc > 0 && !also && !only) {
|
||||
only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
|
||||
also = 0;
|
||||
}
|
||||
|
||||
if (all && argc > 0)
|
||||
die("Paths with -a does not make sense.");
|
||||
else if (interactive && argc > 0)
|
||||
die("Paths with --interactive does not make sense.");
|
||||
|
||||
return argc;
|
||||
}
|
||||
|
||||
int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
const char *index_file;
|
||||
int commitable;
|
||||
|
||||
git_config(git_status_config);
|
||||
|
||||
argc = parse_and_validate_options(argc, argv, builtin_status_usage);
|
||||
|
||||
index_file = prepare_index(argc, argv, prefix);
|
||||
|
||||
commitable = run_status(stdout, index_file, prefix);
|
||||
|
||||
rollback_index_files();
|
||||
|
||||
return commitable ? 0 : 1;
|
||||
}
|
||||
|
||||
static int run_hook(const char *index_file, const char *name, const char *arg)
|
||||
{
|
||||
struct child_process hook;
|
||||
const char *argv[3], *env[2];
|
||||
char index[PATH_MAX];
|
||||
|
||||
argv[0] = git_path("hooks/%s", name);
|
||||
argv[1] = arg;
|
||||
argv[2] = NULL;
|
||||
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
|
||||
env[0] = index;
|
||||
env[1] = NULL;
|
||||
|
||||
if (access(argv[0], X_OK) < 0)
|
||||
return 0;
|
||||
|
||||
memset(&hook, 0, sizeof(hook));
|
||||
hook.argv = argv;
|
||||
hook.no_stdin = 1;
|
||||
hook.stdout_to_stderr = 1;
|
||||
hook.env = env;
|
||||
|
||||
return run_command(&hook);
|
||||
}
|
||||
|
||||
static void print_summary(const char *prefix, const unsigned char *sha1)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct commit *commit;
|
||||
|
||||
commit = lookup_commit(sha1);
|
||||
if (!commit)
|
||||
die("couldn't look up newly created commit");
|
||||
if (!commit || parse_commit(commit))
|
||||
die("could not parse newly created commit");
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
|
||||
rev.abbrev = 0;
|
||||
rev.diff = 1;
|
||||
rev.diffopt.output_format =
|
||||
DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
|
||||
|
||||
rev.verbose_header = 1;
|
||||
rev.show_root_diff = 1;
|
||||
rev.commit_format = get_commit_format("format:%h: %s");
|
||||
rev.always_show_header = 1;
|
||||
|
||||
printf("Created %scommit ", initial_commit ? "initial " : "");
|
||||
|
||||
log_tree_commit(&rev, commit);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int git_commit_config(const char *k, const char *v)
|
||||
{
|
||||
if (!strcmp(k, "commit.template")) {
|
||||
template_file = xstrdup(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_status_config(k, v);
|
||||
}
|
||||
|
||||
static int is_a_merge(const unsigned char *sha1)
|
||||
{
|
||||
struct commit *commit = lookup_commit(sha1);
|
||||
if (!commit || parse_commit(commit))
|
||||
die("could not parse HEAD commit");
|
||||
return !!(commit->parents && commit->parents->next);
|
||||
}
|
||||
|
||||
static const char commit_utf8_warn[] =
|
||||
"Warning: commit message does not conform to UTF-8.\n"
|
||||
"You may want to amend it after fixing the message, or set the config\n"
|
||||
"variable i18n.commitencoding to the encoding your project uses.\n";
|
||||
|
||||
int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int header_len;
|
||||
struct strbuf sb;
|
||||
const char *index_file, *reflog_msg;
|
||||
char *nl, *p;
|
||||
unsigned char commit_sha1[20];
|
||||
struct ref_lock *ref_lock;
|
||||
|
||||
git_config(git_commit_config);
|
||||
|
||||
argc = parse_and_validate_options(argc, argv, builtin_commit_usage);
|
||||
|
||||
index_file = prepare_index(argc, argv, prefix);
|
||||
|
||||
if (!no_verify && run_hook(index_file, "pre-commit", NULL)) {
|
||||
rollback_index_files();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!prepare_log_message(index_file, prefix) && !in_merge &&
|
||||
!allow_empty && !(amend && is_a_merge(head_sha1))) {
|
||||
run_status(stdout, index_file, prefix);
|
||||
rollback_index_files();
|
||||
unlink(commit_editmsg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-read the index as pre-commit hook could have updated it,
|
||||
* and write it out as a tree.
|
||||
*/
|
||||
discard_cache();
|
||||
read_cache_from(index_file);
|
||||
if (!active_cache_tree)
|
||||
active_cache_tree = cache_tree();
|
||||
if (cache_tree_update(active_cache_tree,
|
||||
active_cache, active_nr, 0, 0) < 0) {
|
||||
rollback_index_files();
|
||||
die("Error building trees");
|
||||
}
|
||||
|
||||
/*
|
||||
* The commit object
|
||||
*/
|
||||
strbuf_init(&sb, 0);
|
||||
strbuf_addf(&sb, "tree %s\n",
|
||||
sha1_to_hex(active_cache_tree->sha1));
|
||||
|
||||
/* Determine parents */
|
||||
if (initial_commit) {
|
||||
reflog_msg = "commit (initial)";
|
||||
} else if (amend) {
|
||||
struct commit_list *c;
|
||||
struct commit *commit;
|
||||
|
||||
reflog_msg = "commit (amend)";
|
||||
commit = lookup_commit(head_sha1);
|
||||
if (!commit || parse_commit(commit))
|
||||
die("could not parse HEAD commit");
|
||||
|
||||
for (c = commit->parents; c; c = c->next)
|
||||
strbuf_addf(&sb, "parent %s\n",
|
||||
sha1_to_hex(c->item->object.sha1));
|
||||
} else if (in_merge) {
|
||||
struct strbuf m;
|
||||
FILE *fp;
|
||||
|
||||
reflog_msg = "commit (merge)";
|
||||
strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
|
||||
strbuf_init(&m, 0);
|
||||
fp = fopen(git_path("MERGE_HEAD"), "r");
|
||||
if (fp == NULL)
|
||||
die("could not open %s for reading: %s",
|
||||
git_path("MERGE_HEAD"), strerror(errno));
|
||||
while (strbuf_getline(&m, fp, '\n') != EOF)
|
||||
strbuf_addf(&sb, "parent %s\n", m.buf);
|
||||
fclose(fp);
|
||||
strbuf_release(&m);
|
||||
} else {
|
||||
reflog_msg = "commit";
|
||||
strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
|
||||
}
|
||||
|
||||
determine_author_info(&sb);
|
||||
strbuf_addf(&sb, "committer %s\n", git_committer_info(1));
|
||||
if (!is_encoding_utf8(git_commit_encoding))
|
||||
strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
|
||||
strbuf_addch(&sb, '\n');
|
||||
|
||||
/* Get the commit message and validate it */
|
||||
header_len = sb.len;
|
||||
if (!no_edit) {
|
||||
char index[PATH_MAX];
|
||||
const char *env[2] = { index, NULL };
|
||||
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
|
||||
launch_editor(git_path(commit_editmsg), &sb, env);
|
||||
} else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
|
||||
rollback_index_files();
|
||||
die("could not read commit message");
|
||||
}
|
||||
if (run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
|
||||
rollback_index_files();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Truncate the message just before the diff, if any. */
|
||||
p = strstr(sb.buf, "\ndiff --git a/");
|
||||
if (p != NULL)
|
||||
strbuf_setlen(&sb, p - sb.buf + 1);
|
||||
|
||||
stripspace(&sb, 1);
|
||||
if (sb.len < header_len || message_is_empty(&sb, header_len)) {
|
||||
rollback_index_files();
|
||||
die("no commit message? aborting commit.");
|
||||
}
|
||||
strbuf_addch(&sb, '\0');
|
||||
if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
|
||||
fprintf(stderr, commit_utf8_warn);
|
||||
|
||||
if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) {
|
||||
rollback_index_files();
|
||||
die("failed to write commit object");
|
||||
}
|
||||
|
||||
ref_lock = lock_any_ref_for_update("HEAD",
|
||||
initial_commit ? NULL : head_sha1,
|
||||
0);
|
||||
|
||||
nl = strchr(sb.buf + header_len, '\n');
|
||||
if (nl)
|
||||
strbuf_setlen(&sb, nl + 1 - sb.buf);
|
||||
else
|
||||
strbuf_addch(&sb, '\n');
|
||||
strbuf_remove(&sb, 0, header_len);
|
||||
strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
|
||||
strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
|
||||
|
||||
if (!ref_lock) {
|
||||
rollback_index_files();
|
||||
die("cannot lock HEAD ref");
|
||||
}
|
||||
if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) {
|
||||
rollback_index_files();
|
||||
die("cannot update HEAD ref");
|
||||
}
|
||||
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
|
||||
commit_index_files();
|
||||
|
||||
rerere();
|
||||
run_hook(get_index_file(), "post-commit", NULL);
|
||||
if (!quiet)
|
||||
print_summary(prefix, commit_sha1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "color.h"
|
||||
|
||||
static const char git_config_set_usage[] =
|
||||
"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list";
|
||||
"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default]";
|
||||
|
||||
static char *key;
|
||||
static regex_t *key_regexp;
|
||||
@@ -161,6 +162,53 @@ char *normalize_value(const char *key, const char *value)
|
||||
return normalized;
|
||||
}
|
||||
|
||||
static int get_color_found;
|
||||
static const char *get_color_slot;
|
||||
static char parsed_color[COLOR_MAXLEN];
|
||||
|
||||
static int git_get_color_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, get_color_slot)) {
|
||||
color_parse(value, var, parsed_color);
|
||||
get_color_found = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_color(int argc, const char **argv)
|
||||
{
|
||||
/*
|
||||
* grab the color setting for the given slot from the configuration,
|
||||
* or parse the default value if missing, and return ANSI color
|
||||
* escape sequence.
|
||||
*
|
||||
* e.g.
|
||||
* git config --get-color color.diff.whitespace "blue reverse"
|
||||
*/
|
||||
const char *def_color = NULL;
|
||||
|
||||
switch (argc) {
|
||||
default:
|
||||
usage(git_config_set_usage);
|
||||
case 2:
|
||||
def_color = argv[1];
|
||||
/* fallthru */
|
||||
case 1:
|
||||
get_color_slot = argv[0];
|
||||
break;
|
||||
}
|
||||
|
||||
get_color_found = 0;
|
||||
parsed_color[0] = '\0';
|
||||
git_config(git_get_color_config);
|
||||
|
||||
if (!get_color_found && def_color)
|
||||
color_parse(def_color, "command line", parsed_color);
|
||||
|
||||
fputs(parsed_color, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_config(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int nongit = 0;
|
||||
@@ -234,8 +282,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
} else if (!strcmp(argv[1], "--get-color")) {
|
||||
return get_color(argc-2, argv+2);
|
||||
} else
|
||||
break;
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
406
builtin-fast-export.c
Executable file
406
builtin-fast-export.c
Executable file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* "git fast-export" builtin command
|
||||
*
|
||||
* Copyright (C) 2007 Johannes E. Schindelin
|
||||
*/
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "object.h"
|
||||
#include "tag.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "log-tree.h"
|
||||
#include "revision.h"
|
||||
#include "decorate.h"
|
||||
#include "path-list.h"
|
||||
#include "utf8.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char *fast_export_usage[] = {
|
||||
"git-fast-export [rev-list-opts]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int progress;
|
||||
static enum { VERBATIM, WARN, STRIP, ABORT } signed_tag_mode = ABORT;
|
||||
|
||||
static int parse_opt_signed_tag_mode(const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
if (unset || !strcmp(arg, "abort"))
|
||||
signed_tag_mode = ABORT;
|
||||
else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
|
||||
signed_tag_mode = VERBATIM;
|
||||
else if (!strcmp(arg, "warn"))
|
||||
signed_tag_mode = WARN;
|
||||
else if (!strcmp(arg, "strip"))
|
||||
signed_tag_mode = STRIP;
|
||||
else
|
||||
return error("Unknown signed-tag mode: %s", arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct decoration idnums;
|
||||
static uint32_t last_idnum;
|
||||
|
||||
static int has_unshown_parent(struct commit *commit)
|
||||
{
|
||||
struct commit_list *parent;
|
||||
|
||||
for (parent = commit->parents; parent; parent = parent->next)
|
||||
if (!(parent->item->object.flags & SHOWN) &&
|
||||
!(parent->item->object.flags & UNINTERESTING))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Since intptr_t is C99, we do not use it here */
|
||||
static void mark_object(struct object *object)
|
||||
{
|
||||
last_idnum++;
|
||||
add_decoration(&idnums, object, ((uint32_t *)NULL) + last_idnum);
|
||||
}
|
||||
|
||||
static int get_object_mark(struct object *object)
|
||||
{
|
||||
void *decoration = lookup_decoration(&idnums, object);
|
||||
if (!decoration)
|
||||
return 0;
|
||||
return (uint32_t *)decoration - (uint32_t *)NULL;
|
||||
}
|
||||
|
||||
static void show_progress(void)
|
||||
{
|
||||
static int counter = 0;
|
||||
if (!progress)
|
||||
return;
|
||||
if ((++counter % progress) == 0)
|
||||
printf("progress %d objects\n", counter);
|
||||
}
|
||||
|
||||
static void handle_object(const unsigned char *sha1)
|
||||
{
|
||||
unsigned long size;
|
||||
enum object_type type;
|
||||
char *buf;
|
||||
struct object *object;
|
||||
|
||||
if (is_null_sha1(sha1))
|
||||
return;
|
||||
|
||||
object = parse_object(sha1);
|
||||
if (!object)
|
||||
die ("Could not read blob %s", sha1_to_hex(sha1));
|
||||
|
||||
if (object->flags & SHOWN)
|
||||
return;
|
||||
|
||||
buf = read_sha1_file(sha1, &type, &size);
|
||||
if (!buf)
|
||||
die ("Could not read blob %s", sha1_to_hex(sha1));
|
||||
|
||||
mark_object(object);
|
||||
|
||||
printf("blob\nmark :%d\ndata %lu\n", last_idnum, size);
|
||||
if (fwrite(buf, size, 1, stdout) != 1)
|
||||
die ("Could not write blob %s", sha1_to_hex(sha1));
|
||||
printf("\n");
|
||||
|
||||
show_progress();
|
||||
|
||||
object->flags |= SHOWN;
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void show_filemodify(struct diff_queue_struct *q,
|
||||
struct diff_options *options, void *data)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filespec *spec = q->queue[i]->two;
|
||||
if (is_null_sha1(spec->sha1))
|
||||
printf("D %s\n", spec->path);
|
||||
else {
|
||||
struct object *object = lookup_object(spec->sha1);
|
||||
printf("M 0%06o :%d %s\n", spec->mode,
|
||||
get_object_mark(object), spec->path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *find_encoding(const char *begin, const char *end)
|
||||
{
|
||||
const char *needle = "\nencoding ";
|
||||
char *bol, *eol;
|
||||
|
||||
bol = memmem(begin, end ? end - begin : strlen(begin),
|
||||
needle, strlen(needle));
|
||||
if (!bol)
|
||||
return git_commit_encoding;
|
||||
bol += strlen(needle);
|
||||
eol = strchrnul(bol, '\n');
|
||||
*eol = '\0';
|
||||
return bol;
|
||||
}
|
||||
|
||||
static void handle_commit(struct commit *commit, struct rev_info *rev)
|
||||
{
|
||||
int saved_output_format = rev->diffopt.output_format;
|
||||
const char *author, *author_end, *committer, *committer_end;
|
||||
const char *encoding, *message;
|
||||
char *reencoded = NULL;
|
||||
struct commit_list *p;
|
||||
int i;
|
||||
|
||||
rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
|
||||
parse_commit(commit);
|
||||
author = strstr(commit->buffer, "\nauthor ");
|
||||
if (!author)
|
||||
die ("Could not find author in commit %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
author++;
|
||||
author_end = strchrnul(author, '\n');
|
||||
committer = strstr(author_end, "\ncommitter ");
|
||||
if (!committer)
|
||||
die ("Could not find committer in commit %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
committer++;
|
||||
committer_end = strchrnul(committer, '\n');
|
||||
message = strstr(committer_end, "\n\n");
|
||||
encoding = find_encoding(committer_end, message);
|
||||
if (message)
|
||||
message += 2;
|
||||
|
||||
if (commit->parents) {
|
||||
parse_commit(commit->parents->item);
|
||||
diff_tree_sha1(commit->parents->item->tree->object.sha1,
|
||||
commit->tree->object.sha1, "", &rev->diffopt);
|
||||
}
|
||||
else
|
||||
diff_root_tree_sha1(commit->tree->object.sha1,
|
||||
"", &rev->diffopt);
|
||||
|
||||
for (i = 0; i < diff_queued_diff.nr; i++)
|
||||
handle_object(diff_queued_diff.queue[i]->two->sha1);
|
||||
|
||||
mark_object(&commit->object);
|
||||
if (!is_encoding_utf8(encoding))
|
||||
reencoded = reencode_string(message, "UTF-8", encoding);
|
||||
printf("commit %s\nmark :%d\n%.*s\n%.*s\ndata %u\n%s",
|
||||
(const char *)commit->util, last_idnum,
|
||||
(int)(author_end - author), author,
|
||||
(int)(committer_end - committer), committer,
|
||||
(unsigned)(reencoded
|
||||
? strlen(reencoded) : message
|
||||
? strlen(message) : 0),
|
||||
reencoded ? reencoded : message ? message : "");
|
||||
if (reencoded)
|
||||
free(reencoded);
|
||||
|
||||
for (i = 0, p = commit->parents; p; p = p->next) {
|
||||
int mark = get_object_mark(&p->item->object);
|
||||
if (!mark)
|
||||
continue;
|
||||
if (i == 0)
|
||||
printf("from :%d\n", mark);
|
||||
else if (i == 1)
|
||||
printf("merge :%d", mark);
|
||||
else
|
||||
printf(" :%d", mark);
|
||||
i++;
|
||||
}
|
||||
if (i > 1)
|
||||
printf("\n");
|
||||
|
||||
log_tree_diff_flush(rev);
|
||||
rev->diffopt.output_format = saved_output_format;
|
||||
|
||||
printf("\n");
|
||||
|
||||
show_progress();
|
||||
}
|
||||
|
||||
static void handle_tail(struct object_array *commits, struct rev_info *revs)
|
||||
{
|
||||
struct commit *commit;
|
||||
while (commits->nr) {
|
||||
commit = (struct commit *)commits->objects[commits->nr - 1].item;
|
||||
if (has_unshown_parent(commit))
|
||||
return;
|
||||
handle_commit(commit, revs);
|
||||
commits->nr--;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_tag(const char *name, struct tag *tag)
|
||||
{
|
||||
unsigned long size;
|
||||
enum object_type type;
|
||||
char *buf;
|
||||
const char *tagger, *tagger_end, *message;
|
||||
size_t message_size = 0;
|
||||
|
||||
buf = read_sha1_file(tag->object.sha1, &type, &size);
|
||||
if (!buf)
|
||||
die ("Could not read tag %s", sha1_to_hex(tag->object.sha1));
|
||||
message = memmem(buf, size, "\n\n", 2);
|
||||
if (message) {
|
||||
message += 2;
|
||||
message_size = strlen(message);
|
||||
}
|
||||
tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8);
|
||||
if (!tagger)
|
||||
die ("No tagger for tag %s", sha1_to_hex(tag->object.sha1));
|
||||
tagger++;
|
||||
tagger_end = strchrnul(tagger, '\n');
|
||||
|
||||
/* handle signed tags */
|
||||
if (message) {
|
||||
const char *signature = strstr(message,
|
||||
"\n-----BEGIN PGP SIGNATURE-----\n");
|
||||
if (signature)
|
||||
switch(signed_tag_mode) {
|
||||
case ABORT:
|
||||
die ("Encountered signed tag %s; use "
|
||||
"--signed-tag=<mode> to handle it.",
|
||||
sha1_to_hex(tag->object.sha1));
|
||||
case WARN:
|
||||
warning ("Exporting signed tag %s",
|
||||
sha1_to_hex(tag->object.sha1));
|
||||
/* fallthru */
|
||||
case VERBATIM:
|
||||
break;
|
||||
case STRIP:
|
||||
message_size = signature + 1 - message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prefixcmp(name, "refs/tags/"))
|
||||
name += 10;
|
||||
printf("tag %s\nfrom :%d\n%.*s\ndata %d\n%.*s\n",
|
||||
name, get_object_mark(tag->tagged),
|
||||
(int)(tagger_end - tagger), tagger,
|
||||
(int)message_size, (int)message_size, message ? message : "");
|
||||
}
|
||||
|
||||
static void get_tags_and_duplicates(struct object_array *pending,
|
||||
struct path_list *extra_refs)
|
||||
{
|
||||
struct tag *tag;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pending->nr; i++) {
|
||||
struct object_array_entry *e = pending->objects + i;
|
||||
unsigned char sha1[20];
|
||||
struct commit *commit = commit;
|
||||
char *full_name;
|
||||
|
||||
if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
|
||||
continue;
|
||||
|
||||
switch (e->item->type) {
|
||||
case OBJ_COMMIT:
|
||||
commit = (struct commit *)e->item;
|
||||
break;
|
||||
case OBJ_TAG:
|
||||
tag = (struct tag *)e->item;
|
||||
while (tag && tag->object.type == OBJ_TAG) {
|
||||
path_list_insert(full_name, extra_refs)->util = tag;
|
||||
tag = (struct tag *)tag->tagged;
|
||||
}
|
||||
if (!tag)
|
||||
die ("Tag %s points nowhere?", e->name);
|
||||
switch(tag->object.type) {
|
||||
case OBJ_COMMIT:
|
||||
commit = (struct commit *)tag;
|
||||
break;
|
||||
case OBJ_BLOB:
|
||||
handle_object(tag->object.sha1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
die ("Unexpected object of type %s",
|
||||
typename(e->item->type));
|
||||
}
|
||||
if (commit->util)
|
||||
/* more than one name for the same object */
|
||||
path_list_insert(full_name, extra_refs)->util = commit;
|
||||
else
|
||||
commit->util = full_name;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_tags_and_duplicates(struct path_list *extra_refs)
|
||||
{
|
||||
struct commit *commit;
|
||||
int i;
|
||||
|
||||
for (i = extra_refs->nr - 1; i >= 0; i--) {
|
||||
const char *name = extra_refs->items[i].path;
|
||||
struct object *object = extra_refs->items[i].util;
|
||||
switch (object->type) {
|
||||
case OBJ_TAG:
|
||||
handle_tag(name, (struct tag *)object);
|
||||
break;
|
||||
case OBJ_COMMIT:
|
||||
/* create refs pointing to already seen commits */
|
||||
commit = (struct commit *)object;
|
||||
printf("reset %s\nfrom :%d\n\n", name,
|
||||
get_object_mark(&commit->object));
|
||||
show_progress();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cmd_fast_export(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct rev_info revs;
|
||||
struct object_array commits = { 0, 0, NULL };
|
||||
struct path_list extra_refs = { NULL, 0, 0, 0 };
|
||||
struct commit *commit;
|
||||
struct option options[] = {
|
||||
OPT_INTEGER(0, "progress", &progress,
|
||||
"show progress after <n> objects"),
|
||||
OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
|
||||
"select handling of signed tags",
|
||||
parse_opt_signed_tag_mode),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
/* we handle encodings */
|
||||
git_config(git_default_config);
|
||||
|
||||
init_revisions(&revs, prefix);
|
||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||
argc = parse_options(argc, argv, options, fast_export_usage, 0);
|
||||
if (argc > 1)
|
||||
usage_with_options (fast_export_usage, options);
|
||||
|
||||
get_tags_and_duplicates(&revs.pending, &extra_refs);
|
||||
|
||||
prepare_revision_walk(&revs);
|
||||
revs.diffopt.format_callback = show_filemodify;
|
||||
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
|
||||
while ((commit = get_revision(&revs))) {
|
||||
if (has_unshown_parent(commit)) {
|
||||
struct commit_list *parent = commit->parents;
|
||||
add_object_array(&commit->object, NULL, &commits);
|
||||
for (; parent; parent = parent->next)
|
||||
if (!parent->item->util)
|
||||
parent->item->util = commit->util;
|
||||
}
|
||||
else {
|
||||
handle_commit(commit, &revs);
|
||||
handle_tail(&commits, &revs);
|
||||
}
|
||||
}
|
||||
|
||||
handle_tags_and_duplicates(&extra_refs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upl
|
||||
|
||||
static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
|
||||
static const char *depth;
|
||||
static char *default_rla = NULL;
|
||||
static struct strbuf default_rla = STRBUF_INIT;
|
||||
static struct transport *transport;
|
||||
|
||||
static void unlock_pack(void)
|
||||
@@ -142,7 +142,7 @@ static int s_update_ref(const char *action,
|
||||
static struct ref_lock *lock;
|
||||
|
||||
if (!rla)
|
||||
rla = default_rla;
|
||||
rla = default_rla.buf;
|
||||
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
|
||||
lock = lock_any_ref_for_update(ref->name,
|
||||
check_old ? ref->old_sha1 : NULL, 0);
|
||||
@@ -543,16 +543,19 @@ static void set_option(const char *name, const char *value)
|
||||
int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct remote *remote;
|
||||
int i, j, rla_offset;
|
||||
int i;
|
||||
static const char **refs = NULL;
|
||||
int ref_nr = 0;
|
||||
int cmd_len = 0;
|
||||
const char *upload_pack = NULL;
|
||||
int keep = 0;
|
||||
|
||||
/* Record the command line for the reflog */
|
||||
strbuf_addstr(&default_rla, "fetch");
|
||||
for (i = 1; i < argc; i++)
|
||||
strbuf_addf(&default_rla, " %s", argv[i]);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
cmd_len += strlen(arg);
|
||||
|
||||
if (arg[0] != '-')
|
||||
break;
|
||||
@@ -613,17 +616,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
usage(fetch_usage);
|
||||
}
|
||||
|
||||
for (j = i; j < argc; j++)
|
||||
cmd_len += strlen(argv[j]);
|
||||
|
||||
default_rla = xmalloc(cmd_len + 5 + argc + 1);
|
||||
sprintf(default_rla, "fetch");
|
||||
rla_offset = strlen(default_rla);
|
||||
for (j = 1; j < argc; j++) {
|
||||
sprintf(default_rla + rla_offset, " %s", argv[j]);
|
||||
rla_offset += strlen(argv[j]) + 1;
|
||||
}
|
||||
|
||||
if (i == argc)
|
||||
remote = remote_get(NULL);
|
||||
else
|
||||
|
||||
@@ -38,28 +38,28 @@ static const char *tag_modified = "";
|
||||
|
||||
|
||||
/*
|
||||
* Match a pathspec against a filename. The first "len" characters
|
||||
* Match a pathspec against a filename. The first "skiplen" characters
|
||||
* are the common prefix
|
||||
*/
|
||||
static int match(const char **spec, char *ps_matched,
|
||||
const char *filename, int len)
|
||||
int pathspec_match(const char **spec, char *ps_matched,
|
||||
const char *filename, int skiplen)
|
||||
{
|
||||
const char *m;
|
||||
|
||||
while ((m = *spec++) != NULL) {
|
||||
int matchlen = strlen(m + len);
|
||||
int matchlen = strlen(m + skiplen);
|
||||
|
||||
if (!matchlen)
|
||||
goto matched;
|
||||
if (!strncmp(m + len, filename + len, matchlen)) {
|
||||
if (m[len + matchlen - 1] == '/')
|
||||
if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
|
||||
if (m[skiplen + matchlen - 1] == '/')
|
||||
goto matched;
|
||||
switch (filename[len + matchlen]) {
|
||||
switch (filename[skiplen + matchlen]) {
|
||||
case '/': case '\0':
|
||||
goto matched;
|
||||
}
|
||||
}
|
||||
if (!fnmatch(m + len, filename + len, 0))
|
||||
if (!fnmatch(m + skiplen, filename + skiplen, 0))
|
||||
goto matched;
|
||||
if (ps_matched)
|
||||
ps_matched++;
|
||||
@@ -80,7 +80,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
||||
if (len >= ent->len)
|
||||
die("git-ls-files: internal error - directory entry not superset of prefix");
|
||||
|
||||
if (pathspec && !match(pathspec, ps_matched, ent->name, len))
|
||||
if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
|
||||
return;
|
||||
|
||||
fputs(tag, stdout);
|
||||
@@ -185,7 +185,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
|
||||
if (len >= ce_namelen(ce))
|
||||
die("git-ls-files: internal error - cache entry not superset of prefix");
|
||||
|
||||
if (pathspec && !match(pathspec, ps_matched, ce->name, len))
|
||||
if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
|
||||
return;
|
||||
|
||||
if (tag && *tag && show_valid_bit &&
|
||||
@@ -331,7 +331,7 @@ static const char *verify_pathspec(const char *prefix)
|
||||
* that were given from the command line. We are not
|
||||
* going to write this index out.
|
||||
*/
|
||||
static void overlay_tree(const char *tree_name, const char *prefix)
|
||||
void overlay_tree_on_cache(const char *tree_name, const char *prefix)
|
||||
{
|
||||
struct tree *tree;
|
||||
unsigned char sha1[20];
|
||||
@@ -384,6 +384,42 @@ static void overlay_tree(const char *tree_name, const char *prefix)
|
||||
}
|
||||
}
|
||||
|
||||
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
|
||||
{
|
||||
/*
|
||||
* Make sure all pathspec matched; otherwise it is an error.
|
||||
*/
|
||||
int num, errors = 0;
|
||||
for (num = 0; pathspec[num]; num++) {
|
||||
int other, found_dup;
|
||||
|
||||
if (ps_matched[num])
|
||||
continue;
|
||||
/*
|
||||
* The caller might have fed identical pathspec
|
||||
* twice. Do not barf on such a mistake.
|
||||
*/
|
||||
for (found_dup = other = 0;
|
||||
!found_dup && pathspec[other];
|
||||
other++) {
|
||||
if (other == num || !ps_matched[other])
|
||||
continue;
|
||||
if (!strcmp(pathspec[other], pathspec[num]))
|
||||
/*
|
||||
* Ok, we have a match already.
|
||||
*/
|
||||
found_dup = 1;
|
||||
}
|
||||
if (found_dup)
|
||||
continue;
|
||||
|
||||
error("pathspec '%s' did not match any file(s) known to git.",
|
||||
pathspec[num] + prefix_offset);
|
||||
errors++;
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
static const char ls_files_usage[] =
|
||||
"git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
|
||||
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
|
||||
@@ -568,47 +604,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
||||
*/
|
||||
if (show_stage || show_unmerged)
|
||||
die("ls-files --with-tree is incompatible with -s or -u");
|
||||
overlay_tree(with_tree, prefix);
|
||||
overlay_tree_on_cache(with_tree, prefix);
|
||||
}
|
||||
show_files(&dir, prefix);
|
||||
|
||||
if (ps_matched) {
|
||||
/* We need to make sure all pathspec matched otherwise
|
||||
* it is an error.
|
||||
*/
|
||||
int num, errors = 0;
|
||||
for (num = 0; pathspec[num]; num++) {
|
||||
int other, found_dup;
|
||||
|
||||
if (ps_matched[num])
|
||||
continue;
|
||||
/*
|
||||
* The caller might have fed identical pathspec
|
||||
* twice. Do not barf on such a mistake.
|
||||
*/
|
||||
for (found_dup = other = 0;
|
||||
!found_dup && pathspec[other];
|
||||
other++) {
|
||||
if (other == num || !ps_matched[other])
|
||||
continue;
|
||||
if (!strcmp(pathspec[other], pathspec[num]))
|
||||
/*
|
||||
* Ok, we have a match already.
|
||||
*/
|
||||
found_dup = 1;
|
||||
}
|
||||
if (found_dup)
|
||||
continue;
|
||||
|
||||
error("pathspec '%s' did not match any file(s) known to git.",
|
||||
pathspec[num] + prefix_offset);
|
||||
errors++;
|
||||
}
|
||||
|
||||
if (errors)
|
||||
int bad;
|
||||
bad = report_path_error(ps_matched, pathspec, prefix_offset);
|
||||
if (bad)
|
||||
fprintf(stderr, "Did you forget to 'git add'?\n");
|
||||
|
||||
return errors ? 1 : 0;
|
||||
return bad ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -7,15 +7,24 @@
|
||||
|
||||
static const char prune_usage[] = "git-prune [-n]";
|
||||
static int show_only;
|
||||
static unsigned long expire;
|
||||
|
||||
static int prune_object(char *path, const char *filename, const unsigned char *sha1)
|
||||
{
|
||||
const char *fullpath = mkpath("%s/%s", path, filename);
|
||||
if (expire) {
|
||||
struct stat st;
|
||||
if (lstat(fullpath, &st))
|
||||
return error("Could not stat '%s'", fullpath);
|
||||
if (st.st_mtime > expire)
|
||||
return 0;
|
||||
}
|
||||
if (show_only) {
|
||||
enum object_type type = sha1_object_info(sha1, NULL);
|
||||
printf("%s %s\n", sha1_to_hex(sha1),
|
||||
(type > 0) ? typename(type) : "unknown");
|
||||
} else
|
||||
unlink(mkpath("%s/%s", path, filename));
|
||||
unlink(fullpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -85,6 +94,16 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
|
||||
show_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--expire")) {
|
||||
if (++i < argc) {
|
||||
expire = approxidate(argv[i]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (!prefixcmp(arg, "--expire=")) {
|
||||
expire = approxidate(arg + 9);
|
||||
continue;
|
||||
}
|
||||
usage(prune_usage);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,15 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
|
||||
keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0);
|
||||
|
||||
strbuf_addf(&parsed, " --");
|
||||
sq_quote_argv(&parsed, argv, argc, 0);
|
||||
sq_quote_argv(&parsed, argv, 0);
|
||||
puts(parsed.buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,27 @@ static int merge_recursive(const char *base_sha1,
|
||||
return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static char *help_msg(const unsigned char *sha1)
|
||||
{
|
||||
static char helpbuf[1024];
|
||||
char *msg = getenv("GIT_CHERRY_PICK_HELP");
|
||||
|
||||
if (msg)
|
||||
return msg;
|
||||
|
||||
strcpy(helpbuf, " After resolving the conflicts,\n"
|
||||
"mark the corrected paths with 'git add <paths>' "
|
||||
"or 'git rm <paths>' and commit the result.");
|
||||
|
||||
if (action == CHERRY_PICK) {
|
||||
sprintf(helpbuf + strlen(helpbuf),
|
||||
"\nWhen commiting, use the option "
|
||||
"'-c %s' to retain authorship and message.",
|
||||
find_unique_abbrev(sha1, DEFAULT_ABBREV));
|
||||
}
|
||||
return helpbuf;
|
||||
}
|
||||
|
||||
static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
{
|
||||
unsigned char head[20];
|
||||
@@ -352,16 +373,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
}
|
||||
if (close(msg_fd) || commit_lock_file(&msg_file) < 0)
|
||||
die ("Error wrapping up %s", defmsg);
|
||||
fprintf(stderr, "Automatic %s failed. "
|
||||
"After resolving the conflicts,\n"
|
||||
"mark the corrected paths with 'git add <paths>' "
|
||||
"and commit the result.\n", me);
|
||||
if (action == CHERRY_PICK) {
|
||||
fprintf(stderr, "When commiting, use the option "
|
||||
"'-c %s' to retain authorship and message.\n",
|
||||
find_unique_abbrev(commit->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
}
|
||||
fprintf(stderr, "Automatic %s failed.%s\n",
|
||||
me, help_msg(commit->object.sha1));
|
||||
exit(1);
|
||||
}
|
||||
if (close(msg_fd) || commit_lock_file(&msg_file) < 0)
|
||||
|
||||
@@ -14,6 +14,7 @@ int cmd_runstatus(int argc, const char **argv, const char *prefix)
|
||||
|
||||
git_config(git_status_config);
|
||||
wt_status_prepare(&s);
|
||||
s.prefix = prefix;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--color"))
|
||||
|
||||
185
builtin-tag.c
185
builtin-tag.c
@@ -11,17 +11,21 @@
|
||||
#include "refs.h"
|
||||
#include "tag.h"
|
||||
#include "run-command.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char builtin_tag_usage[] =
|
||||
"git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
|
||||
static const char * const git_tag_usage[] = {
|
||||
"git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
|
||||
"git-tag -d <tagname>...",
|
||||
"git-tag [-n [<num>]] -l [<pattern>]",
|
||||
"git-tag -v <tagname>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char signingkey[1000];
|
||||
|
||||
static void launch_editor(const char *path, struct strbuf *buffer)
|
||||
void launch_editor(const char *path, struct strbuf *buffer, const char *const *env)
|
||||
{
|
||||
const char *editor, *terminal;
|
||||
struct child_process child;
|
||||
const char *args[3];
|
||||
|
||||
editor = getenv("GIT_EDITOR");
|
||||
if (!editor && editor_program)
|
||||
@@ -42,14 +46,12 @@ static void launch_editor(const char *path, struct strbuf *buffer)
|
||||
if (!editor)
|
||||
editor = "vi";
|
||||
|
||||
memset(&child, 0, sizeof(child));
|
||||
child.argv = args;
|
||||
args[0] = editor;
|
||||
args[1] = path;
|
||||
args[2] = NULL;
|
||||
if (strcmp(editor, ":")) {
|
||||
const char *args[] = { editor, path, NULL };
|
||||
|
||||
if (run_command(&child))
|
||||
die("There was a problem with the editor %s.", editor);
|
||||
if (run_command_v_opt_cd_env(args, 0, NULL, env))
|
||||
die("There was a problem with the editor %s.", editor);
|
||||
}
|
||||
|
||||
if (strbuf_read_file(buffer, path, 0) < 0)
|
||||
die("could not read message file '%s': %s",
|
||||
@@ -276,7 +278,7 @@ static void write_tag_body(int fd, const unsigned char *sha1)
|
||||
|
||||
static void create_tag(const unsigned char *object, const char *tag,
|
||||
struct strbuf *buf, int message, int sign,
|
||||
unsigned char *prev, unsigned char *result)
|
||||
unsigned char *prev, unsigned char *result)
|
||||
{
|
||||
enum object_type type;
|
||||
char header_buf[1024];
|
||||
@@ -316,7 +318,7 @@ static void create_tag(const unsigned char *object, const char *tag,
|
||||
write_or_die(fd, tag_template, strlen(tag_template));
|
||||
close(fd);
|
||||
|
||||
launch_editor(path, buf);
|
||||
launch_editor(path, buf, NULL);
|
||||
|
||||
unlink(path);
|
||||
free(path);
|
||||
@@ -335,105 +337,101 @@ static void create_tag(const unsigned char *object, const char *tag,
|
||||
die("unable to write tag file");
|
||||
}
|
||||
|
||||
struct msg_arg {
|
||||
int given;
|
||||
struct strbuf buf;
|
||||
};
|
||||
|
||||
static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct msg_arg *msg = opt->value;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
if (msg->buf.len)
|
||||
strbuf_addstr(&(msg->buf), "\n\n");
|
||||
strbuf_addstr(&(msg->buf), arg);
|
||||
msg->given = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct strbuf buf;
|
||||
unsigned char object[20], prev[20];
|
||||
int annotate = 0, sign = 0, force = 0, lines = 0, message = 0;
|
||||
char ref[PATH_MAX];
|
||||
const char *object_ref, *tag;
|
||||
int i;
|
||||
struct ref_lock *lock;
|
||||
|
||||
int annotate = 0, sign = 0, force = 0, lines = 0,
|
||||
delete = 0, verify = 0;
|
||||
char *list = NULL, *msgfile = NULL, *keyid = NULL;
|
||||
const char *no_pattern = "NO_PATTERN";
|
||||
struct msg_arg msg = { 0, STRBUF_INIT };
|
||||
struct option options[] = {
|
||||
{ OPTION_STRING, 'l', NULL, &list, "pattern", "list tag names",
|
||||
PARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern },
|
||||
{ OPTION_INTEGER, 'n', NULL, &lines, NULL,
|
||||
"print n lines of each tag message",
|
||||
PARSE_OPT_OPTARG, NULL, 1 },
|
||||
OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
|
||||
OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
|
||||
|
||||
OPT_GROUP("Tag creation options"),
|
||||
OPT_BOOLEAN('a', NULL, &annotate,
|
||||
"annotated tag, needs a message"),
|
||||
OPT_CALLBACK('m', NULL, &msg, "msg",
|
||||
"message for the tag", parse_msg_arg),
|
||||
OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
|
||||
OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
|
||||
OPT_STRING('u', NULL, &keyid, "key-id",
|
||||
"use another key to sign the tag"),
|
||||
OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
git_config(git_tag_config);
|
||||
|
||||
argc = parse_options(argc, argv, options, git_tag_usage, 0);
|
||||
|
||||
if (sign)
|
||||
annotate = 1;
|
||||
|
||||
if (list)
|
||||
return list_tags(list == no_pattern ? NULL : list, lines);
|
||||
if (delete)
|
||||
return for_each_tag_name(argv, delete_tag);
|
||||
if (verify)
|
||||
return for_each_tag_name(argv, verify_tag);
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (arg[0] != '-')
|
||||
break;
|
||||
if (!strcmp(arg, "-a")) {
|
||||
annotate = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-s")) {
|
||||
annotate = 1;
|
||||
sign = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-f")) {
|
||||
force = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-n")) {
|
||||
if (i + 1 == argc || *argv[i + 1] == '-')
|
||||
/* no argument */
|
||||
lines = 1;
|
||||
else
|
||||
lines = isdigit(*argv[++i]) ?
|
||||
atoi(argv[i]) : 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-m")) {
|
||||
annotate = 1;
|
||||
i++;
|
||||
if (i == argc)
|
||||
die("option -m needs an argument.");
|
||||
if (message)
|
||||
die("only one -F or -m option is allowed.");
|
||||
strbuf_addstr(&buf, argv[i]);
|
||||
message = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-F")) {
|
||||
annotate = 1;
|
||||
i++;
|
||||
if (i == argc)
|
||||
die("option -F needs an argument.");
|
||||
if (message)
|
||||
die("only one -F or -m option is allowed.");
|
||||
|
||||
if (!strcmp(argv[i], "-")) {
|
||||
if (msg.given || msgfile) {
|
||||
if (msg.given && msgfile)
|
||||
die("only one -F or -m option is allowed.");
|
||||
annotate = 1;
|
||||
if (msg.given)
|
||||
strbuf_addbuf(&buf, &(msg.buf));
|
||||
else {
|
||||
if (!strcmp(msgfile, "-")) {
|
||||
if (strbuf_read(&buf, 0, 1024) < 0)
|
||||
die("cannot read %s", argv[i]);
|
||||
die("cannot read %s", msgfile);
|
||||
} else {
|
||||
if (strbuf_read_file(&buf, argv[i], 1024) < 0)
|
||||
if (strbuf_read_file(&buf, msgfile, 1024) < 0)
|
||||
die("could not open or read '%s': %s",
|
||||
argv[i], strerror(errno));
|
||||
msgfile, strerror(errno));
|
||||
}
|
||||
message = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-u")) {
|
||||
annotate = 1;
|
||||
sign = 1;
|
||||
i++;
|
||||
if (i == argc)
|
||||
die("option -u needs an argument.");
|
||||
if (strlcpy(signingkey, argv[i], sizeof(signingkey))
|
||||
>= sizeof(signingkey))
|
||||
die("argument to option -u too long");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-l"))
|
||||
return list_tags(argv[i + 1], lines);
|
||||
if (!strcmp(arg, "-d"))
|
||||
return for_each_tag_name(argv + i + 1, delete_tag);
|
||||
if (!strcmp(arg, "-v"))
|
||||
return for_each_tag_name(argv + i + 1, verify_tag);
|
||||
usage(builtin_tag_usage);
|
||||
}
|
||||
|
||||
if (i == argc) {
|
||||
if (argc == 0) {
|
||||
if (annotate)
|
||||
usage(builtin_tag_usage);
|
||||
usage_with_options(git_tag_usage, options);
|
||||
return list_tags(NULL, lines);
|
||||
}
|
||||
tag = argv[i++];
|
||||
tag = argv[0];
|
||||
|
||||
object_ref = i < argc ? argv[i] : "HEAD";
|
||||
if (i + 1 < argc)
|
||||
object_ref = argc == 2 ? argv[1] : "HEAD";
|
||||
if (argc > 2)
|
||||
die("too many params");
|
||||
|
||||
if (get_sha1(object_ref, object))
|
||||
@@ -450,7 +448,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
die("tag '%s' already exists", tag);
|
||||
|
||||
if (annotate)
|
||||
create_tag(object, tag, &buf, message, sign, prev, object);
|
||||
create_tag(object, tag, &buf, msg.given || msgfile,
|
||||
sign, prev, object);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, prev, 0);
|
||||
if (!lock)
|
||||
|
||||
@@ -25,6 +25,7 @@ extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
|
||||
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_clean(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_commit(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_describe(int argc, const char **argv, const char *prefix);
|
||||
@@ -32,6 +33,7 @@ extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_diff(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_fetch(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_fetch__tool(int argc, const char **argv, const char *prefix);
|
||||
@@ -71,11 +73,11 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_revert(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_rm(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_show(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_status(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_tag(int argc, const char **argv, const char *prefix);
|
||||
|
||||
22
cache.h
22
cache.h
@@ -192,6 +192,13 @@ enum object_type {
|
||||
OBJ_MAX,
|
||||
};
|
||||
|
||||
static inline enum object_type object_type(unsigned int mode)
|
||||
{
|
||||
return S_ISDIR(mode) ? OBJ_TREE :
|
||||
S_ISGITLINK(mode) ? OBJ_COMMIT :
|
||||
OBJ_BLOB;
|
||||
}
|
||||
|
||||
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
|
||||
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
|
||||
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
|
||||
@@ -420,6 +427,10 @@ extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *
|
||||
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
|
||||
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
|
||||
|
||||
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
|
||||
extern const char *ref_rev_parse_rules[];
|
||||
extern const char *ref_fetch_rules[];
|
||||
|
||||
extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
|
||||
extern int validate_headref(const char *ref);
|
||||
|
||||
@@ -449,6 +460,7 @@ enum date_mode parse_date_format(const char *format);
|
||||
extern const char *git_author_info(int);
|
||||
extern const char *git_committer_info(int);
|
||||
extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
|
||||
extern const char *fmt_name(const char *name, const char *email);
|
||||
|
||||
struct checkout {
|
||||
const char *base_dir;
|
||||
@@ -617,17 +629,25 @@ extern void alloc_report(void);
|
||||
|
||||
/* trace.c */
|
||||
extern void trace_printf(const char *format, ...);
|
||||
extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
|
||||
extern void trace_argv_printf(const char **argv, const char *format, ...);
|
||||
|
||||
/* convert.c */
|
||||
/* returns 1 if *dst was used */
|
||||
extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
|
||||
extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
|
||||
|
||||
/* add */
|
||||
void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
|
||||
|
||||
/* diff.c */
|
||||
extern int diff_auto_refresh_index;
|
||||
|
||||
/* match-trees.c */
|
||||
void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, 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);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
|
||||
32
color.c
32
color.c
@@ -118,21 +118,27 @@ bad:
|
||||
|
||||
int git_config_colorbool(const char *var, const char *value)
|
||||
{
|
||||
if (!value)
|
||||
return 1;
|
||||
if (!strcasecmp(value, "auto")) {
|
||||
if (isatty(1) || (pager_in_use && pager_use_color)) {
|
||||
char *term = getenv("TERM");
|
||||
if (term && strcmp(term, "dumb"))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
if (value) {
|
||||
if (!strcasecmp(value, "never"))
|
||||
return 0;
|
||||
if (!strcasecmp(value, "always"))
|
||||
return 1;
|
||||
if (!strcasecmp(value, "auto"))
|
||||
goto auto_color;
|
||||
}
|
||||
if (!strcasecmp(value, "never"))
|
||||
|
||||
/* Missing or explicit false to turn off colorization */
|
||||
if (!git_config_bool(var, value))
|
||||
return 0;
|
||||
if (!strcasecmp(value, "always"))
|
||||
return 1;
|
||||
return git_config_bool(var, value);
|
||||
|
||||
/* any normal truth value defaults to 'auto' */
|
||||
auto_color:
|
||||
if (isatty(1) || (pager_in_use && pager_use_color)) {
|
||||
char *term = getenv("TERM");
|
||||
if (term && strcmp(term, "dumb"))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
|
||||
|
||||
130
command-list.txt
Normal file
130
command-list.txt
Normal file
@@ -0,0 +1,130 @@
|
||||
# List of known git commands.
|
||||
# command name category [deprecated] [common]
|
||||
git-add mainporcelain common
|
||||
git-am mainporcelain
|
||||
git-annotate ancillaryinterrogators
|
||||
git-apply plumbingmanipulators
|
||||
git-archimport foreignscminterface
|
||||
git-archive mainporcelain
|
||||
git-bisect mainporcelain common
|
||||
git-blame ancillaryinterrogators
|
||||
git-branch mainporcelain common
|
||||
git-bundle mainporcelain
|
||||
git-cat-file plumbinginterrogators
|
||||
git-check-attr purehelpers
|
||||
git-checkout mainporcelain common
|
||||
git-checkout-index plumbingmanipulators
|
||||
git-check-ref-format purehelpers
|
||||
git-cherry ancillaryinterrogators
|
||||
git-cherry-pick mainporcelain
|
||||
git-citool mainporcelain
|
||||
git-clean mainporcelain
|
||||
git-clone mainporcelain common
|
||||
git-commit mainporcelain common
|
||||
git-commit-tree plumbingmanipulators
|
||||
git-config ancillarymanipulators
|
||||
git-count-objects ancillaryinterrogators
|
||||
git-cvsexportcommit foreignscminterface
|
||||
git-cvsimport foreignscminterface
|
||||
git-cvsserver foreignscminterface
|
||||
git-daemon synchingrepositories
|
||||
git-describe mainporcelain
|
||||
git-diff mainporcelain common
|
||||
git-diff-files plumbinginterrogators
|
||||
git-diff-index plumbinginterrogators
|
||||
git-diff-tree plumbinginterrogators
|
||||
git-fast-export ancillarymanipulators
|
||||
git-fast-import ancillarymanipulators
|
||||
git-fetch mainporcelain common
|
||||
git-fetch-pack synchingrepositories
|
||||
git-filter-branch ancillarymanipulators
|
||||
git-fmt-merge-msg purehelpers
|
||||
git-for-each-ref plumbinginterrogators
|
||||
git-format-patch mainporcelain
|
||||
git-fsck ancillaryinterrogators
|
||||
git-gc mainporcelain
|
||||
git-get-tar-commit-id ancillaryinterrogators
|
||||
git-grep mainporcelain common
|
||||
git-gui mainporcelain
|
||||
git-hash-object plumbingmanipulators
|
||||
git-help ancillaryinterrogators
|
||||
git-http-fetch synchelpers
|
||||
git-http-push synchelpers
|
||||
git-imap-send foreignscminterface
|
||||
git-index-pack plumbingmanipulators
|
||||
git-init mainporcelain common
|
||||
git-instaweb ancillaryinterrogators
|
||||
gitk mainporcelain
|
||||
git-log mainporcelain common
|
||||
git-lost-found ancillarymanipulators deprecated
|
||||
git-ls-files plumbinginterrogators
|
||||
git-ls-remote plumbinginterrogators
|
||||
git-ls-tree plumbinginterrogators
|
||||
git-mailinfo purehelpers
|
||||
git-mailsplit purehelpers
|
||||
git-merge mainporcelain common
|
||||
git-merge-base plumbinginterrogators
|
||||
git-merge-file plumbingmanipulators
|
||||
git-merge-index plumbingmanipulators
|
||||
git-merge-one-file purehelpers
|
||||
git-mergetool ancillarymanipulators
|
||||
git-merge-tree ancillaryinterrogators
|
||||
git-mktag plumbingmanipulators
|
||||
git-mktree plumbingmanipulators
|
||||
git-mv mainporcelain common
|
||||
git-name-rev plumbinginterrogators
|
||||
git-pack-objects plumbingmanipulators
|
||||
git-pack-redundant plumbinginterrogators
|
||||
git-pack-refs ancillarymanipulators
|
||||
git-parse-remote synchelpers
|
||||
git-patch-id purehelpers
|
||||
git-peek-remote purehelpers deprecated
|
||||
git-prune ancillarymanipulators
|
||||
git-prune-packed plumbingmanipulators
|
||||
git-pull mainporcelain common
|
||||
git-push mainporcelain common
|
||||
git-quiltimport foreignscminterface
|
||||
git-read-tree plumbingmanipulators
|
||||
git-rebase mainporcelain common
|
||||
git-receive-pack synchelpers
|
||||
git-reflog ancillarymanipulators
|
||||
git-relink ancillarymanipulators
|
||||
git-remote ancillarymanipulators
|
||||
git-repack ancillarymanipulators
|
||||
git-request-pull foreignscminterface
|
||||
git-rerere ancillaryinterrogators
|
||||
git-reset mainporcelain common
|
||||
git-revert mainporcelain
|
||||
git-rev-list plumbinginterrogators
|
||||
git-rev-parse ancillaryinterrogators
|
||||
git-rm mainporcelain common
|
||||
git-runstatus ancillaryinterrogators
|
||||
git-send-email foreignscminterface
|
||||
git-send-pack synchingrepositories
|
||||
git-shell synchelpers
|
||||
git-shortlog mainporcelain
|
||||
git-show mainporcelain common
|
||||
git-show-branch ancillaryinterrogators
|
||||
git-show-index plumbinginterrogators
|
||||
git-show-ref plumbinginterrogators
|
||||
git-sh-setup purehelpers
|
||||
git-stash mainporcelain
|
||||
git-status mainporcelain common
|
||||
git-stripspace purehelpers
|
||||
git-submodule mainporcelain
|
||||
git-svn foreignscminterface
|
||||
git-symbolic-ref plumbingmanipulators
|
||||
git-tag mainporcelain common
|
||||
git-tar-tree plumbinginterrogators deprecated
|
||||
git-unpack-file plumbinginterrogators
|
||||
git-unpack-objects plumbingmanipulators
|
||||
git-update-index plumbingmanipulators
|
||||
git-update-ref plumbingmanipulators
|
||||
git-update-server-info synchingrepositories
|
||||
git-upload-archive synchelpers
|
||||
git-upload-pack synchelpers
|
||||
git-var plumbinginterrogators
|
||||
git-verify-pack plumbinginterrogators
|
||||
git-verify-tag ancillaryinterrogators
|
||||
git-whatchanged ancillaryinterrogators
|
||||
git-write-tree plumbingmanipulators
|
||||
3
commit.h
3
commit.h
@@ -113,8 +113,7 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
|
||||
|
||||
int in_merge_bases(struct commit *, struct commit **, int);
|
||||
|
||||
extern int interactive_add(void);
|
||||
extern void add_files_to_cache(int verbose, const char *prefix, const char **files);
|
||||
extern int interactive_add(int argc, const char **argv, const char *prefix);
|
||||
extern int rerere(void);
|
||||
|
||||
static inline int single_parent(struct commit *commit)
|
||||
|
||||
@@ -31,7 +31,7 @@ THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}"
|
||||
NEXT_INDEX="$GIT_DIR/next-index$$"
|
||||
rm -f "$NEXT_INDEX"
|
||||
save_index () {
|
||||
ln "$THIS_INDEX" "$NEXT_INDEX"
|
||||
cp -p "$THIS_INDEX" "$NEXT_INDEX"
|
||||
}
|
||||
|
||||
run_status () {
|
||||
@@ -74,6 +74,7 @@ trap '
|
||||
|
||||
all=
|
||||
also=
|
||||
allow_empty=f
|
||||
interactive=
|
||||
only=
|
||||
logfile=
|
||||
@@ -114,6 +115,10 @@ do
|
||||
-a|--a|--al|--all)
|
||||
all=t
|
||||
;;
|
||||
--allo|--allow|--allow-|--allow-e|--allow-em|--allow-emp|\
|
||||
--allow-empt|--allow-empty)
|
||||
allow_empty=t
|
||||
;;
|
||||
--au=*|--aut=*|--auth=*|--autho=*|--author=*)
|
||||
force_author="${1#*=}"
|
||||
;;
|
||||
@@ -515,13 +520,18 @@ else
|
||||
# we need to check if there is anything to commit
|
||||
run_status >/dev/null
|
||||
fi
|
||||
if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
|
||||
then
|
||||
case "$allow_empty,$?,$PARENTS" in
|
||||
t,* | ?,0,* | ?,*,-p' '?*-p' '?*)
|
||||
# an explicit --allow-empty, or a merge commit can record the
|
||||
# same tree as its parent. Otherwise having commitable paths
|
||||
# is required.
|
||||
;;
|
||||
*)
|
||||
rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
|
||||
use_status_color=t
|
||||
run_status
|
||||
exit 1
|
||||
fi
|
||||
esac
|
||||
|
||||
case "$no_edit" in
|
||||
'')
|
||||
8
contrib/remotes2config.sh
Normal file → Executable file
8
contrib/remotes2config.sh
Normal file → Executable file
@@ -11,11 +11,11 @@ if [ -d "$GIT_DIR"/remotes ]; then
|
||||
{
|
||||
cd "$GIT_DIR"/remotes
|
||||
ls | while read f; do
|
||||
name=$(printf "$f" | tr -c "A-Za-z0-9" ".")
|
||||
name=$(printf "$f" | tr -c "A-Za-z0-9-" ".")
|
||||
sed -n \
|
||||
-e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \
|
||||
-e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \
|
||||
-e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \
|
||||
-e "s/^URL:[ ]*\(.*\)$/remote.$name.url \1 ./p" \
|
||||
-e "s/^Pull:[ ]*\(.*\)$/remote.$name.fetch \1 ^$ /p" \
|
||||
-e "s/^Push:[ ]*\(.*\)$/remote.$name.push \1 ^$ /p" \
|
||||
< "$f"
|
||||
done
|
||||
echo done
|
||||
|
||||
@@ -52,8 +52,10 @@ static int should_break(struct diff_filespec *src,
|
||||
* is the default.
|
||||
*/
|
||||
|
||||
if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
|
||||
return 0; /* leave symlink rename alone */
|
||||
if (S_ISREG(src->mode) != S_ISREG(dst->mode)) {
|
||||
*merge_score_p = (int)MAX_SCORE;
|
||||
return 1; /* even their types are different */
|
||||
}
|
||||
|
||||
if (src->sha1_valid && dst->sha1_valid &&
|
||||
!hashcmp(src->sha1, dst->sha1))
|
||||
@@ -168,11 +170,13 @@ void diffcore_break(int break_score)
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
int score;
|
||||
|
||||
/* We deal only with in-place edit of non directory.
|
||||
/*
|
||||
* We deal only with in-place edit of blobs.
|
||||
* We do not break anything else.
|
||||
*/
|
||||
if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) &&
|
||||
!S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) &&
|
||||
object_type(p->one->mode) == OBJ_BLOB &&
|
||||
object_type(p->two->mode) == OBJ_BLOB &&
|
||||
!strcmp(p->one->path, p->two->path)) {
|
||||
if (should_break(p->one, p->two,
|
||||
break_score, &score)) {
|
||||
|
||||
2
dir.c
2
dir.c
@@ -692,7 +692,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
|
||||
int file_exists(const char *f)
|
||||
{
|
||||
struct stat sb;
|
||||
return stat(f, &sb) == 0;
|
||||
return lstat(f, &sb) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -117,7 +117,7 @@ int execv_git_cmd(const char **argv)
|
||||
tmp = argv[0];
|
||||
argv[0] = cmd.buf;
|
||||
|
||||
trace_argv_printf(argv, -1, "trace: exec:");
|
||||
trace_argv_printf(argv, "trace: exec:");
|
||||
|
||||
/* execvp() can only ever return if it fails */
|
||||
execvp(cmd.buf, (char **)argv);
|
||||
|
||||
@@ -9,35 +9,8 @@ struct cmdname_help
|
||||
|
||||
static struct cmdname_help common_cmds[] = {"
|
||||
|
||||
/usr/bin/sort <<\EOF |
|
||||
add
|
||||
apply
|
||||
archive
|
||||
bisect
|
||||
branch
|
||||
checkout
|
||||
cherry-pick
|
||||
clone
|
||||
commit
|
||||
diff
|
||||
fetch
|
||||
grep
|
||||
init
|
||||
log
|
||||
merge
|
||||
mv
|
||||
prune
|
||||
pull
|
||||
push
|
||||
rebase
|
||||
reset
|
||||
revert
|
||||
rm
|
||||
show
|
||||
show-branch
|
||||
status
|
||||
tag
|
||||
EOF
|
||||
sed -n -e 's/^git-\([^ ]*\)[ ].* common.*/\1/p' command-list.txt |
|
||||
sort |
|
||||
while read cmd
|
||||
do
|
||||
sed -n '
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
use strict;
|
||||
|
||||
# command line options
|
||||
my $patch_mode;
|
||||
|
||||
sub run_cmd_pipe {
|
||||
if ($^O eq 'MSWin32') {
|
||||
my @invalid = grep {m/[":*]/} @_;
|
||||
@@ -37,14 +40,13 @@ sub list_untracked {
|
||||
chomp $_;
|
||||
$_;
|
||||
}
|
||||
run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @_);
|
||||
run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
|
||||
}
|
||||
|
||||
my $status_fmt = '%12s %12s %s';
|
||||
my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
|
||||
|
||||
# Returns list of hashes, contents of each of which are:
|
||||
# PRINT: print message
|
||||
# VALUE: pathname
|
||||
# BINARY: is a binary path
|
||||
# INDEX: is index different from HEAD?
|
||||
@@ -56,9 +58,17 @@ sub list_modified {
|
||||
my ($only) = @_;
|
||||
my (%data, @return);
|
||||
my ($add, $del, $adddel, $file);
|
||||
my @tracked = ();
|
||||
|
||||
if (@ARGV) {
|
||||
@tracked = map {
|
||||
chomp $_; $_;
|
||||
} run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV);
|
||||
return if (!@tracked);
|
||||
}
|
||||
|
||||
for (run_cmd_pipe(qw(git diff-index --cached
|
||||
--numstat --summary HEAD))) {
|
||||
--numstat --summary HEAD --), @tracked)) {
|
||||
if (($add, $del, $file) =
|
||||
/^([-\d]+) ([-\d]+) (.*)/) {
|
||||
my ($change, $bin);
|
||||
@@ -81,7 +91,7 @@ sub list_modified {
|
||||
}
|
||||
}
|
||||
|
||||
for (run_cmd_pipe(qw(git diff-files --numstat --summary))) {
|
||||
for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) {
|
||||
if (($add, $del, $file) =
|
||||
/^([-\d]+) ([-\d]+) (.*)/) {
|
||||
if (!exists $data{$file}) {
|
||||
@@ -122,8 +132,6 @@ sub list_modified {
|
||||
}
|
||||
push @return, +{
|
||||
VALUE => $_,
|
||||
PRINT => (sprintf $status_fmt,
|
||||
$it->{INDEX}, $it->{FILE}, $_),
|
||||
%$it,
|
||||
};
|
||||
}
|
||||
@@ -159,10 +167,96 @@ sub find_unique {
|
||||
return $found;
|
||||
}
|
||||
|
||||
# inserts string into trie and updates count for each character
|
||||
sub update_trie {
|
||||
my ($trie, $string) = @_;
|
||||
foreach (split //, $string) {
|
||||
$trie = $trie->{$_} ||= {COUNT => 0};
|
||||
$trie->{COUNT}++;
|
||||
}
|
||||
}
|
||||
|
||||
# returns an array of tuples (prefix, remainder)
|
||||
sub find_unique_prefixes {
|
||||
my @stuff = @_;
|
||||
my @return = ();
|
||||
|
||||
# any single prefix exceeding the soft limit is omitted
|
||||
# if any prefix exceeds the hard limit all are omitted
|
||||
# 0 indicates no limit
|
||||
my $soft_limit = 0;
|
||||
my $hard_limit = 3;
|
||||
|
||||
# build a trie modelling all possible options
|
||||
my %trie;
|
||||
foreach my $print (@stuff) {
|
||||
if ((ref $print) eq 'ARRAY') {
|
||||
$print = $print->[0];
|
||||
}
|
||||
elsif ((ref $print) eq 'HASH') {
|
||||
$print = $print->{VALUE};
|
||||
}
|
||||
update_trie(\%trie, $print);
|
||||
push @return, $print;
|
||||
}
|
||||
|
||||
# use the trie to find the unique prefixes
|
||||
for (my $i = 0; $i < @return; $i++) {
|
||||
my $ret = $return[$i];
|
||||
my @letters = split //, $ret;
|
||||
my %search = %trie;
|
||||
my ($prefix, $remainder);
|
||||
my $j;
|
||||
for ($j = 0; $j < @letters; $j++) {
|
||||
my $letter = $letters[$j];
|
||||
if ($search{$letter}{COUNT} == 1) {
|
||||
$prefix = substr $ret, 0, $j + 1;
|
||||
$remainder = substr $ret, $j + 1;
|
||||
last;
|
||||
}
|
||||
else {
|
||||
my $prefix = substr $ret, 0, $j;
|
||||
return ()
|
||||
if ($hard_limit && $j + 1 > $hard_limit);
|
||||
}
|
||||
%search = %{$search{$letter}};
|
||||
}
|
||||
if ($soft_limit && $j + 1 > $soft_limit) {
|
||||
$prefix = undef;
|
||||
$remainder = $ret;
|
||||
}
|
||||
$return[$i] = [$prefix, $remainder];
|
||||
}
|
||||
return @return;
|
||||
}
|
||||
|
||||
# filters out prefixes which have special meaning to list_and_choose()
|
||||
sub is_valid_prefix {
|
||||
my $prefix = shift;
|
||||
return (defined $prefix) &&
|
||||
!($prefix =~ /[\s,]/) && # separators
|
||||
!($prefix =~ /^-/) && # deselection
|
||||
!($prefix =~ /^\d+/) && # selection
|
||||
($prefix ne '*') && # "all" wildcard
|
||||
($prefix ne '?'); # prompt help
|
||||
}
|
||||
|
||||
# given a prefix/remainder tuple return a string with the prefix highlighted
|
||||
# for now use square brackets; later might use ANSI colors (underline, bold)
|
||||
sub highlight_prefix {
|
||||
my $prefix = shift;
|
||||
my $remainder = shift;
|
||||
return $remainder unless defined $prefix;
|
||||
return is_valid_prefix($prefix) ?
|
||||
"[$prefix]$remainder" :
|
||||
"$prefix$remainder";
|
||||
}
|
||||
|
||||
sub list_and_choose {
|
||||
my ($opts, @stuff) = @_;
|
||||
my (@chosen, @return);
|
||||
my $i;
|
||||
my @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY};
|
||||
|
||||
TOPLOOP:
|
||||
while (1) {
|
||||
@@ -177,13 +271,21 @@ sub list_and_choose {
|
||||
for ($i = 0; $i < @stuff; $i++) {
|
||||
my $chosen = $chosen[$i] ? '*' : ' ';
|
||||
my $print = $stuff[$i];
|
||||
if (ref $print) {
|
||||
if ((ref $print) eq 'ARRAY') {
|
||||
$print = $print->[0];
|
||||
}
|
||||
else {
|
||||
$print = $print->{PRINT};
|
||||
}
|
||||
my $ref = ref $print;
|
||||
my $highlighted = highlight_prefix(@{$prefixes[$i]})
|
||||
if @prefixes;
|
||||
if ($ref eq 'ARRAY') {
|
||||
$print = $highlighted || $print->[0];
|
||||
}
|
||||
elsif ($ref eq 'HASH') {
|
||||
my $value = $highlighted || $print->{VALUE};
|
||||
$print = sprintf($status_fmt,
|
||||
$print->{INDEX},
|
||||
$print->{FILE},
|
||||
$value);
|
||||
}
|
||||
else {
|
||||
$print = $highlighted || $print;
|
||||
}
|
||||
printf("%s%2d: %s", $chosen, $i+1, $print);
|
||||
if (($opts->{LIST_FLAT}) &&
|
||||
@@ -217,6 +319,12 @@ sub list_and_choose {
|
||||
}
|
||||
chomp $line;
|
||||
last if $line eq '';
|
||||
if ($line eq '?') {
|
||||
$opts->{SINGLETON} ?
|
||||
singleton_prompt_help_cmd() :
|
||||
prompt_help_cmd();
|
||||
next TOPLOOP;
|
||||
}
|
||||
for my $choice (split(/[\s,]+/, $line)) {
|
||||
my $choose = 1;
|
||||
my ($bottom, $top);
|
||||
@@ -252,7 +360,7 @@ sub list_and_choose {
|
||||
$chosen[$i] = $choose;
|
||||
}
|
||||
}
|
||||
last if ($opts->{IMMEDIATE});
|
||||
last if ($opts->{IMMEDIATE} || $line eq '*');
|
||||
}
|
||||
for ($i = 0; $i < @stuff; $i++) {
|
||||
if ($chosen[$i]) {
|
||||
@@ -262,6 +370,28 @@ sub list_and_choose {
|
||||
return @return;
|
||||
}
|
||||
|
||||
sub singleton_prompt_help_cmd {
|
||||
print <<\EOF ;
|
||||
Prompt help:
|
||||
1 - select a numbered item
|
||||
foo - select item based on unique prefix
|
||||
- (empty) select nothing
|
||||
EOF
|
||||
}
|
||||
|
||||
sub prompt_help_cmd {
|
||||
print <<\EOF ;
|
||||
Prompt help:
|
||||
1 - select a single item
|
||||
3-5 - select a range of items
|
||||
2-3,6-9 - select multiple ranges
|
||||
foo - select item based on unique prefix
|
||||
-... - unselect specified items
|
||||
* - choose all items
|
||||
- (empty) finish selecting
|
||||
EOF
|
||||
}
|
||||
|
||||
sub status_cmd {
|
||||
list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },
|
||||
list_modified());
|
||||
@@ -544,27 +674,36 @@ sub help_patch_cmd {
|
||||
print <<\EOF ;
|
||||
y - stage this hunk
|
||||
n - do not stage this hunk
|
||||
a - stage this and all the remaining hunks
|
||||
d - do not stage this hunk nor any of the remaining hunks
|
||||
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
|
||||
EOF
|
||||
}
|
||||
|
||||
sub patch_update_cmd {
|
||||
my @mods = list_modified('file-only');
|
||||
@mods = grep { !($_->{BINARY}) } @mods;
|
||||
return if (!@mods);
|
||||
my @mods = grep { !($_->{BINARY}) } list_modified('file-only');
|
||||
my @them;
|
||||
|
||||
my ($it) = list_and_choose({ PROMPT => 'Patch update',
|
||||
SINGLETON => 1,
|
||||
IMMEDIATE => 1,
|
||||
HEADER => $status_head, },
|
||||
@mods);
|
||||
patch_update_file($it->{VALUE}) if ($it);
|
||||
if (!@mods) {
|
||||
print STDERR "No changes.\n";
|
||||
return 0;
|
||||
}
|
||||
if ($patch_mode) {
|
||||
@them = @mods;
|
||||
}
|
||||
else {
|
||||
@them = list_and_choose({ PROMPT => 'Patch update',
|
||||
HEADER => $status_head, },
|
||||
@mods);
|
||||
}
|
||||
for (@them) {
|
||||
patch_update_file($_->{VALUE});
|
||||
}
|
||||
}
|
||||
|
||||
sub patch_update_file {
|
||||
@@ -775,6 +914,20 @@ add untracked - add contents of untracked files to the staged set of changes
|
||||
EOF
|
||||
}
|
||||
|
||||
sub process_args {
|
||||
return unless @ARGV;
|
||||
my $arg = shift @ARGV;
|
||||
if ($arg eq "--patch") {
|
||||
$patch_mode = 1;
|
||||
$arg = shift @ARGV or die "missing --";
|
||||
die "invalid argument $arg, expecting --"
|
||||
unless $arg eq "--";
|
||||
}
|
||||
elsif ($arg ne "--") {
|
||||
die "invalid argument $arg, expecting --";
|
||||
}
|
||||
}
|
||||
|
||||
sub main_loop {
|
||||
my @cmd = ([ 'status', \&status_cmd, ],
|
||||
[ 'update', \&update_cmd, ],
|
||||
@@ -803,6 +956,12 @@ sub main_loop {
|
||||
}
|
||||
}
|
||||
|
||||
process_args();
|
||||
refresh();
|
||||
status_cmd();
|
||||
main_loop();
|
||||
if ($patch_mode) {
|
||||
patch_update_cmd();
|
||||
}
|
||||
else {
|
||||
status_cmd();
|
||||
main_loop();
|
||||
}
|
||||
|
||||
@@ -307,9 +307,9 @@ do
|
||||
GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
|
||||
GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
|
||||
|
||||
if test -z "$GIT_AUTHOR_EMAIL"
|
||||
if test -z "$GIT_AUTHOR_EMAIL" || test -z "$GIT_AUTHOR_DATE"
|
||||
then
|
||||
echo "Patch does not have a valid e-mail address."
|
||||
echo "Patch does not have valid authorship information."
|
||||
stop_here $this
|
||||
fi
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ cleanup() {
|
||||
trap cleanup 0
|
||||
mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage
|
||||
test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" &&
|
||||
W=$(cd "$GIT_WORK_TREE" && pwd) && export GIT_WORK_TREE="$W"
|
||||
W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE
|
||||
if test yes = "$bare" || test -n "$GIT_WORK_TREE"; then
|
||||
GIT_DIR="$D"
|
||||
else
|
||||
|
||||
@@ -66,17 +66,17 @@ set_ident () {
|
||||
h
|
||||
s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/export GIT_'$uid'_NAME='\''&'\''/p
|
||||
s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p
|
||||
|
||||
g
|
||||
s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/export GIT_'$uid'_EMAIL='\''&'\''/p
|
||||
s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
|
||||
|
||||
g
|
||||
s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/export GIT_'$uid'_DATE='\''&'\''/p
|
||||
s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
|
||||
|
||||
q
|
||||
}
|
||||
@@ -84,7 +84,7 @@ set_ident () {
|
||||
|
||||
LANG=C LC_ALL=C sed -ne "$pick_id_script"
|
||||
# Ensure non-empty id name.
|
||||
echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\""
|
||||
echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
|
||||
}
|
||||
|
||||
USAGE="[--env-filter <command>] [--tree-filter <command>] \
|
||||
@@ -206,7 +206,8 @@ done < "$tempdir"/backup-refs
|
||||
ORIG_GIT_DIR="$GIT_DIR"
|
||||
ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
|
||||
ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
|
||||
export GIT_DIR GIT_WORK_TREE=.
|
||||
GIT_WORK_TREE=.
|
||||
export GIT_DIR GIT_WORK_TREE
|
||||
|
||||
# These refs should be updated if their heads were rewritten
|
||||
|
||||
@@ -231,7 +232,8 @@ done > "$tempdir"/heads
|
||||
test -s "$tempdir"/heads ||
|
||||
die "Which ref do you want to rewrite?"
|
||||
|
||||
export GIT_INDEX_FILE="$(pwd)/../index"
|
||||
GIT_INDEX_FILE="$(pwd)/../index"
|
||||
export GIT_INDEX_FILE
|
||||
git read-tree || die "Could not seed the index"
|
||||
|
||||
ret=0
|
||||
@@ -267,7 +269,8 @@ while read commit parents; do
|
||||
git read-tree -i -m $commit:"$filter_subdir"
|
||||
esac || die "Could not initialize the index"
|
||||
|
||||
export GIT_COMMIT=$commit
|
||||
GIT_COMMIT=$commit
|
||||
export GIT_COMMIT
|
||||
git cat-file commit "$commit" >../commit ||
|
||||
die "Cannot read commit $commit"
|
||||
|
||||
@@ -401,7 +404,8 @@ if [ "$filter_tag_name" ]; then
|
||||
|
||||
[ -f "../map/$sha1" ] || continue
|
||||
new_sha1="$(cat "../map/$sha1")"
|
||||
export GIT_COMMIT="$sha1"
|
||||
GIT_COMMIT="$sha1"
|
||||
export GIT_COMMIT
|
||||
new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
|
||||
die "tag name filter failed: $filter_tag_name"
|
||||
|
||||
|
||||
11
git-pull.sh
11
git-pull.sh
@@ -17,6 +17,9 @@ test -z "$(git ls-files -u)" ||
|
||||
die "You are in the middle of a conflicted merge."
|
||||
|
||||
strategy_args= no_summary= no_commit= squash= no_ff=
|
||||
curr_branch=$(git symbolic-ref -q HEAD)
|
||||
curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
|
||||
rebase=$(git config --bool branch.$curr_branch_short.rebase)
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
@@ -52,6 +55,12 @@ do
|
||||
esac
|
||||
strategy_args="${strategy_args}-s $strategy "
|
||||
;;
|
||||
-r|--r|--re|--reb|--reba|--rebas|--rebase)
|
||||
rebase=true
|
||||
;;
|
||||
--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
|
||||
rebase=false
|
||||
;;
|
||||
-h|--h|--he|--hel|--help)
|
||||
usage
|
||||
;;
|
||||
@@ -95,7 +104,6 @@ merge_head=$(sed -e '/ not-for-merge /d' \
|
||||
|
||||
case "$merge_head" in
|
||||
'')
|
||||
curr_branch=$(git symbolic-ref -q HEAD)
|
||||
case $? in
|
||||
0) ;;
|
||||
1) echo >&2 "You are not currently on a branch; you must explicitly"
|
||||
@@ -142,5 +150,6 @@ then
|
||||
fi
|
||||
|
||||
merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
|
||||
test true = "$rebase" && exec git-rebase $merge_head
|
||||
exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \
|
||||
"$merge_name" HEAD $merge_head
|
||||
|
||||
@@ -77,8 +77,9 @@ for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
|
||||
}
|
||||
|
||||
# Parse the author information
|
||||
export GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info")
|
||||
export GIT_AUTHOR_EMAIL=$(sed -ne 's/Email: //p' "$tmp_info")
|
||||
GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info")
|
||||
GIT_AUTHOR_EMAIL=$(sed -ne 's/Email: //p' "$tmp_info")
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
|
||||
while test -z "$GIT_AUTHOR_EMAIL" && test -z "$GIT_AUTHOR_NAME" ; do
|
||||
if [ -n "$quilt_author" ] ; then
|
||||
GIT_AUTHOR_NAME="$quilt_author_name";
|
||||
@@ -104,8 +105,9 @@ for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
|
||||
GIT_AUTHOR_EMAIL="$patch_author_email"
|
||||
fi
|
||||
done
|
||||
export GIT_AUTHOR_DATE=$(sed -ne 's/Date: //p' "$tmp_info")
|
||||
export SUBJECT=$(sed -ne 's/Subject: //p' "$tmp_info")
|
||||
GIT_AUTHOR_DATE=$(sed -ne 's/Date: //p' "$tmp_info")
|
||||
SUBJECT=$(sed -ne 's/Subject: //p' "$tmp_info")
|
||||
export GIT_AUTHOR_DATE SUBJECT
|
||||
if [ -z "$SUBJECT" ] ; then
|
||||
SUBJECT=$(echo $patch_name | sed -e 's/.patch$//')
|
||||
fi
|
||||
|
||||
@@ -30,6 +30,11 @@ test -d "$REWRITTEN" && PRESERVE_MERGES=t
|
||||
test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
|
||||
test -f "$DOTEST"/verbose && VERBOSE=t
|
||||
|
||||
GIT_CHERRY_PICK_HELP=" After resolving the conflicts,
|
||||
mark the corrected paths with 'git add <paths>', and
|
||||
run 'git rebase --continue'"
|
||||
export GIT_CHERRY_PICK_HELP
|
||||
|
||||
warn () {
|
||||
echo "$*" >&2
|
||||
}
|
||||
@@ -90,6 +95,7 @@ make_patch () {
|
||||
|
||||
die_with_patch () {
|
||||
make_patch "$1"
|
||||
git rerere
|
||||
die "$2"
|
||||
}
|
||||
|
||||
@@ -175,13 +181,13 @@ pick_one_preserving_merges () {
|
||||
msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
|
||||
# No point in merging the first parent, that's HEAD
|
||||
new_parents=${new_parents# $first_parent}
|
||||
# NEEDSWORK: give rerere a chance
|
||||
if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
|
||||
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
|
||||
GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
|
||||
output git merge $STRATEGY -m "$msg" \
|
||||
$new_parents
|
||||
then
|
||||
git rerere
|
||||
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
|
||||
die Error redoing merge $sha1
|
||||
fi
|
||||
@@ -369,6 +375,7 @@ do
|
||||
--abort)
|
||||
comment_for_reflog abort
|
||||
|
||||
git rerere clear
|
||||
test -d "$DOTEST" || die "No interactive rebase running"
|
||||
|
||||
HEADNAME=$(cat "$DOTEST"/head-name)
|
||||
@@ -385,6 +392,7 @@ do
|
||||
--skip)
|
||||
comment_for_reflog skip
|
||||
|
||||
git rerere clear
|
||||
test -d "$DOTEST" || die "No interactive rebase running"
|
||||
|
||||
output git reset --hard && do_rest
|
||||
|
||||
16
git-stash.sh
16
git-stash.sh
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2007, Nanako Shiraishi
|
||||
|
||||
USAGE='[ | list | show | apply | clear]'
|
||||
USAGE='[ | save | list | show | apply | clear | create ]'
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_SPEC=
|
||||
@@ -207,6 +207,10 @@ show)
|
||||
shift
|
||||
show_stash "$@"
|
||||
;;
|
||||
save)
|
||||
shift
|
||||
save_stash "$*" && git-reset --hard
|
||||
;;
|
||||
apply)
|
||||
shift
|
||||
apply_stash "$@"
|
||||
@@ -221,14 +225,12 @@ create)
|
||||
fi
|
||||
create_stash "$*" && echo "$w_commit"
|
||||
;;
|
||||
help | usage)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
if test $# -gt 0 && test "$1" = save
|
||||
if test $# -eq 0
|
||||
then
|
||||
shift
|
||||
save_stash && git-reset --hard
|
||||
else
|
||||
usage
|
||||
fi
|
||||
save_stash "$*" && git-reset --hard
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -158,7 +158,7 @@ module_add()
|
||||
die "'$path' already exists in the index"
|
||||
|
||||
module_clone "$path" "$realrepo" || exit
|
||||
(unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
|
||||
(unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
|
||||
die "Unable to checkout submodule '$path'"
|
||||
git add "$path" ||
|
||||
die "Failed to add submodule '$path'"
|
||||
@@ -228,14 +228,14 @@ modules_update()
|
||||
module_clone "$path" "$url" || exit
|
||||
subsha1=
|
||||
else
|
||||
subsha1=$(unset GIT_DIR && cd "$path" &&
|
||||
subsha1=$(unset GIT_DIR; cd "$path" &&
|
||||
git rev-parse --verify HEAD) ||
|
||||
die "Unable to find current revision in submodule path '$path'"
|
||||
fi
|
||||
|
||||
if test "$subsha1" != "$sha1"
|
||||
then
|
||||
(unset GIT_DIR && cd "$path" && git-fetch &&
|
||||
(unset GIT_DIR; cd "$path" && git-fetch &&
|
||||
git-checkout -q "$sha1") ||
|
||||
die "Unable to checkout '$sha1' in submodule path '$path'"
|
||||
|
||||
@@ -246,7 +246,7 @@ modules_update()
|
||||
|
||||
set_name_rev () {
|
||||
revname=$( (
|
||||
unset GIT_DIR &&
|
||||
unset GIT_DIR
|
||||
cd "$1" && {
|
||||
git describe "$2" 2>/dev/null ||
|
||||
git describe --tags "$2" 2>/dev/null ||
|
||||
@@ -285,7 +285,7 @@ modules_list()
|
||||
else
|
||||
if test -z "$cached"
|
||||
then
|
||||
sha1=$(unset GIT_DIR && cd "$path" && git rev-parse --verify HEAD)
|
||||
sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
|
||||
set_name_rev "$path" "$sha1"
|
||||
fi
|
||||
say "+$sha1 $path$revname"
|
||||
|
||||
115
git-svn.perl
115
git-svn.perl
@@ -35,6 +35,7 @@ push @Git::SVN::Ra::ISA, 'SVN::Ra';
|
||||
push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
|
||||
push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
|
||||
use Carp qw/croak/;
|
||||
use Digest::MD5;
|
||||
use IO::File qw//;
|
||||
use File::Basename qw/dirname basename/;
|
||||
use File::Path qw/mkpath/;
|
||||
@@ -48,8 +49,7 @@ BEGIN {
|
||||
foreach (qw/command command_oneline command_noisy command_output_pipe
|
||||
command_input_pipe command_close_pipe/) {
|
||||
for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
|
||||
Git::SVN::Migration Git::SVN::Log Git::SVN
|
||||
Git::SVN::Util),
|
||||
Git::SVN::Migration Git::SVN::Log Git::SVN),
|
||||
__PACKAGE__) {
|
||||
*{"${package}::$_"} = \&{"Git::$_"};
|
||||
}
|
||||
@@ -81,6 +81,7 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
|
||||
'quiet|q' => \$_q,
|
||||
'repack-flags|repack-args|repack-opts=s' =>
|
||||
\$Git::SVN::_repack_flags,
|
||||
'use-log-author' => \$Git::SVN::_use_log_author,
|
||||
%remote_opts );
|
||||
|
||||
my ($_trunk, $_tags, $_branches, $_stdlayout);
|
||||
@@ -142,6 +143,9 @@ my %cmd = (
|
||||
'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings",
|
||||
{ 'revision|r=i' => \$_revision
|
||||
} ],
|
||||
'show-externals' => [ \&cmd_show_externals, "Show svn:externals listings",
|
||||
{ 'revision|r=i' => \$_revision
|
||||
} ],
|
||||
'multi-fetch' => [ \&cmd_multi_fetch,
|
||||
"Deprecated alias for $0 fetch --all",
|
||||
{ 'revision|r=s' => \$_revision, %fc_opts } ],
|
||||
@@ -193,23 +197,6 @@ for (my $i = 0; $i < @ARGV; $i++) {
|
||||
}
|
||||
};
|
||||
|
||||
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
|
||||
|
||||
read_repo_config(\%opts);
|
||||
Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
|
||||
my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
|
||||
'minimize-connections' => \$Git::SVN::Migration::_minimize,
|
||||
'id|i=s' => \$Git::SVN::default_ref_id,
|
||||
'svn-remote|remote|R=s' => sub {
|
||||
$Git::SVN::no_reuse_existing = 1;
|
||||
$Git::SVN::default_repo_id = $_[1] });
|
||||
exit 1 if (!$rv && $cmd && $cmd ne 'log');
|
||||
|
||||
usage(0) if $_help;
|
||||
version() if $_version;
|
||||
usage(1) unless defined $cmd;
|
||||
load_authors() if $_authors;
|
||||
|
||||
# make sure we're always running
|
||||
unless ($cmd =~ /(?:clone|init|multi-init)$/) {
|
||||
unless (-d $ENV{GIT_DIR}) {
|
||||
@@ -231,6 +218,24 @@ unless ($cmd =~ /(?:clone|init|multi-init)$/) {
|
||||
$ENV{GIT_DIR} = $git_dir;
|
||||
}
|
||||
}
|
||||
|
||||
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
|
||||
|
||||
read_repo_config(\%opts);
|
||||
Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
|
||||
my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
|
||||
'minimize-connections' => \$Git::SVN::Migration::_minimize,
|
||||
'id|i=s' => \$Git::SVN::default_ref_id,
|
||||
'svn-remote|remote|R=s' => sub {
|
||||
$Git::SVN::no_reuse_existing = 1;
|
||||
$Git::SVN::default_repo_id = $_[1] });
|
||||
exit 1 if (!$rv && $cmd && $cmd ne 'log');
|
||||
|
||||
usage(0) if $_help;
|
||||
version() if $_version;
|
||||
usage(1) unless defined $cmd;
|
||||
load_authors() if $_authors;
|
||||
|
||||
unless ($cmd =~ /^(?:clone|init|multi-init|commit-diff)$/) {
|
||||
Git::SVN::Migration::migration_check();
|
||||
}
|
||||
@@ -545,6 +550,8 @@ sub cmd_rebase {
|
||||
exit 1;
|
||||
}
|
||||
unless ($_local) {
|
||||
# rebase will checkout for us, so no need to do it explicitly
|
||||
$_no_checkout = 'true';
|
||||
$_fetch_all ? $gs->fetch_all : $gs->fetch;
|
||||
}
|
||||
command_noisy(rebase_cmd(), $gs->refname);
|
||||
@@ -565,6 +572,21 @@ sub cmd_show_ignore {
|
||||
});
|
||||
}
|
||||
|
||||
sub cmd_show_externals {
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
|
||||
$gs ||= Git::SVN->new;
|
||||
my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
|
||||
$gs->prop_walk($gs->{path}, $r, sub {
|
||||
my ($gs, $path, $props) = @_;
|
||||
print STDOUT "\n# $path\n";
|
||||
my $s = $props->{'svn:externals'} or return;
|
||||
$s =~ s/[\r\n]+/\n/g;
|
||||
chomp $s;
|
||||
$s =~ s#^#$path#gm;
|
||||
print STDOUT "$s\n";
|
||||
});
|
||||
}
|
||||
|
||||
sub cmd_create_ignore {
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
|
||||
$gs ||= Git::SVN->new;
|
||||
@@ -840,19 +862,19 @@ sub cmd_info {
|
||||
command_output_pipe(qw(cat-file blob), "HEAD:$path");
|
||||
if ($file_type eq "link") {
|
||||
my $file_name = <$fh>;
|
||||
$checksum = Git::SVN::Util::md5sum("link $file_name");
|
||||
$checksum = md5sum("link $file_name");
|
||||
} else {
|
||||
$checksum = Git::SVN::Util::md5sum($fh);
|
||||
$checksum = md5sum($fh);
|
||||
}
|
||||
command_close_pipe($fh, $ctx);
|
||||
} elsif ($file_type eq "link") {
|
||||
my $file_name =
|
||||
command(qw(cat-file blob), "HEAD:$path");
|
||||
$checksum =
|
||||
Git::SVN::Util::md5sum("link " . $file_name);
|
||||
md5sum("link " . $file_name);
|
||||
} else {
|
||||
open FILE, "<", $path or die $!;
|
||||
$checksum = Git::SVN::Util::md5sum(\*FILE);
|
||||
$checksum = md5sum(\*FILE);
|
||||
close FILE or die $!;
|
||||
}
|
||||
$result .= "Checksum: " . $checksum . "\n";
|
||||
@@ -1193,11 +1215,6 @@ sub find_file_type_and_diff_status {
|
||||
return ("file", $diff_status);
|
||||
}
|
||||
|
||||
package Git::SVN::Util;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Digest::MD5;
|
||||
|
||||
sub md5sum {
|
||||
my $arg = shift;
|
||||
my $ref = ref $arg;
|
||||
@@ -1219,7 +1236,8 @@ use strict;
|
||||
use warnings;
|
||||
use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
|
||||
$_repack $_repack_flags $_use_svm_props $_head
|
||||
$_use_svnsync_props $no_reuse_existing $_minimize_url/;
|
||||
$_use_svnsync_props $no_reuse_existing $_minimize_url
|
||||
$_use_log_author/;
|
||||
use Carp qw/croak/;
|
||||
use File::Path qw/mkpath/;
|
||||
use File::Copy qw/copy/;
|
||||
@@ -2059,11 +2077,17 @@ sub do_git_commit {
|
||||
croak "$log_entry->{revision} = $c already exists! ",
|
||||
"Why are we refetching it?\n";
|
||||
}
|
||||
$ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $log_entry->{name};
|
||||
$ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} =
|
||||
$log_entry->{email};
|
||||
$ENV{GIT_AUTHOR_NAME} = $log_entry->{name};
|
||||
$ENV{GIT_AUTHOR_EMAIL} = $log_entry->{email};
|
||||
$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date};
|
||||
|
||||
$ENV{GIT_COMMITTER_NAME} = (defined $log_entry->{commit_name})
|
||||
? $log_entry->{commit_name}
|
||||
: $log_entry->{name};
|
||||
$ENV{GIT_COMMITTER_EMAIL} = (defined $log_entry->{commit_email})
|
||||
? $log_entry->{commit_email}
|
||||
: $log_entry->{email};
|
||||
|
||||
my $tree = $log_entry->{tree};
|
||||
if (!defined $tree) {
|
||||
$tree = $self->tmp_index_do(sub {
|
||||
@@ -2351,7 +2375,17 @@ sub make_log_entry {
|
||||
$log_entry{log} .= "\n";
|
||||
my $author = $log_entry{author} = check_author($log_entry{author});
|
||||
my ($name, $email) = defined $::users{$author} ? @{$::users{$author}}
|
||||
: ($author, undef);
|
||||
: ($author, undef);
|
||||
|
||||
my ($commit_name, $commit_email) = ($name, $email);
|
||||
if ($_use_log_author) {
|
||||
if ($log_entry{log} =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) {
|
||||
($name, $email) = ($1, $2);
|
||||
} elsif ($log_entry{log} =~
|
||||
/Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
|
||||
($name, $email) = ($1, $2);
|
||||
}
|
||||
}
|
||||
if (defined $headrev && $self->use_svm_props) {
|
||||
if ($self->rewrite_root) {
|
||||
die "Can't have both 'useSvmProps' and 'rewriteRoot' ",
|
||||
@@ -2374,23 +2408,28 @@ sub make_log_entry {
|
||||
remove_username($full_url);
|
||||
$log_entry{metadata} = "$full_url\@$r $uuid";
|
||||
$log_entry{svm_revision} = $r;
|
||||
$email ||= "$author\@$uuid"
|
||||
$email ||= "$author\@$uuid";
|
||||
$commit_email ||= "$author\@$uuid";
|
||||
} elsif ($self->use_svnsync_props) {
|
||||
my $full_url = $self->svnsync->{url};
|
||||
$full_url .= "/$self->{path}" if length $self->{path};
|
||||
remove_username($full_url);
|
||||
my $uuid = $self->svnsync->{uuid};
|
||||
$log_entry{metadata} = "$full_url\@$rev $uuid";
|
||||
$email ||= "$author\@$uuid"
|
||||
$email ||= "$author\@$uuid";
|
||||
$commit_email ||= "$author\@$uuid";
|
||||
} else {
|
||||
my $url = $self->metadata_url;
|
||||
remove_username($url);
|
||||
$log_entry{metadata} = "$url\@$rev " .
|
||||
$self->ra->get_uuid;
|
||||
$email ||= "$author\@" . $self->ra->get_uuid;
|
||||
$commit_email ||= "$author\@" . $self->ra->get_uuid;
|
||||
}
|
||||
$log_entry{name} = $name;
|
||||
$log_entry{email} = $email;
|
||||
$log_entry{commit_name} = $commit_name;
|
||||
$log_entry{commit_email} = $commit_email;
|
||||
\%log_entry;
|
||||
}
|
||||
|
||||
@@ -2947,7 +2986,7 @@ sub apply_textdelta {
|
||||
|
||||
if (defined $exp) {
|
||||
seek $base, 0, 0 or croak $!;
|
||||
my $got = Git::SVN::Util::md5sum($base);
|
||||
my $got = ::md5sum($base);
|
||||
die "Checksum mismatch: $fb->{path} $fb->{blob}\n",
|
||||
"expected: $exp\n",
|
||||
" got: $got\n" if ($got ne $exp);
|
||||
@@ -2966,7 +3005,7 @@ sub close_file {
|
||||
if (my $fh = $fb->{fh}) {
|
||||
if (defined $exp) {
|
||||
seek($fh, 0, 0) or croak $!;
|
||||
my $got = Git::SVN::Util::md5sum($fh);
|
||||
my $got = ::md5sum($fh);
|
||||
if ($got ne $exp) {
|
||||
die "Checksum mismatch: $path\n",
|
||||
"expected: $exp\n got: $got\n";
|
||||
@@ -3321,7 +3360,7 @@ sub chg_file {
|
||||
$fh->flush == 0 or croak $!;
|
||||
seek $fh, 0, 0 or croak $!;
|
||||
|
||||
my $exp = Git::SVN::Util::md5sum($fh);
|
||||
my $exp = ::md5sum($fh);
|
||||
seek $fh, 0, 0 or croak $!;
|
||||
|
||||
my $pool = SVN::Pool->new;
|
||||
|
||||
10
git.c
10
git.c
@@ -169,7 +169,7 @@ static int handle_alias(int *argcp, const char ***argv)
|
||||
|
||||
strbuf_init(&buf, PATH_MAX);
|
||||
strbuf_addstr(&buf, alias_string);
|
||||
sq_quote_argv(&buf, (*argv) + 1, *argcp - 1, PATH_MAX);
|
||||
sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
|
||||
free(alias_string);
|
||||
alias_string = buf.buf;
|
||||
}
|
||||
@@ -198,7 +198,7 @@ static int handle_alias(int *argcp, const char ***argv)
|
||||
if (!strcmp(alias_command, new_argv[0]))
|
||||
die("recursive alias: %s", alias_command);
|
||||
|
||||
trace_argv_printf(new_argv, count,
|
||||
trace_argv_printf(new_argv,
|
||||
"trace: alias expansion: %s =>",
|
||||
alias_command);
|
||||
|
||||
@@ -252,7 +252,7 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
|
||||
if (p->option & NEED_WORK_TREE)
|
||||
setup_work_tree();
|
||||
|
||||
trace_argv_printf(argv, argc, "trace: built-in: git");
|
||||
trace_argv_printf(argv, "trace: built-in: git");
|
||||
|
||||
status = p->fn(argc, argv, prefix);
|
||||
if (status)
|
||||
@@ -294,6 +294,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "cherry", cmd_cherry, RUN_SETUP },
|
||||
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
|
||||
{ "config", cmd_config },
|
||||
{ "count-objects", cmd_count_objects, RUN_SETUP },
|
||||
@@ -302,6 +303,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "diff-files", cmd_diff_files },
|
||||
{ "diff-index", cmd_diff_index, RUN_SETUP },
|
||||
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
|
||||
{ "fast-export", cmd_fast_export, RUN_SETUP },
|
||||
{ "fetch", cmd_fetch, RUN_SETUP },
|
||||
{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
|
||||
{ "fetch--tool", cmd_fetch__tool, RUN_SETUP },
|
||||
@@ -345,11 +347,11 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "rev-parse", cmd_rev_parse },
|
||||
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "rm", cmd_rm, RUN_SETUP },
|
||||
{ "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "send-pack", cmd_send_pack, RUN_SETUP },
|
||||
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
|
||||
{ "show-branch", cmd_show_branch, RUN_SETUP },
|
||||
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
|
||||
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "stripspace", cmd_stripspace },
|
||||
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
|
||||
{ "tag", cmd_tag, RUN_SETUP },
|
||||
|
||||
29
gitk-git/Makefile
Normal file
29
gitk-git/Makefile
Normal file
@@ -0,0 +1,29 @@
|
||||
# The default target of this Makefile is...
|
||||
all::
|
||||
|
||||
prefix ?= $(HOME)
|
||||
bindir ?= $(prefix)/bin
|
||||
TCLTK_PATH ?= wish
|
||||
INSTALL ?= install
|
||||
RM ?= rm -f
|
||||
|
||||
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
|
||||
bindir_SQ = $(subst ','\'',$(bindir))
|
||||
TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
|
||||
|
||||
ifndef V
|
||||
QUIET = @
|
||||
QUIET_GEN = $(QUIET)echo ' ' GEN $@ &&
|
||||
endif
|
||||
|
||||
all:: gitk-wish
|
||||
install:: all
|
||||
$(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
|
||||
clean::
|
||||
$(RM) gitk-wish
|
||||
|
||||
gitk-wish: gitk
|
||||
$(QUIET_GEN)$(RM) $@ $@+ && \
|
||||
sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \
|
||||
chmod +x $@+ && \
|
||||
mv -f $@+ $@
|
||||
0
gitk → gitk-git/gitk
Executable file → Normal file
0
gitk → gitk-git/gitk
Executable file → Normal file
215
gitweb/README
215
gitweb/README
@@ -10,28 +10,96 @@ From the git version 1.4.0 gitweb is bundled with git.
|
||||
How to configure gitweb for your local system
|
||||
---------------------------------------------
|
||||
|
||||
See also "Build time configuration" section in INSTALL
|
||||
file for gitweb (in gitweb/INSTALL).
|
||||
|
||||
You can specify the following configuration variables when building GIT:
|
||||
* GIT_BINDIR
|
||||
Points out where to find git executable. You should set up it to
|
||||
the place where git binary was installed (usually /usr/bin) if you
|
||||
don't install git from sources together with gitweb. [Default: $(bindir)]
|
||||
* GITWEB_SITENAME
|
||||
Shown in the title of all generated pages, defaults to the servers name.
|
||||
Shown in the title of all generated pages, defaults to the server name
|
||||
(SERVER_NAME CGI environment variable) if not set. [No default]
|
||||
* GITWEB_PROJECTROOT
|
||||
The root directory for all projects shown by gitweb.
|
||||
The root directory for all projects shown by gitweb. Must be set
|
||||
correctly for gitweb to find repositories to display. See also
|
||||
"Gitweb repositories" in INSTALL file for gitweb. [Default: /pub/git]
|
||||
* GITWEB_PROJECT_MAXDEPTH
|
||||
The filesystem traversing limit for getting projects list; the number
|
||||
is taken as depth relative to the projectroot. It is used when
|
||||
GITWEB_LIST is a directory (or is not set; then project root is used).
|
||||
Is is meant to speed up project listing on large work trees by limiting
|
||||
find depth. [Default: 2007]
|
||||
* GITWEB_LIST
|
||||
points to a directory to scan for projects (defaults to project root)
|
||||
or to a file for explicit listing of projects.
|
||||
Points to a directory to scan for projects (defaults to project root
|
||||
if not set / if empty) or to a file with explicit listing of projects
|
||||
(together with projects' ownership). See "Generating projects list
|
||||
using gitweb" in INSTALL file for gitweb to find out how to generate
|
||||
such file from scan of a directory. [No default, which means use root
|
||||
directory for projects]
|
||||
* GITWEB_EXPORT_OK
|
||||
Show repository only if this file exists (in repository). Only
|
||||
effective if this variable evaluates to true. [No default / Not set]
|
||||
* GITWEB_STRICT_EXPORT
|
||||
Only allow viewing of repositories also shown on the overview page.
|
||||
This for example makes GITWEB_EXPORT_OK to decide if repository is
|
||||
available and not only if it is shown. If GITWEB_LIST points to
|
||||
file with list of project, only those repositories listed would be
|
||||
available for gitweb. [No default]
|
||||
* GITWEB_HOMETEXT
|
||||
points to an .html file which is included on the gitweb project
|
||||
overview page.
|
||||
Points to an .html file which is included on the gitweb project
|
||||
overview page ('projects_list' view), if it exists. Relative to
|
||||
gitweb.cgi script. [Default: indextext.html]
|
||||
* GITWEB_SITE_HEADER
|
||||
Filename of html text to include at top of each page. Relative to
|
||||
gitweb.cgi script. [No default]
|
||||
* GITWEB_SITE_FOOTER
|
||||
Filename of html text to include at bottom of each page. Relative to
|
||||
gitweb.cgi script. [No default]
|
||||
* GITWEB_HOME_LINK_STR
|
||||
String of the home link on top of all pages, leading to $home_link
|
||||
(usually main gitweb page, which means projects list). Used as first
|
||||
part of gitweb view "breadcrumb trail": <home> / <project> / <view>.
|
||||
[Default: projects]
|
||||
* GITWEB_SITENAME
|
||||
Name of your site or organization to appear in page titles. Set it
|
||||
to something descriptive for clearer bookmarks etc. If not set
|
||||
(if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if
|
||||
SERVER_NAME CGI environment variable is not set (e.g. if running
|
||||
gitweb as standalone script). [No default]
|
||||
* GITWEB_BASE_URL
|
||||
Git base URLs used for URL to where fetch project from, i.e. full
|
||||
URL is "$git_base_url/$project". Shown on projects summary page.
|
||||
Repository URL for project can be also configured per repository; this
|
||||
takes precendence over URL composed from base URL and project name.
|
||||
Note that you can setup multiple base URLs (for example one for
|
||||
git:// protocol access, one for http:// access) from gitweb config
|
||||
file. [No default]
|
||||
* GITWEB_CSS
|
||||
Points to the location where you put gitweb.css on your web server.
|
||||
Points to the location where you put gitweb.css on your web server
|
||||
(or to be more generic URI of gitweb stylesheet). Relative to base
|
||||
URI of gitweb. Note that you can setup multiple stylesheets from
|
||||
gitweb config file. [Default: gitweb.css]
|
||||
* GITWEB_LOGO
|
||||
Points to the location where you put git-logo.png on your web server.
|
||||
Points to the location where you put git-logo.png on your web server
|
||||
(or to be more generic URI of logo, 72x27 size, displayed in top right
|
||||
corner of each gitweb page, and used as logo for Atom feed). Relative
|
||||
to base URI of gitweb. [Default: git-logo.png]
|
||||
* GITWEB_FAVICON
|
||||
Points to the location where you put git-favicon.png on your web server
|
||||
(or to be more generic URI of favicon, assumed to be image/png type;
|
||||
web browsers that support favicons (website icons) may display them
|
||||
in the browser's URL bar and next to site name in bookmarks). Relative
|
||||
to base URI of gitweb. [Default: git-favicon.png]
|
||||
* GITWEB_CONFIG
|
||||
This file will be loaded using 'require' and can be used to override any
|
||||
of the options above as well as some other options - see the top of
|
||||
'gitweb.cgi' for their full list and description. If the environment
|
||||
$GITWEB_CONFIG is set when gitweb.cgi is executed the file in the
|
||||
environment variable will be loaded instead of the file
|
||||
specified when gitweb.cgi was created.
|
||||
This Perl file will be loaded using 'do' and can be used to override any
|
||||
of the options above as well as some other options -- see the "Runtime
|
||||
gitweb configuration" section below, and top of 'gitweb.cgi' for their
|
||||
full list and description. If the environment variable GITWEB_CONFIG
|
||||
is set when gitweb.cgi is executed, then the file specified in the
|
||||
environment variable will be loaded instead of the file specified
|
||||
when gitweb.cgi was created. [Default: gitweb_config.perl]
|
||||
|
||||
|
||||
Runtime gitweb configuration
|
||||
@@ -39,11 +107,122 @@ Runtime gitweb configuration
|
||||
|
||||
You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
|
||||
(defaults to 'gitweb_config.perl' in the same directory as the CGI).
|
||||
See the top of 'gitweb.cgi' for the list of variables and some description.
|
||||
The most notable thing that is not configurable at compile time are the
|
||||
optional features, stored in the '%features' variable. You can find further
|
||||
description on how to reconfigure the default features setting in your
|
||||
`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'.
|
||||
optional features, stored in the '%features' variable.
|
||||
|
||||
Ultimate description on how to reconfigure the default features setting
|
||||
in your `GITWEB_CONFIG` or per-project in `project.git/config` can be found
|
||||
as comments inside 'gitweb.cgi'.
|
||||
|
||||
See also "Gitweb config file" (with example of gitweb config file), and
|
||||
"Gitweb repositories" sections in INSTALL file for gitweb.
|
||||
|
||||
|
||||
Gitweb config file is [fragment] of perl code. You can set variables
|
||||
using "our $variable = value"; text from "#" character until the end
|
||||
of a line is ignored. See perlsyn(1) man page for details.
|
||||
|
||||
Below there is list of vaiables which you might want to set in gitweb config.
|
||||
See the top of 'gitweb.cgi' for the full list of variables and their
|
||||
descriptions.
|
||||
|
||||
Gitweb config file variables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can set, among others, the following variables in gitweb config files:
|
||||
* $GIT
|
||||
Cure git executable to use. By default set to "$GIT_BINDIR/git", which
|
||||
in turn is by default set to "$(bindir)/git". If you use git from binary
|
||||
package, set this to "/usr/bin/git". This can just be "git" if your
|
||||
webserver has a sensible PATH. If you have multiple git versions
|
||||
installed it is / can be used to choose which one to use.
|
||||
* $version
|
||||
Gitweb version, set automatically when creating gitweb.cgi from
|
||||
gitweb.perl. You might want to modify it if you are running modified
|
||||
gitweb.
|
||||
* $my_url, $my_uri
|
||||
URL and absolute URL of gitweb script; you might need to set those
|
||||
variables if you are using 'pathinfo' feature: see also below.
|
||||
* $home_link
|
||||
Target of the home link on top of all pages (the first part of view
|
||||
"breadcrumbs"). By default set to absolute URI of a page; you might
|
||||
need to set it up to [base] gitweb URI if you use 'pathinfo' feature
|
||||
(alternative format of the URLs, with project name embedded directly
|
||||
in the path part of URL).
|
||||
* @stylesheets
|
||||
List of URIs of stylesheets (relative to base URI of a page). You
|
||||
might specify more than one stylesheet, for example use gitweb.css
|
||||
as base, with site specific modifications in separate stylesheet
|
||||
to make it easier to upgrade gitweb. You can add 'site' stylesheet
|
||||
for example by using
|
||||
push @stylesheets, "gitweb-site.css";
|
||||
in gitweb config file.
|
||||
* $logo_url, $logo_label
|
||||
URI and label (title) of GIT logo link (or your site logo, if you choose
|
||||
to use different logo image). By default they point to git homepage;
|
||||
in the past they pointed to git documentation at www.kernel.org.
|
||||
* $projects_list_description_width
|
||||
The width (in characters) of the projects list "Description" column.
|
||||
Longer descriptions will be cut (trying to cut at word boundary);
|
||||
full description is available as 'title' attribute (usually shown on
|
||||
mouseover). By default set to 25, which might be too small if you
|
||||
use long project descriptions.
|
||||
* @git_base_url_list
|
||||
List of git base URLs used for URL to where fetch project from, shown
|
||||
in project summary page. Full URL is "$git_base_url/$project".
|
||||
You can setup multiple base URLs (for example one for git:// protocol
|
||||
access, and one for http:// "dumb" protocol access). Note that per
|
||||
repository configuration in 'cloneurl' file, or as values of gitweb.url
|
||||
project config.
|
||||
* $default_blob_plain_mimetype
|
||||
Default mimetype for blob_plain (raw) view, if mimetype checking
|
||||
doesn't result in some other type; by default 'text/plain'.
|
||||
* $default_text_plain_charset
|
||||
Default charset for text files. If not set, web serwer configuration
|
||||
would be used.
|
||||
* $mimetypes_file
|
||||
File to use for (filename extension based) guessing of MIME types before
|
||||
trying /etc/mime.types. Path, if relative, is taken currently as taken
|
||||
relative to current git repositoy.
|
||||
* $fallback_encoding
|
||||
Gitweb assumes this charset if line contains non-UTF-8 characters.
|
||||
Fallback decoding is used without error checking, so it can be even
|
||||
'utf-8'. Value mist be valid encodig; see Encoding::Supported(3pm) man
|
||||
page for a list. By default 'latin1', aka. 'iso-8859-1'.
|
||||
* @diff_opts
|
||||
Rename detection options for git-diff and git-diff-tree. By default
|
||||
('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or
|
||||
set it to () if you don't want to have renames detection.
|
||||
|
||||
Per-repository gitweb configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can also configure individual repositories shown in gitweb by creating
|
||||
file in the GIT_DIR of git repository, or by setting some repo configuration
|
||||
variable (in GIT_DIR/config).
|
||||
|
||||
You can use the following files in repository:
|
||||
* README.html
|
||||
A .html file (HTML fragment) which is included on the gitweb project
|
||||
summary page inside <div> block element. You can use it for longer
|
||||
description of a project, to provide links for example to projects
|
||||
homepage, etc.
|
||||
* description (or gitweb.description)
|
||||
Short (shortened by default to 25 characters in the projects list page)
|
||||
single line description of a project (of a repository). Plain text file;
|
||||
HTML will be escaped. By default set to
|
||||
Unnamed repository; edit this file to name it for gitweb.
|
||||
from the template during creating repository. You can use
|
||||
gitweb.description repo configuration variable, but the file takes
|
||||
precendence.
|
||||
* cloneurl (or multiple-valued gitweb.url)
|
||||
File with repository URL (used for clone and fetch), one per line.
|
||||
Displayed in the project summary page. You can use multiple-valued
|
||||
gitweb.url repository configuration variable for that, but the file
|
||||
takes precendence.
|
||||
* various gitweb.* config variables (in config)
|
||||
Read description of %feature hash for detailed list, and some
|
||||
descriptions.
|
||||
|
||||
|
||||
Webserver configuration
|
||||
|
||||
@@ -695,10 +695,9 @@ sub validate_refname {
|
||||
# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
|
||||
sub to_utf8 {
|
||||
my $str = shift;
|
||||
my $res;
|
||||
eval { $res = decode_utf8($str, Encode::FB_CROAK); };
|
||||
if (defined $res) {
|
||||
return $res;
|
||||
if (utf8::valid($str)) {
|
||||
utf8::decode($str);
|
||||
return $str;
|
||||
} else {
|
||||
return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
|
||||
}
|
||||
@@ -3786,6 +3785,8 @@ sub git_search_grep_body {
|
||||
"<td class=\"link\">" .
|
||||
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
|
||||
" | " .
|
||||
$cgi->a({-href => href(action=>"commitdiff", hash=>$co{'id'})}, "commitdiff") .
|
||||
" | " .
|
||||
$cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
|
||||
print "</td>\n" .
|
||||
"</tr>\n";
|
||||
|
||||
1
help.c
1
help.c
@@ -237,7 +237,6 @@ void list_common_cmds_help(void)
|
||||
mput_char(' ', longest - strlen(common_cmds[i].name));
|
||||
puts(common_cmds[i].help);
|
||||
}
|
||||
puts("(use 'git help -a' to get a list of all installed git commands)");
|
||||
}
|
||||
|
||||
static void show_man_page(const char *git_cmd)
|
||||
|
||||
11
http.c
11
http.c
@@ -24,6 +24,7 @@ char *ssl_cainfo = NULL;
|
||||
long curl_low_speed_limit = -1;
|
||||
long curl_low_speed_time = -1;
|
||||
int curl_ftp_no_epsv = 0;
|
||||
char *curl_http_proxy = NULL;
|
||||
|
||||
struct curl_slist *pragma_header;
|
||||
|
||||
@@ -160,6 +161,13 @@ static int http_options(const char *var, const char *value)
|
||||
curl_ftp_no_epsv = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp("http.proxy", var)) {
|
||||
if (curl_http_proxy == NULL) {
|
||||
curl_http_proxy = xmalloc(strlen(value)+1);
|
||||
strcpy(curl_http_proxy, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fall back on the default ones */
|
||||
return git_default_config(var, value);
|
||||
@@ -205,6 +213,9 @@ static CURL* get_curl_handle(void)
|
||||
if (curl_ftp_no_epsv)
|
||||
curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
|
||||
|
||||
if (curl_http_proxy)
|
||||
curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
56
ident.c
56
ident.c
@@ -113,25 +113,15 @@ static int add_raw(char *buf, size_t size, int offset, const char *str)
|
||||
|
||||
static int crud(unsigned char c)
|
||||
{
|
||||
static char crud_array[256];
|
||||
static int crud_array_initialized = 0;
|
||||
|
||||
if (!crud_array_initialized) {
|
||||
int k;
|
||||
|
||||
for (k = 0; k <= 31; ++k) crud_array[k] = 1;
|
||||
crud_array[' '] = 1;
|
||||
crud_array['.'] = 1;
|
||||
crud_array[','] = 1;
|
||||
crud_array[':'] = 1;
|
||||
crud_array[';'] = 1;
|
||||
crud_array['<'] = 1;
|
||||
crud_array['>'] = 1;
|
||||
crud_array['"'] = 1;
|
||||
crud_array['\''] = 1;
|
||||
crud_array_initialized = 1;
|
||||
}
|
||||
return crud_array[c];
|
||||
return c <= 32 ||
|
||||
c == '.' ||
|
||||
c == ',' ||
|
||||
c == ':' ||
|
||||
c == ';' ||
|
||||
c == '<' ||
|
||||
c == '>' ||
|
||||
c == '"' ||
|
||||
c == '\'';
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -192,12 +182,14 @@ static const char *env_hint =
|
||||
"Omit --global to set the identity only in this repository.\n"
|
||||
"\n";
|
||||
|
||||
const char *fmt_ident(const char *name, const char *email,
|
||||
const char *date_str, int error_on_no_name)
|
||||
static const char *fmt_ident_1(const char *name, const char *email,
|
||||
const char *date_str, int flag)
|
||||
{
|
||||
static char buffer[1000];
|
||||
char date[50];
|
||||
int i;
|
||||
int error_on_no_name = !!(flag & 01);
|
||||
int name_addr_only = !!(flag & 02);
|
||||
|
||||
setup_ident();
|
||||
if (!name)
|
||||
@@ -224,20 +216,36 @@ const char *fmt_ident(const char *name, const char *email,
|
||||
}
|
||||
|
||||
strcpy(date, git_default_date);
|
||||
if (date_str)
|
||||
if (!name_addr_only && date_str)
|
||||
parse_date(date_str, date, sizeof(date));
|
||||
|
||||
i = copy(buffer, sizeof(buffer), 0, name);
|
||||
i = add_raw(buffer, sizeof(buffer), i, " <");
|
||||
i = copy(buffer, sizeof(buffer), i, email);
|
||||
i = add_raw(buffer, sizeof(buffer), i, "> ");
|
||||
i = copy(buffer, sizeof(buffer), i, date);
|
||||
if (!name_addr_only) {
|
||||
i = add_raw(buffer, sizeof(buffer), i, "> ");
|
||||
i = copy(buffer, sizeof(buffer), i, date);
|
||||
} else {
|
||||
i = add_raw(buffer, sizeof(buffer), i, ">");
|
||||
}
|
||||
if (i >= sizeof(buffer))
|
||||
die("Impossibly long personal identifier");
|
||||
buffer[i] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *fmt_ident(const char *name, const char *email,
|
||||
const char *date_str, int error_on_no_name)
|
||||
{
|
||||
int flag = (error_on_no_name ? 01 : 0);
|
||||
return fmt_ident_1(name, email, date_str, flag);
|
||||
}
|
||||
|
||||
const char *fmt_name(const char *name, const char *email)
|
||||
{
|
||||
return fmt_ident_1(name, email, NULL, 03);
|
||||
}
|
||||
|
||||
const char *git_author_info(int error_on_no_name)
|
||||
{
|
||||
return fmt_ident(getenv("GIT_AUTHOR_NAME"),
|
||||
|
||||
@@ -216,6 +216,9 @@ is_abbreviated:
|
||||
return error("unknown option `%s'", arg);
|
||||
}
|
||||
|
||||
static NORETURN void usage_with_options_internal(const char * const *,
|
||||
const struct option *, int);
|
||||
|
||||
int parse_options(int argc, const char **argv, const struct option *options,
|
||||
const char * const usagestr[], int flags)
|
||||
{
|
||||
@@ -249,6 +252,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(arg + 2, "help-all"))
|
||||
usage_with_options_internal(usagestr, options, 1);
|
||||
if (!strcmp(arg + 2, "help"))
|
||||
usage_with_options(usagestr, options);
|
||||
if (parse_long_opt(&args, arg + 2, options))
|
||||
@@ -263,8 +268,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
|
||||
#define USAGE_OPTS_WIDTH 24
|
||||
#define USAGE_GAP 2
|
||||
|
||||
void usage_with_options(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
void usage_with_options_internal(const char * const *usagestr,
|
||||
const struct option *opts, int full)
|
||||
{
|
||||
fprintf(stderr, "usage: %s\n", *usagestr++);
|
||||
while (*usagestr && **usagestr)
|
||||
@@ -285,6 +290,8 @@ void usage_with_options(const char * const *usagestr,
|
||||
fprintf(stderr, "%s\n", opts->help);
|
||||
continue;
|
||||
}
|
||||
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
|
||||
continue;
|
||||
|
||||
pos = fprintf(stderr, " ");
|
||||
if (opts->short_name)
|
||||
@@ -335,6 +342,12 @@ void usage_with_options(const char * const *usagestr,
|
||||
exit(129);
|
||||
}
|
||||
|
||||
void usage_with_options(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
{
|
||||
usage_with_options_internal(usagestr, opts, 0);
|
||||
}
|
||||
|
||||
/*----- some often used options -----*/
|
||||
#include "cache.h"
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ enum parse_opt_option_flags {
|
||||
PARSE_OPT_OPTARG = 1,
|
||||
PARSE_OPT_NOARG = 2,
|
||||
PARSE_OPT_NONEG = 4,
|
||||
PARSE_OPT_HIDDEN = 8,
|
||||
};
|
||||
|
||||
struct option;
|
||||
@@ -57,6 +58,8 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
|
||||
* PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs)
|
||||
* PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
|
||||
* PARSE_OPT_NONEG: says that this option cannot be negated
|
||||
* PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in
|
||||
* the long one.
|
||||
*
|
||||
* `callback`::
|
||||
* pointer to the callback to use for OPTION_CALLBACK.
|
||||
|
||||
13
quote.c
13
quote.c
@@ -56,20 +56,13 @@ void sq_quote_print(FILE *stream, const char *src)
|
||||
fputc('\'', stream);
|
||||
}
|
||||
|
||||
void sq_quote_argv(struct strbuf *dst, const char** argv, int count,
|
||||
size_t maxlen)
|
||||
void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Count argv if needed. */
|
||||
if (count < 0) {
|
||||
for (count = 0; argv[count]; count++)
|
||||
; /* just counting */
|
||||
}
|
||||
|
||||
/* Copy into destination buffer. */
|
||||
strbuf_grow(dst, 32 * count);
|
||||
for (i = 0; i < count; ++i) {
|
||||
strbuf_grow(dst, 255);
|
||||
for (i = 0; argv[i]; ++i) {
|
||||
strbuf_addch(dst, ' ');
|
||||
sq_quote_buf(dst, argv[i]);
|
||||
if (maxlen && dst->len > maxlen)
|
||||
|
||||
3
quote.h
3
quote.h
@@ -31,8 +31,7 @@
|
||||
extern void sq_quote_print(FILE *stream, const char *src);
|
||||
|
||||
extern void sq_quote_buf(struct strbuf *, const char *src);
|
||||
extern void sq_quote_argv(struct strbuf *, const char **argv, int count,
|
||||
size_t maxlen);
|
||||
extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
|
||||
|
||||
/* This unwraps what sq_quote() produces in place, but returns
|
||||
* NULL if the input does not look like what sq_quote would have
|
||||
|
||||
31
refs.c
31
refs.c
@@ -643,6 +643,37 @@ int check_ref_format(const char *ref)
|
||||
}
|
||||
}
|
||||
|
||||
const char *ref_rev_parse_rules[] = {
|
||||
"%.*s",
|
||||
"refs/%.*s",
|
||||
"refs/tags/%.*s",
|
||||
"refs/heads/%.*s",
|
||||
"refs/remotes/%.*s",
|
||||
"refs/remotes/%.*s/HEAD",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *ref_fetch_rules[] = {
|
||||
"%.*s",
|
||||
"refs/%.*s",
|
||||
"refs/heads/%.*s",
|
||||
NULL
|
||||
};
|
||||
|
||||
int refname_match(const char *abbrev_name, const char *full_name, const char **rules)
|
||||
{
|
||||
const char **p;
|
||||
const int abbrev_name_len = strlen(abbrev_name);
|
||||
|
||||
for (p = rules; *p; p++) {
|
||||
if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ref_lock *verify_lock(struct ref_lock *lock,
|
||||
const unsigned char *old_sha1, int mustexist)
|
||||
{
|
||||
|
||||
30
remote.c
30
remote.c
@@ -278,6 +278,8 @@ static int handle_config(const char *key, const char *value)
|
||||
} else if (!strcmp(subkey, ".tagopt")) {
|
||||
if (!strcmp(value, "--no-tags"))
|
||||
remote->fetch_tags = -1;
|
||||
} else if (!strcmp(subkey, ".proxy")) {
|
||||
remote->http_proxy = xstrdup(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -417,25 +419,6 @@ int remote_has_url(struct remote *remote, const char *url)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if, under the matching rules for fetching, name is the
|
||||
* same as the given full name.
|
||||
*/
|
||||
static int ref_matches_abbrev(const char *name, const char *full)
|
||||
{
|
||||
if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD"))
|
||||
return !strcmp(name, full);
|
||||
if (prefixcmp(full, "refs/"))
|
||||
return 0;
|
||||
if (!prefixcmp(name, "heads/") ||
|
||||
!prefixcmp(name, "tags/") ||
|
||||
!prefixcmp(name, "remotes/"))
|
||||
return !strcmp(name, full + 5);
|
||||
if (prefixcmp(full + 5, "heads/"))
|
||||
return 0;
|
||||
return !strcmp(full + 11, name);
|
||||
}
|
||||
|
||||
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
|
||||
{
|
||||
int find_src = refspec->src == NULL;
|
||||
@@ -531,10 +514,7 @@ static int count_refspec_match(const char *pattern,
|
||||
char *name = refs->name;
|
||||
int namelen = strlen(name);
|
||||
|
||||
if (namelen < patlen ||
|
||||
memcmp(name + namelen - patlen, pattern, patlen))
|
||||
continue;
|
||||
if (namelen != patlen && name[namelen - patlen - 1] != '/')
|
||||
if (!refname_match(pattern, name, ref_rev_parse_rules))
|
||||
continue;
|
||||
|
||||
/* A match is "weak" if it is with refs outside
|
||||
@@ -816,7 +796,7 @@ int branch_merge_matches(struct branch *branch,
|
||||
{
|
||||
if (!branch || i < 0 || i >= branch->merge_nr)
|
||||
return 0;
|
||||
return ref_matches_abbrev(branch->merge[i]->src, refname);
|
||||
return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
|
||||
}
|
||||
|
||||
static struct ref *get_expanded_map(const struct ref *remote_refs,
|
||||
@@ -855,7 +835,7 @@ static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const c
|
||||
{
|
||||
const struct ref *ref;
|
||||
for (ref = refs; ref; ref = ref->next) {
|
||||
if (ref_matches_abbrev(name, ref->name))
|
||||
if (refname_match(name, ref->name, ref_fetch_rules))
|
||||
return ref;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
5
remote.h
5
remote.h
@@ -25,6 +25,11 @@ struct remote {
|
||||
|
||||
const char *receivepack;
|
||||
const char *uploadpack;
|
||||
|
||||
/*
|
||||
* for curl remotes only
|
||||
*/
|
||||
char *http_proxy;
|
||||
};
|
||||
|
||||
struct remote *remote_get(const char *name);
|
||||
|
||||
33
sha1_name.c
33
sha1_name.c
@@ -239,23 +239,13 @@ static int ambiguous_path(const char *path, int len)
|
||||
return slash;
|
||||
}
|
||||
|
||||
static const char *ref_fmt[] = {
|
||||
"%.*s",
|
||||
"refs/%.*s",
|
||||
"refs/tags/%.*s",
|
||||
"refs/heads/%.*s",
|
||||
"refs/remotes/%.*s",
|
||||
"refs/remotes/%.*s/HEAD",
|
||||
NULL
|
||||
};
|
||||
|
||||
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
|
||||
{
|
||||
const char **p, *r;
|
||||
int refs_found = 0;
|
||||
|
||||
*ref = NULL;
|
||||
for (p = ref_fmt; *p; p++) {
|
||||
for (p = ref_rev_parse_rules; *p; p++) {
|
||||
unsigned char sha1_from_ref[20];
|
||||
unsigned char *this_result;
|
||||
|
||||
@@ -277,7 +267,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
|
||||
int logs_found = 0;
|
||||
|
||||
*log = NULL;
|
||||
for (p = ref_fmt; *p; p++) {
|
||||
for (p = ref_rev_parse_rules; *p; p++) {
|
||||
struct stat st;
|
||||
unsigned char hash[20];
|
||||
char path[PATH_MAX];
|
||||
@@ -610,24 +600,35 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
|
||||
{
|
||||
struct commit_list *list = NULL, *backup = NULL, *l;
|
||||
int retval = -1;
|
||||
char *temp_commit_buffer = NULL;
|
||||
|
||||
if (prefix[0] == '!') {
|
||||
if (prefix[1] != '!')
|
||||
die ("Invalid search pattern: %s", prefix);
|
||||
prefix++;
|
||||
}
|
||||
if (!save_commit_buffer)
|
||||
return error("Could not expand oneline-name.");
|
||||
for_each_ref(handle_one_ref, &list);
|
||||
for (l = list; l; l = l->next)
|
||||
commit_list_insert(l->item, &backup);
|
||||
while (list) {
|
||||
char *p;
|
||||
struct commit *commit;
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
|
||||
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
|
||||
parse_object(commit->object.sha1);
|
||||
if (!commit->buffer || !(p = strstr(commit->buffer, "\n\n")))
|
||||
if (temp_commit_buffer)
|
||||
free(temp_commit_buffer);
|
||||
if (commit->buffer)
|
||||
p = commit->buffer;
|
||||
else {
|
||||
p = read_sha1_file(commit->object.sha1, &type, &size);
|
||||
if (!p)
|
||||
continue;
|
||||
temp_commit_buffer = p;
|
||||
}
|
||||
if (!(p = strstr(p, "\n\n")))
|
||||
continue;
|
||||
if (!prefixcmp(p + 2, prefix)) {
|
||||
hashcpy(sha1, commit->object.sha1);
|
||||
@@ -635,6 +636,8 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (temp_commit_buffer)
|
||||
free(temp_commit_buffer);
|
||||
free_commit_list(list);
|
||||
for (l = backup; l; l = l->next)
|
||||
clear_commit_marks(l->item, ONELINE_SEEN);
|
||||
|
||||
1
strbuf.h
1
strbuf.h
@@ -117,5 +117,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
|
||||
extern int strbuf_getline(struct strbuf *, FILE *, int);
|
||||
|
||||
extern void stripspace(struct strbuf *buf, int skip_comments);
|
||||
extern void launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
|
||||
|
||||
#endif /* STRBUF_H */
|
||||
|
||||
@@ -25,7 +25,7 @@ check_config () {
|
||||
|
||||
test_expect_success 'plain' '
|
||||
(
|
||||
unset GIT_DIR GIT_WORK_TREE &&
|
||||
unset GIT_DIR GIT_WORK_TREE
|
||||
mkdir plain &&
|
||||
cd plain &&
|
||||
git init
|
||||
@@ -35,7 +35,7 @@ test_expect_success 'plain' '
|
||||
|
||||
test_expect_success 'plain with GIT_WORK_TREE' '
|
||||
if (
|
||||
unset GIT_DIR &&
|
||||
unset GIT_DIR
|
||||
mkdir plain-wt &&
|
||||
cd plain-wt &&
|
||||
GIT_WORK_TREE=$(pwd) git init
|
||||
@@ -48,7 +48,7 @@ test_expect_success 'plain with GIT_WORK_TREE' '
|
||||
|
||||
test_expect_success 'plain bare' '
|
||||
(
|
||||
unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
|
||||
unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
|
||||
mkdir plain-bare-1 &&
|
||||
cd plain-bare-1 &&
|
||||
git --bare init
|
||||
@@ -58,7 +58,7 @@ test_expect_success 'plain bare' '
|
||||
|
||||
test_expect_success 'plain bare with GIT_WORK_TREE' '
|
||||
if (
|
||||
unset GIT_DIR GIT_CONFIG &&
|
||||
unset GIT_DIR GIT_CONFIG
|
||||
mkdir plain-bare-2 &&
|
||||
cd plain-bare-2 &&
|
||||
GIT_WORK_TREE=$(pwd) git --bare init
|
||||
@@ -72,7 +72,7 @@ test_expect_success 'plain bare with GIT_WORK_TREE' '
|
||||
test_expect_success 'GIT_DIR bare' '
|
||||
|
||||
(
|
||||
unset GIT_CONFIG &&
|
||||
unset GIT_CONFIG
|
||||
mkdir git-dir-bare.git &&
|
||||
GIT_DIR=git-dir-bare.git git init
|
||||
) &&
|
||||
@@ -82,7 +82,7 @@ test_expect_success 'GIT_DIR bare' '
|
||||
test_expect_success 'GIT_DIR non-bare' '
|
||||
|
||||
(
|
||||
unset GIT_CONFIG &&
|
||||
unset GIT_CONFIG
|
||||
mkdir non-bare &&
|
||||
cd non-bare &&
|
||||
GIT_DIR=.git git init
|
||||
@@ -93,7 +93,7 @@ test_expect_success 'GIT_DIR non-bare' '
|
||||
test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' '
|
||||
|
||||
(
|
||||
unset GIT_CONFIG &&
|
||||
unset GIT_CONFIG
|
||||
mkdir git-dir-wt-1.git &&
|
||||
GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-1.git git init
|
||||
) &&
|
||||
@@ -103,7 +103,7 @@ test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' '
|
||||
test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' '
|
||||
|
||||
if (
|
||||
unset GIT_CONFIG &&
|
||||
unset GIT_CONFIG
|
||||
mkdir git-dir-wt-2.git &&
|
||||
GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-2.git git --bare init
|
||||
)
|
||||
|
||||
@@ -175,4 +175,22 @@ test_expect_success 'recover and check' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'prune --expire' '
|
||||
|
||||
before=$(git count-objects | sed "s/ .*//") &&
|
||||
BLOB=$(echo aleph | git hash-object -w --stdin) &&
|
||||
BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
|
||||
test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
|
||||
test -f $BLOB_FILE &&
|
||||
git reset --hard &&
|
||||
git prune --expire=1.hour.ago &&
|
||||
test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
|
||||
test -f $BLOB_FILE &&
|
||||
test-chmtime -86500 $BLOB_FILE &&
|
||||
git prune --expire 1.day &&
|
||||
test $before = $(git count-objects | sed "s/ .*//") &&
|
||||
! test -f $BLOB_FILE
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -86,7 +86,7 @@ EOF
|
||||
|
||||
git config core.excludesFile excludes-file
|
||||
|
||||
git runstatus | grep "^# " > output
|
||||
git status | grep "^# " > output
|
||||
|
||||
cat > expect << EOF
|
||||
# .gitignore
|
||||
|
||||
58
t/t3201-branch-contains.sh
Executable file
58
t/t3201-branch-contains.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='branch --contains <commit>'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
>file &&
|
||||
git add file &&
|
||||
test_tick &&
|
||||
git commit -m initial &&
|
||||
git branch side &&
|
||||
|
||||
echo 1 >file &&
|
||||
test_tick &&
|
||||
git commit -a -m "second on master" &&
|
||||
|
||||
git checkout side &&
|
||||
echo 1 >file &&
|
||||
test_tick &&
|
||||
git commit -a -m "second on side" &&
|
||||
|
||||
git merge master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --contains=master' '
|
||||
|
||||
git branch --contains=master >actual &&
|
||||
{
|
||||
echo " master" && echo "* side"
|
||||
} >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --contains master' '
|
||||
|
||||
git branch --contains master >actual &&
|
||||
{
|
||||
echo " master" && echo "* side"
|
||||
} >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --contains=side' '
|
||||
|
||||
git branch --contains=side >actual &&
|
||||
{
|
||||
echo "* side"
|
||||
} >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -71,10 +71,10 @@ test_expect_success 'favour same basenames over different ones' '
|
||||
git rm path1 &&
|
||||
mkdir subdir &&
|
||||
git mv another-path subdir/path1 &&
|
||||
git runstatus | grep "renamed: .*path1 -> subdir/path1"'
|
||||
git status | grep "renamed: .*path1 -> subdir/path1"'
|
||||
|
||||
test_expect_success 'favour same basenames even with minor differences' '
|
||||
git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
|
||||
git runstatus | grep "renamed: .*path1 -> subdir/path1"'
|
||||
git status | grep "renamed: .*path1 -> subdir/path1"'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -121,14 +121,14 @@ test_expect_success \
|
||||
'compare_diff_raw expected current'
|
||||
|
||||
test_expect_success \
|
||||
'run diff with -B' \
|
||||
'run diff with -B -M' \
|
||||
'git diff-index -B -M "$tree" >current'
|
||||
|
||||
# This should not mistake file0 as the copy source of new file1
|
||||
# due to type differences.
|
||||
# file0 changed from regular to symlink. file1 is very close to the preimage of file0.
|
||||
# because we break file0, file1 can become a rename of it.
|
||||
cat >expected <<\EOF
|
||||
:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
|
||||
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
|
||||
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1
|
||||
EOF
|
||||
|
||||
test_expect_success \
|
||||
|
||||
86
t/t4023-diff-rename-typechange.sh
Executable file
86
t/t4023-diff-rename-typechange.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='typechange rename detection'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
rm -f foo bar &&
|
||||
cat ../../COPYING >foo &&
|
||||
ln -s linklink bar &&
|
||||
git add foo bar &&
|
||||
git commit -a -m Initial &&
|
||||
git tag one &&
|
||||
|
||||
rm -f foo bar &&
|
||||
cat ../../COPYING >bar &&
|
||||
ln -s linklink foo &&
|
||||
git add foo bar &&
|
||||
git commit -a -m Second &&
|
||||
git tag two &&
|
||||
|
||||
rm -f foo bar &&
|
||||
cat ../../COPYING >foo &&
|
||||
git add foo &&
|
||||
git commit -a -m Third &&
|
||||
git tag three &&
|
||||
|
||||
mv foo bar &&
|
||||
ln -s linklink foo &&
|
||||
git add foo bar &&
|
||||
git commit -a -m Fourth &&
|
||||
git tag four &&
|
||||
|
||||
# This is purely for sanity check
|
||||
|
||||
rm -f foo bar &&
|
||||
cat ../../COPYING >foo &&
|
||||
cat ../../Makefile >bar &&
|
||||
git add foo bar &&
|
||||
git commit -a -m Fifth &&
|
||||
git tag five &&
|
||||
|
||||
rm -f foo bar &&
|
||||
cat ../../Makefile >foo &&
|
||||
cat ../../COPYING >bar &&
|
||||
git add foo bar &&
|
||||
git commit -a -m Sixth &&
|
||||
git tag six
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'cross renames to be detected for regular files' '
|
||||
|
||||
git diff-tree five six -r --name-status -B -M | sort >actual &&
|
||||
{
|
||||
echo "R100 foo bar"
|
||||
echo "R100 bar foo"
|
||||
} | sort >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'cross renames to be detected for typechange' '
|
||||
|
||||
git diff-tree one two -r --name-status -B -M | sort >actual &&
|
||||
{
|
||||
echo "R100 foo bar"
|
||||
echo "R100 bar foo"
|
||||
} | sort >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'moves and renames' '
|
||||
|
||||
git diff-tree three four -r --name-status -B -M | sort >actual &&
|
||||
{
|
||||
echo "R100 foo bar"
|
||||
echo "T100 foo"
|
||||
} | sort >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -95,6 +95,31 @@ test_expect_success 'fetch following tags' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_failure 'fetch must not resolve short tag name' '
|
||||
|
||||
cd "$D" &&
|
||||
|
||||
mkdir five &&
|
||||
cd five &&
|
||||
git init &&
|
||||
|
||||
git fetch .. anno:five
|
||||
|
||||
'
|
||||
|
||||
test_expect_failure 'fetch must not resolve short remote name' '
|
||||
|
||||
cd "$D" &&
|
||||
git-update-ref refs/remotes/six/HEAD HEAD
|
||||
|
||||
mkdir six &&
|
||||
cd six &&
|
||||
git init &&
|
||||
|
||||
git fetch .. six:six
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'create bundle 1' '
|
||||
cd "$D" &&
|
||||
echo >file updated again by origin &&
|
||||
@@ -228,4 +253,46 @@ test_expect_success 'bundle should record HEAD correctly' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'explicit fetch should not update tracking' '
|
||||
|
||||
cd "$D" &&
|
||||
git branch -f side &&
|
||||
(
|
||||
cd three &&
|
||||
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||
git fetch origin master &&
|
||||
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||
test "$o" = "$n" &&
|
||||
! git rev-parse --verify refs/remotes/origin/side
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'explicit pull should not update tracking' '
|
||||
|
||||
cd "$D" &&
|
||||
git branch -f side &&
|
||||
(
|
||||
cd three &&
|
||||
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||
git pull origin master &&
|
||||
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||
test "$o" = "$n" &&
|
||||
! git rev-parse --verify refs/remotes/origin/side
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'configured fetch updates tracking' '
|
||||
|
||||
cd "$D" &&
|
||||
git branch -f side &&
|
||||
(
|
||||
cd three &&
|
||||
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||
git fetch origin &&
|
||||
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||
test "$o" != "$n" &&
|
||||
git rev-parse --verify refs/remotes/origin/side
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -145,11 +145,21 @@ test_expect_success 'push with no ambiguity (1)' '
|
||||
test_expect_success 'push with no ambiguity (2)' '
|
||||
|
||||
mk_test remotes/origin/master &&
|
||||
git push testrepo master:master &&
|
||||
git push testrepo master:origin/master &&
|
||||
check_push_result $the_commit remotes/origin/master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push with colon-less refspec, no ambiguity' '
|
||||
|
||||
mk_test heads/master heads/t/master &&
|
||||
git branch -f t/master master &&
|
||||
git push testrepo master &&
|
||||
check_push_result $the_commit heads/master &&
|
||||
check_push_result $the_first_commit heads/t/master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push with weak ambiguity (1)' '
|
||||
|
||||
mk_test heads/master remotes/origin/master &&
|
||||
@@ -244,6 +254,23 @@ test_expect_success 'push with colon-less refspec (4)' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push with HEAD' '
|
||||
|
||||
mk_test heads/master &&
|
||||
git checkout master &&
|
||||
git push testrepo HEAD &&
|
||||
check_push_result $the_commit heads/master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push with HEAD nonexisting at remote' '
|
||||
|
||||
mk_test heads/master &&
|
||||
git checkout -b local master &&
|
||||
git push testrepo HEAD &&
|
||||
check_push_result $the_commit heads/local
|
||||
'
|
||||
|
||||
test_expect_success 'push with dry-run' '
|
||||
|
||||
mk_test heads/master &&
|
||||
|
||||
@@ -53,4 +53,26 @@ test_expect_success 'the default remote . should not break explicit pull' '
|
||||
test `cat file` = modified
|
||||
'
|
||||
|
||||
test_expect_success '--rebase' '
|
||||
git branch to-rebase &&
|
||||
echo modified again > file &&
|
||||
git commit -m file file &&
|
||||
git checkout to-rebase &&
|
||||
echo new > file2 &&
|
||||
git add file2 &&
|
||||
git commit -m "new file" &&
|
||||
git tag before-rebase &&
|
||||
git pull --rebase . copy &&
|
||||
test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
|
||||
test new = $(git show HEAD:file2)
|
||||
'
|
||||
|
||||
test_expect_success 'branch.to-rebase.rebase' '
|
||||
git reset --hard before-rebase &&
|
||||
git config branch.to-rebase.rebase 1 &&
|
||||
git pull . copy &&
|
||||
test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
|
||||
test new = $(git show HEAD:file2)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -340,20 +340,14 @@ test_expect_success \
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'trying to create tags giving many -m or -F options should fail' '
|
||||
'trying to create tags giving both -m or -F options should fail' '
|
||||
echo "message file 1" >msgfile1 &&
|
||||
echo "message file 2" >msgfile2 &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -m "message 1" -m "message 2" msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -F msgfile1 -F msgfile2 msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -m "message 1" -F msgfile1 msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -F msgfile1 -m "message 1" msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag &&
|
||||
! tag_exists msgtag
|
||||
'
|
||||
@@ -674,6 +668,22 @@ test_expect_success 'creating a signed tag with -F - should succeed' '
|
||||
git diff expect actual
|
||||
'
|
||||
|
||||
cat >fakeeditor <<'EOF'
|
||||
#!/bin/sh
|
||||
test -n "$1" && exec >"$1"
|
||||
echo A signed tag message
|
||||
echo from a fake editor.
|
||||
EOF
|
||||
chmod +x fakeeditor
|
||||
get_tag_header implied-annotate $commit commit $time >expect
|
||||
./fakeeditor >>expect
|
||||
echo '-----BEGIN PGP SIGNATURE-----' >>expect
|
||||
test_expect_success '-s implies annotated tag' '
|
||||
GIT_EDITOR=./fakeeditor git-tag -s implied-annotate &&
|
||||
get_tag_msg implied-annotate >actual &&
|
||||
git diff expect actual
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'trying to create a signed tag with non-existing -F file should fail' '
|
||||
! test -f nonexistingfile &&
|
||||
|
||||
@@ -122,7 +122,20 @@ test_expect_success 'using alternate GIT_INDEX_FILE (2)' '
|
||||
|
||||
) &&
|
||||
cmp .git/index saved-index >/dev/null
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
zort
|
||||
|
||||
Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
|
||||
test_expect_success '--signoff' '
|
||||
echo "yet another content *narf*" >> foo &&
|
||||
echo "zort" |
|
||||
GIT_EDITOR=../t7500/add-content git commit -s -F - foo &&
|
||||
git cat-file commit HEAD | sed "1,/^$/d" > output &&
|
||||
diff expect output
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -244,4 +244,70 @@ test_expect_success 'multiple -m' '
|
||||
|
||||
'
|
||||
|
||||
author="The Real Author <someguy@his.email.org>"
|
||||
test_expect_success 'amend commit to fix author' '
|
||||
|
||||
oldtick=$GIT_AUTHOR_DATE &&
|
||||
test_tick &&
|
||||
git reset --hard &&
|
||||
git cat-file -p HEAD |
|
||||
sed -e "s/author.*/author $author $oldtick/" \
|
||||
-e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
|
||||
expected &&
|
||||
git commit --amend --author="$author" &&
|
||||
git cat-file -p HEAD > current &&
|
||||
diff expected current
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git commit <file> with dirty index' '
|
||||
echo tacocat > elif &&
|
||||
echo tehlulz > chz &&
|
||||
git add chz &&
|
||||
git commit elif -m "tacocat is a palindrome" &&
|
||||
git show --stat | grep elif &&
|
||||
git diff --cached | grep chz
|
||||
'
|
||||
|
||||
test_expect_success 'same tree (single parent)' '
|
||||
|
||||
git reset --hard
|
||||
|
||||
if git commit -m empty
|
||||
then
|
||||
echo oops -- should have complained
|
||||
false
|
||||
else
|
||||
: happy
|
||||
fi
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'same tree (single parent) --allow-empty' '
|
||||
|
||||
git commit --allow-empty -m "forced empty" &&
|
||||
git cat-file commit HEAD | grep forced
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'same tree (merge and amend merge)' '
|
||||
|
||||
git checkout -b side HEAD^ &&
|
||||
echo zero >zero &&
|
||||
git add zero &&
|
||||
git commit -m "add zero" &&
|
||||
git checkout master &&
|
||||
|
||||
git merge -s ours side -m "empty ok" &&
|
||||
git diff HEAD^ HEAD >actual &&
|
||||
: >expected &&
|
||||
diff -u expected actual &&
|
||||
|
||||
git commit --amend -m "empty really ok" &&
|
||||
git diff HEAD^ HEAD >actual &&
|
||||
: >expected &&
|
||||
diff -u expected actual
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
92
t/t7502-commit.sh
Executable file
92
t/t7502-commit.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git commit porcelain-ish'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'the basics' '
|
||||
|
||||
echo doing partial >"commit is" &&
|
||||
mkdir not &&
|
||||
echo very much encouraged but we should >not/forbid &&
|
||||
git add "commit is" not &&
|
||||
echo update added "commit is" file >"commit is" &&
|
||||
echo also update another >not/forbid &&
|
||||
test_tick &&
|
||||
git commit -a -m "initial with -a" &&
|
||||
|
||||
git cat-file blob HEAD:"commit is" >current.1 &&
|
||||
git cat-file blob HEAD:not/forbid >current.2 &&
|
||||
|
||||
cmp current.1 "commit is" &&
|
||||
cmp current.2 not/forbid
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'partial' '
|
||||
|
||||
echo another >"commit is" &&
|
||||
echo another >not/forbid &&
|
||||
test_tick &&
|
||||
git commit -m "partial commit to handle a file" "commit is" &&
|
||||
|
||||
changed=$(git diff-tree --name-only HEAD^ HEAD) &&
|
||||
test "$changed" = "commit is"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'partial modification in a subdirecotry' '
|
||||
|
||||
test_tick &&
|
||||
git commit -m "partial commit to subdirectory" not &&
|
||||
|
||||
changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
|
||||
test "$changed" = "not/forbid"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'partial removal' '
|
||||
|
||||
git rm not/forbid &&
|
||||
git commit -m "partial commit to remove not/forbid" not &&
|
||||
|
||||
changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
|
||||
test "$changed" = "not/forbid" &&
|
||||
remain=$(git ls-tree -r --name-only HEAD) &&
|
||||
test "$remain" = "commit is"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'sign off' '
|
||||
|
||||
>positive &&
|
||||
git add positive &&
|
||||
git commit -s -m "thank you" &&
|
||||
actual=$(git cat-file commit HEAD | sed -ne "s/Signed-off-by: //p") &&
|
||||
expected=$(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/") &&
|
||||
test "z$actual" = "z$expected"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'multiple -m' '
|
||||
|
||||
>negative &&
|
||||
git add negative &&
|
||||
git commit -m "one" -m "two" -m "three" &&
|
||||
actual=$(git cat-file commit HEAD | sed -e "1,/^\$/d") &&
|
||||
expected=$(echo one; echo; echo two; echo; echo three) &&
|
||||
test "z$actual" = "z$expected"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'verbose' '
|
||||
|
||||
echo minus >negative &&
|
||||
git add negative &&
|
||||
git status -v | sed -ne "/^diff --git /p" >actual &&
|
||||
echo "diff --git a/negative b/negative" >expect &&
|
||||
diff -u expect actual
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
91
t/t7502-status.sh
Executable file
91
t/t7502-status.sh
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Johannes E. Schindelin
|
||||
#
|
||||
|
||||
test_description='git-status'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
: > tracked &&
|
||||
: > modified &&
|
||||
mkdir dir1 &&
|
||||
: > dir1/tracked &&
|
||||
: > dir1/modified &&
|
||||
mkdir dir2 &&
|
||||
: > dir1/tracked &&
|
||||
: > dir1/modified &&
|
||||
git add . &&
|
||||
test_tick &&
|
||||
git commit -m initial &&
|
||||
: > untracked &&
|
||||
: > dir1/untracked &&
|
||||
: > dir2/untracked &&
|
||||
echo 1 > dir1/modified &&
|
||||
echo 2 > dir2/modified &&
|
||||
echo 3 > dir2/added &&
|
||||
git add dir2/added
|
||||
'
|
||||
|
||||
cat > expect << \EOF
|
||||
# On branch master
|
||||
# Changes to be committed:
|
||||
# (use "git reset HEAD <file>..." to unstage)
|
||||
#
|
||||
# new file: dir2/added
|
||||
#
|
||||
# Changed but not updated:
|
||||
# (use "git add <file>..." to update what will be committed)
|
||||
#
|
||||
# modified: dir1/modified
|
||||
#
|
||||
# Untracked files:
|
||||
# (use "git add <file>..." to include in what will be committed)
|
||||
#
|
||||
# dir1/untracked
|
||||
# dir2/modified
|
||||
# dir2/untracked
|
||||
# expect
|
||||
# output
|
||||
# untracked
|
||||
EOF
|
||||
|
||||
test_expect_success 'status' '
|
||||
|
||||
git status > output &&
|
||||
git diff expect output
|
||||
|
||||
'
|
||||
|
||||
cat > expect << \EOF
|
||||
# On branch master
|
||||
# Changes to be committed:
|
||||
# (use "git reset HEAD <file>..." to unstage)
|
||||
#
|
||||
# new file: ../dir2/added
|
||||
#
|
||||
# Changed but not updated:
|
||||
# (use "git add <file>..." to update what will be committed)
|
||||
#
|
||||
# modified: modified
|
||||
#
|
||||
# Untracked files:
|
||||
# (use "git add <file>..." to include in what will be committed)
|
||||
#
|
||||
# untracked
|
||||
# ../dir2/modified
|
||||
# ../dir2/untracked
|
||||
# ../expect
|
||||
# ../output
|
||||
# ../untracked
|
||||
EOF
|
||||
|
||||
test_expect_success 'status with relative paths' '
|
||||
|
||||
(cd dir1 && git status) > output &&
|
||||
git diff expect output
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
123
t/t9301-fast-export.sh
Executable file
123
t/t9301-fast-export.sh
Executable file
@@ -0,0 +1,123 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Johannes E. Schindelin
|
||||
#
|
||||
|
||||
test_description='git-fast-export'
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
|
||||
echo Wohlauf > file &&
|
||||
git add file &&
|
||||
test_tick &&
|
||||
git commit -m initial &&
|
||||
echo die Luft > file &&
|
||||
echo geht frisch > file2 &&
|
||||
git add file file2 &&
|
||||
test_tick &&
|
||||
git commit -m second &&
|
||||
echo und > file2 &&
|
||||
test_tick &&
|
||||
git commit -m third file2 &&
|
||||
test_tick &&
|
||||
git tag rein &&
|
||||
git checkout -b wer HEAD^ &&
|
||||
echo lange > file2
|
||||
test_tick &&
|
||||
git commit -m sitzt file2 &&
|
||||
test_tick &&
|
||||
git tag -a -m valentin muss &&
|
||||
git merge -s ours master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'fast-export | fast-import' '
|
||||
|
||||
MASTER=$(git rev-parse --verify master) &&
|
||||
REIN=$(git rev-parse --verify rein) &&
|
||||
WER=$(git rev-parse --verify wer) &&
|
||||
MUSS=$(git rev-parse --verify muss) &&
|
||||
mkdir new &&
|
||||
git --git-dir=new/.git init &&
|
||||
git fast-export --all |
|
||||
(cd new &&
|
||||
git fast-import &&
|
||||
test $MASTER = $(git rev-parse --verify refs/heads/master) &&
|
||||
test $REIN = $(git rev-parse --verify refs/tags/rein) &&
|
||||
test $WER = $(git rev-parse --verify refs/heads/wer) &&
|
||||
test $MUSS = $(git rev-parse --verify refs/tags/muss))
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'fast-export master~2..master' '
|
||||
|
||||
git fast-export master~2..master |
|
||||
sed "s/master/partial/" |
|
||||
(cd new &&
|
||||
git fast-import &&
|
||||
test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
|
||||
git diff master..partial &&
|
||||
git diff master^..partial^ &&
|
||||
! git rev-parse partial~2)
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'iso-8859-1' '
|
||||
|
||||
git config i18n.commitencoding ISO-8859-1 &&
|
||||
# use author and committer name in ISO-8859-1 to match it.
|
||||
. ../t3901-8859-1.txt &&
|
||||
test_tick &&
|
||||
echo rosten >file &&
|
||||
git commit -s -m den file &&
|
||||
git fast-export wer^..wer |
|
||||
sed "s/wer/i18n/" |
|
||||
(cd new &&
|
||||
git fast-import &&
|
||||
git cat-file commit i18n | grep "Áéí óú")
|
||||
|
||||
'
|
||||
|
||||
cat > signed-tag-import << EOF
|
||||
tag sign-your-name
|
||||
from $(git rev-parse HEAD)
|
||||
tagger C O Mitter <committer@example.com> 1112911993 -0700
|
||||
data 210
|
||||
A message for a sign
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.5 (GNU/Linux)
|
||||
|
||||
fakedsignaturefakedsignaturefakedsignaturefakedsignaturfakedsign
|
||||
aturefakedsignaturefake=
|
||||
=/59v
|
||||
-----END PGP SIGNATURE-----
|
||||
EOF
|
||||
|
||||
test_expect_success 'set up faked signed tag' '
|
||||
|
||||
cat signed-tag-import | git fast-import
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'signed-tags=abort' '
|
||||
|
||||
! git fast-export --signed-tags=abort sign-your-name
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'signed-tags=verbatim' '
|
||||
|
||||
git fast-export --signed-tags=verbatim sign-your-name > output &&
|
||||
grep PGP output
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'signed-tags=strip' '
|
||||
|
||||
git fast-export --signed-tags=strip sign-your-name > output &&
|
||||
! grep PGP output
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -3,13 +3,29 @@
|
||||
test_description='git-cvsimport basic tests'
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! ( type cvs && type cvsps ) >/dev/null 2>&1
|
||||
if ! type cvs >/dev/null 2>&1
|
||||
then
|
||||
test_expect_success 'skipping cvsimport tests, cvs/cvsps not found' ''
|
||||
say 'skipping cvsimport tests, cvs not found'
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
|
||||
cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
|
||||
case "$cvsps_version" in
|
||||
2.1)
|
||||
;;
|
||||
'')
|
||||
say 'skipping cvsimport tests, cvsps not found'
|
||||
test_done
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
say 'skipping cvsimport tests, cvsps too old'
|
||||
test_done
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
CVSROOT=$(pwd)/cvsroot
|
||||
export CVSROOT
|
||||
# for clean cvsps cache
|
||||
@@ -119,4 +135,16 @@ test_expect_success 'cvsimport.module config works' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'import from a CVS working tree' '
|
||||
|
||||
cvs co -d import-from-wt module &&
|
||||
cd import-from-wt &&
|
||||
git cvsimport -a -z0 &&
|
||||
echo 1 >expect &&
|
||||
git log -1 --pretty=format:%s%n >actual &&
|
||||
git diff actual expect &&
|
||||
cd ..
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -52,6 +52,6 @@ clean:
|
||||
$(RM) -r blt boilerplates.made
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(template_dir_SQ)'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(template_dir_SQ)'
|
||||
(cd blt && $(TAR) cf - .) | \
|
||||
(cd '$(DESTDIR_SQ)$(template_dir_SQ)' && $(TAR) xf -)
|
||||
|
||||
4
trace.c
4
trace.c
@@ -93,7 +93,7 @@ void trace_printf(const char *fmt, ...)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void trace_argv_printf(const char **argv, int count, const char *fmt, ...)
|
||||
void trace_argv_printf(const char **argv, const char *fmt, ...)
|
||||
{
|
||||
struct strbuf buf;
|
||||
va_list ap;
|
||||
@@ -117,7 +117,7 @@ void trace_argv_printf(const char **argv, int count, const char *fmt, ...)
|
||||
}
|
||||
strbuf_setlen(&buf, len);
|
||||
|
||||
sq_quote_argv(&buf, argv, count, 0);
|
||||
sq_quote_argv(&buf, argv, 0);
|
||||
strbuf_addch(&buf, '\n');
|
||||
write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
|
||||
strbuf_release(&buf);
|
||||
|
||||
@@ -470,6 +470,10 @@ static struct ref *get_refs_via_curl(struct transport *transport)
|
||||
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
|
||||
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
|
||||
if (transport->remote->http_proxy)
|
||||
curl_easy_setopt(slot->curl, CURLOPT_PROXY,
|
||||
transport->remote->http_proxy);
|
||||
|
||||
if (start_active_slot(slot)) {
|
||||
run_active_slot(slot);
|
||||
if (results.curl_result != CURLE_OK) {
|
||||
|
||||
@@ -7,13 +7,6 @@ struct name_entry {
|
||||
unsigned int mode;
|
||||
};
|
||||
|
||||
static inline enum object_type object_type(unsigned int mode)
|
||||
{
|
||||
return S_ISDIR(mode) ? OBJ_TREE :
|
||||
S_ISGITLINK(mode) ? OBJ_COMMIT :
|
||||
OBJ_BLOB;
|
||||
}
|
||||
|
||||
struct tree_desc {
|
||||
const void *buffer;
|
||||
struct name_entry entry;
|
||||
|
||||
87
wt-status.c
87
wt-status.c
@@ -81,33 +81,47 @@ static void wt_status_print_trailer(struct wt_status *s)
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
||||
}
|
||||
|
||||
static const char *quote_crlf(const char *in, char *buf, size_t sz)
|
||||
static char *quote_path(const char *in, int len,
|
||||
struct strbuf *out, const char *prefix)
|
||||
{
|
||||
const char *scan;
|
||||
char *out;
|
||||
const char *ret = in;
|
||||
if (len < 0)
|
||||
len = strlen(in);
|
||||
|
||||
for (scan = in, out = buf; *scan; scan++) {
|
||||
int ch = *scan;
|
||||
int quoted;
|
||||
strbuf_grow(out, len);
|
||||
strbuf_setlen(out, 0);
|
||||
if (prefix) {
|
||||
int off = 0;
|
||||
while (prefix[off] && off < len && prefix[off] == in[off])
|
||||
if (prefix[off] == '/') {
|
||||
prefix += off + 1;
|
||||
in += off + 1;
|
||||
len -= off + 1;
|
||||
off = 0;
|
||||
} else
|
||||
off++;
|
||||
|
||||
for (; *prefix; prefix++)
|
||||
if (*prefix == '/')
|
||||
strbuf_addstr(out, "../");
|
||||
}
|
||||
|
||||
for ( ; len > 0; in++, len--) {
|
||||
int ch = *in;
|
||||
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
quoted = 'n';
|
||||
strbuf_addstr(out, "\\n");
|
||||
break;
|
||||
case '\r':
|
||||
quoted = 'r';
|
||||
strbuf_addstr(out, "\\r");
|
||||
break;
|
||||
default:
|
||||
*out++ = ch;
|
||||
strbuf_addch(out, ch);
|
||||
continue;
|
||||
}
|
||||
*out++ = '\\';
|
||||
*out++ = quoted;
|
||||
ret = buf;
|
||||
}
|
||||
*out = '\0';
|
||||
return ret;
|
||||
|
||||
return out->buf;
|
||||
}
|
||||
|
||||
static void wt_status_print_filepair(struct wt_status *s,
|
||||
@@ -115,10 +129,12 @@ static void wt_status_print_filepair(struct wt_status *s,
|
||||
{
|
||||
const char *c = color(t);
|
||||
const char *one, *two;
|
||||
char onebuf[PATH_MAX], twobuf[PATH_MAX];
|
||||
struct strbuf onebuf, twobuf;
|
||||
|
||||
one = quote_crlf(p->one->path, onebuf, sizeof(onebuf));
|
||||
two = quote_crlf(p->two->path, twobuf, sizeof(twobuf));
|
||||
strbuf_init(&onebuf, 0);
|
||||
strbuf_init(&twobuf, 0);
|
||||
one = quote_path(p->one->path, -1, &onebuf, s->prefix);
|
||||
two = quote_path(p->two->path, -1, &twobuf, s->prefix);
|
||||
|
||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
|
||||
switch (p->status) {
|
||||
@@ -150,6 +166,8 @@ static void wt_status_print_filepair(struct wt_status *s,
|
||||
die("bug: unhandled diff status %c", p->status);
|
||||
}
|
||||
fprintf(s->fp, "\n");
|
||||
strbuf_release(&onebuf);
|
||||
strbuf_release(&twobuf);
|
||||
}
|
||||
|
||||
static void wt_status_print_updated_cb(struct diff_queue_struct *q,
|
||||
@@ -204,8 +222,9 @@ static void wt_read_cache(struct wt_status *s)
|
||||
static void wt_status_print_initial(struct wt_status *s)
|
||||
{
|
||||
int i;
|
||||
char buf[PATH_MAX];
|
||||
struct strbuf buf;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
wt_read_cache(s);
|
||||
if (active_nr) {
|
||||
s->commitable = 1;
|
||||
@@ -214,11 +233,12 @@ static void wt_status_print_initial(struct wt_status *s)
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s",
|
||||
quote_crlf(active_cache[i]->name,
|
||||
buf, sizeof(buf)));
|
||||
quote_path(active_cache[i]->name, -1,
|
||||
&buf, s->prefix));
|
||||
}
|
||||
if (active_nr)
|
||||
wt_status_print_trailer(s);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void wt_status_print_updated(struct wt_status *s)
|
||||
@@ -231,6 +251,7 @@ static void wt_status_print_updated(struct wt_status *s)
|
||||
rev.diffopt.format_callback_data = s;
|
||||
rev.diffopt.detect_rename = 1;
|
||||
rev.diffopt.rename_limit = 100;
|
||||
rev.diffopt.break_opt = 0;
|
||||
wt_read_cache(s);
|
||||
run_diff_index(&rev, 1);
|
||||
}
|
||||
@@ -252,7 +273,9 @@ static void wt_status_print_untracked(struct wt_status *s)
|
||||
struct dir_struct dir;
|
||||
int i;
|
||||
int shown_header = 0;
|
||||
struct strbuf buf;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
|
||||
if (!s->untracked) {
|
||||
@@ -284,20 +307,38 @@ static void wt_status_print_untracked(struct wt_status *s)
|
||||
shown_header = 1;
|
||||
}
|
||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%.*s",
|
||||
ent->len, ent->name);
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
|
||||
quote_path(ent->name, ent->len,
|
||||
&buf, s->prefix));
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void wt_status_print_verbose(struct wt_status *s)
|
||||
{
|
||||
struct rev_info rev;
|
||||
int saved_stdout;
|
||||
|
||||
fflush(s->fp);
|
||||
|
||||
/* Sigh, the entire diff machinery is hardcoded to output to
|
||||
* stdout. Do the dup-dance...*/
|
||||
saved_stdout = dup(STDOUT_FILENO);
|
||||
if (saved_stdout < 0 ||dup2(fileno(s->fp), STDOUT_FILENO) < 0)
|
||||
die("couldn't redirect stdout\n");
|
||||
|
||||
init_revisions(&rev, NULL);
|
||||
setup_revisions(0, NULL, &rev, s->reference);
|
||||
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
|
||||
rev.diffopt.detect_rename = 1;
|
||||
wt_read_cache(s);
|
||||
run_diff_index(&rev, 1);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
if (dup2(saved_stdout, STDOUT_FILENO) < 0)
|
||||
die("couldn't restore stdout\n");
|
||||
close(saved_stdout);
|
||||
}
|
||||
|
||||
void wt_status_print(struct wt_status *s)
|
||||
|
||||
@@ -23,9 +23,11 @@ struct wt_status {
|
||||
int workdir_untracked;
|
||||
const char *index_file;
|
||||
FILE *fp;
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
int git_status_config(const char *var, const char *value);
|
||||
int wt_status_use_color;
|
||||
void wt_status_prepare(struct wt_status *s);
|
||||
void wt_status_print(struct wt_status *s);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user