mirror of
https://github.com/git/git.git
synced 2026-03-13 18:33:25 +01:00
Merge branch 'master' of git://repo.or.cz/alt-git.git
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -128,7 +128,6 @@ git-status
|
||||
git-stripspace
|
||||
git-submodule
|
||||
git-svn
|
||||
git-svnimport
|
||||
git-symbolic-ref
|
||||
git-tag
|
||||
git-tar-tree
|
||||
|
||||
1
.mailmap
1
.mailmap
@@ -37,6 +37,7 @@ Sam Vilain <sam@vilain.net>
|
||||
Santi Béjar <sbejar@gmail.com>
|
||||
Sean Estabrooks <seanlkml@sympatico.ca>
|
||||
Shawn O. Pearce <spearce@spearce.org>
|
||||
Steven Grimm <koreth@midwinter.com>
|
||||
Theodore Ts'o <tytso@mit.edu>
|
||||
Tony Luck <tony.luck@intel.com>
|
||||
Uwe Kleine-König <Uwe_Zeisberger@digi.com>
|
||||
|
||||
@@ -71,3 +71,24 @@ Fixes since v1.5.3.4
|
||||
|
||||
* "make clean" no longer deletes the configure script that ships
|
||||
with the git tarball, making multiple architecture builds easier.
|
||||
|
||||
* "git-remote show origin" spewed a warning message from Perl
|
||||
when no remote is defined for the current branch via
|
||||
branch.<name>.remote configuration settings.
|
||||
|
||||
* Building with NO_PERL_MAKEMAKER excessively rebuilt contents
|
||||
of perl/ subdirectory by rewriting perl.mak.
|
||||
|
||||
* http.sslVerify configuration settings were not used in scripted
|
||||
Porcelains.
|
||||
|
||||
* "git-add" leaked a bit of memory while scanning for files to add.
|
||||
|
||||
* A few workarounds to squelch false warnings from recent gcc have
|
||||
been added.
|
||||
|
||||
* "git-send-pack $remote frotz" segfaulted when there is nothing
|
||||
named 'frotz' on the local end.
|
||||
|
||||
* "git-rebase -interactive" did not handle its "--strategy" option
|
||||
properly.
|
||||
|
||||
@@ -4,6 +4,8 @@ GIT v1.5.4 Release Notes
|
||||
Updates since v1.5.3
|
||||
--------------------
|
||||
|
||||
* Comes with much improved gitk.
|
||||
|
||||
* git-reset is now built-in.
|
||||
|
||||
* git-send-email can optionally talk over ssmtp and use SMTP-AUTH.
|
||||
@@ -19,6 +21,29 @@ Updates since v1.5.3
|
||||
* 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.
|
||||
|
||||
* git-push has been rewritten in C.
|
||||
|
||||
* git-push learned --dry-run option to show what would happen
|
||||
if a push is run.
|
||||
|
||||
* 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.
|
||||
|
||||
* Various Perforce importer updates.
|
||||
|
||||
Fixes since v1.5.3
|
||||
@@ -29,7 +54,6 @@ this release, unless otherwise noted.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.3.2-99-ge4b2890
|
||||
O=v1.5.3.4-450-g952a9e5
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
|
||||
|
||||
@@ -185,7 +185,6 @@ git-status mainporcelain
|
||||
git-stripspace purehelpers
|
||||
git-submodule mainporcelain
|
||||
git-svn foreignscminterface
|
||||
git-svnimport foreignscminterface
|
||||
git-symbolic-ref plumbingmanipulators
|
||||
git-tag mainporcelain
|
||||
git-tar-tree plumbinginterrogators
|
||||
|
||||
@@ -553,13 +553,8 @@ can explore on your own.
|
||||
|
||||
[NOTE]
|
||||
Most likely, you are not directly using the core
|
||||
git Plumbing commands, but using Porcelain like Cogito on top
|
||||
of it. Cogito works a bit differently and you usually do not
|
||||
have to run `git-update-index` yourself for changed files (you
|
||||
do tell underlying git about additions and removals via
|
||||
`cg-add` and `cg-rm` commands). Just before you make a commit
|
||||
with `cg-commit`, Cogito figures out which files you modified,
|
||||
and runs `git-update-index` on them for you.
|
||||
git Plumbing commands, but using Porcelain such as `git-add`, `git-rm'
|
||||
and `git-commit'.
|
||||
|
||||
|
||||
Tagging a version
|
||||
@@ -686,8 +681,8 @@ $ git reset
|
||||
|
||||
and in fact a lot of the common git command combinations can be scripted
|
||||
with the `git xyz` interfaces. You can learn things by just looking
|
||||
at what the various git scripts do. For example, `git reset` is the
|
||||
above two lines implemented in `git-reset`, but some things like
|
||||
at what the various git scripts do. For example, `git reset` used to be
|
||||
the above two lines implemented in `git-reset`, but some things like
|
||||
`git status` and `git commit` are slightly more complex scripts around
|
||||
the basic git commands.
|
||||
|
||||
@@ -805,8 +800,8 @@ you have, you can say
|
||||
$ git branch
|
||||
------------
|
||||
|
||||
which is nothing more than a simple script around `ls .git/refs/heads`.
|
||||
There will be asterisk in front of the branch you are currently on.
|
||||
which used to be nothing more than a simple script around `ls .git/refs/heads`.
|
||||
There will be an asterisk in front of the branch you are currently on.
|
||||
|
||||
Sometimes you may wish to create a new branch _without_ actually
|
||||
checking it out and switching to it. If so, just use the command
|
||||
@@ -883,7 +878,7 @@ script called `git merge`, which wants to know which branches you want
|
||||
to resolve and what the merge is all about:
|
||||
|
||||
------------
|
||||
$ git merge "Merge work in mybranch" HEAD mybranch
|
||||
$ git merge -m "Merge work in mybranch" mybranch
|
||||
------------
|
||||
|
||||
where the first argument is going to be used as the commit message if
|
||||
@@ -952,7 +947,7 @@ the later output lines is used to show commits contained in the
|
||||
`master` branch, and the second column for the `mybranch`
|
||||
branch. Three commits are shown along with their log messages.
|
||||
All of them have non blank characters in the first column (`*`
|
||||
shows an ordinary commit on the current branch, `.` is a merge commit), which
|
||||
shows an ordinary commit on the current branch, `-` is a merge commit), which
|
||||
means they are now part of the `master` branch. Only the "Some
|
||||
work" commit has the plus `+` character in the second column,
|
||||
because `mybranch` has not been merged to incorporate these
|
||||
@@ -970,7 +965,7 @@ to the `master` branch. Let's go back to `mybranch`, and run
|
||||
|
||||
------------
|
||||
$ git checkout mybranch
|
||||
$ git merge "Merge upstream changes." HEAD master
|
||||
$ git merge -m "Merge upstream changes." master
|
||||
------------
|
||||
|
||||
This outputs something like this (the actual commit object names
|
||||
@@ -1086,7 +1081,7 @@ to help dumb transport downloaders.
|
||||
There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
|
||||
programs, which are 'commit walkers'; they outlived their
|
||||
usefulness when git Native and SSH transports were introduced,
|
||||
and not used by `git pull` or `git push` scripts.
|
||||
and are not used by `git pull` or `git push` scripts.
|
||||
|
||||
Once you fetch from the remote repository, you `merge` that
|
||||
with your current branch.
|
||||
@@ -1193,7 +1188,7 @@ $ mb=$(git-merge-base HEAD mybranch)
|
||||
|
||||
The command writes the commit object name of the common ancestor
|
||||
to the standard output, so we captured its output to a variable,
|
||||
because we will be using it in the next step. BTW, the common
|
||||
because we will be using it in the next step. By the way, the common
|
||||
ancestor commit is the "New day." commit in this case. You can
|
||||
tell it by:
|
||||
|
||||
@@ -1459,8 +1454,7 @@ Although git is a truly distributed system, it is often
|
||||
convenient to organize your project with an informal hierarchy
|
||||
of developers. Linux kernel development is run this way. There
|
||||
is a nice illustration (page 17, "Merges to Mainline") in
|
||||
link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf
|
||||
[Randy Dunlap's presentation].
|
||||
link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf[Randy Dunlap's presentation].
|
||||
|
||||
It should be stressed that this hierarchy is purely *informal*.
|
||||
There is nothing fundamental in git that enforces the "chain of
|
||||
@@ -1613,8 +1607,8 @@ in both of them. You could merge in 'diff-fix' first and then
|
||||
'commit-fix' next, like this:
|
||||
|
||||
------------
|
||||
$ git merge 'Merge fix in diff-fix' master diff-fix
|
||||
$ git merge 'Merge fix in commit-fix' master commit-fix
|
||||
$ git merge -m 'Merge fix in diff-fix' diff-fix
|
||||
$ git merge -m 'Merge fix in commit-fix' commit-fix
|
||||
------------
|
||||
|
||||
Which would result in:
|
||||
|
||||
@@ -16,8 +16,9 @@ The command takes various subcommands, and different options depending
|
||||
on the subcommand:
|
||||
|
||||
git bisect start [<bad> [<good>...]] [--] [<paths>...]
|
||||
git bisect bad <rev>
|
||||
git bisect good <rev>
|
||||
git bisect bad [<rev>]
|
||||
git bisect good [<rev>...]
|
||||
git bisect skip [<rev>...]
|
||||
git bisect reset [<branch>]
|
||||
git bisect visualize
|
||||
git bisect replay <logfile>
|
||||
@@ -134,6 +135,20 @@ $ git reset --hard HEAD~3 # try 3 revs before what
|
||||
Then compile and test the one you chose to try. After that, tell
|
||||
bisect what the result was as usual.
|
||||
|
||||
Bisect skip
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Instead of choosing by yourself a nearby commit, you may just want git
|
||||
to do it for you using:
|
||||
|
||||
------------
|
||||
$ git bisect skip # Current version cannot be tested
|
||||
------------
|
||||
|
||||
But computing the commit to test may be slower afterwards and git may
|
||||
eventually not be able to tell the first bad among a bad and one or
|
||||
more "skip"ped commits.
|
||||
|
||||
Cutting down bisection by giving more parameters to bisect start
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -167,14 +182,18 @@ $ git bisect run my_script
|
||||
------------
|
||||
|
||||
Note that the "run" script (`my_script` in the above example) should
|
||||
exit with code 0 in case the current source code is good and with a
|
||||
code between 1 and 127 (included) in case the current source code is
|
||||
bad.
|
||||
exit with code 0 in case the current source code is good. Exit with a
|
||||
code between 1 and 127 (inclusive), except 125, if the current
|
||||
source code is bad.
|
||||
|
||||
Any other exit code will abort the automatic bisect process. (A
|
||||
program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
|
||||
the value is chopped with "& 0377".)
|
||||
|
||||
The special exit code 125 should be used when the current source code
|
||||
cannot be tested. If the "run" script exits with this code, the current
|
||||
revision will be skipped, see `git bisect skip` above.
|
||||
|
||||
You may often find that during bisect you want to have near-constant
|
||||
tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
|
||||
"revision that does not have this commit needs this patch applied to
|
||||
|
||||
@@ -27,11 +27,12 @@ OPTIONS
|
||||
message prior committing.
|
||||
|
||||
-x::
|
||||
Cause the command to append which commit was
|
||||
cherry-picked after the original commit message when
|
||||
making a commit. Do not use this option if you are
|
||||
cherry-picking from your private branch because the
|
||||
information is useless to the recipient. If on the
|
||||
When recording the commit, append to the original commit
|
||||
message a note that indicates which commit this change
|
||||
was cherry-picked from. Append the note only for cherry
|
||||
picks without conflicts. Do not use this option if
|
||||
you are cherry-picking from your private branch because
|
||||
the information is useless to the recipient. If on the
|
||||
other hand you are cherry-picking between two publicly
|
||||
visible branches (e.g. backporting a fix to a
|
||||
maintenance branch for an older release from a
|
||||
|
||||
@@ -111,11 +111,11 @@ OPTIONS
|
||||
|
||||
--depth <depth>::
|
||||
Create a 'shallow' clone with a history truncated to the
|
||||
specified number of revs. A shallow repository has
|
||||
specified number of revisions. A shallow repository has a
|
||||
number of limitations (you cannot clone or fetch from
|
||||
it, nor push from nor into it), but is adequate if you
|
||||
want to only look at near the tip of a large project
|
||||
with a long history, and would want to send in a fixes
|
||||
are only interested in the recent history of a large project
|
||||
with a long history, and would want to send in fixes
|
||||
as patches.
|
||||
|
||||
<repository>::
|
||||
|
||||
@@ -73,7 +73,7 @@ Merge one patch into CVS::
|
||||
$ export GIT_DIR=~/project/.git
|
||||
$ cd ~/project_cvs_checkout
|
||||
$ git-cvsexportcommit -v <commit-sha1>
|
||||
$ cvs commit -F .mgs <files>
|
||||
$ cvs commit -F .msg <files>
|
||||
------------
|
||||
|
||||
Merge pending patches into CVS automatically -- only if you really know what you are doing::
|
||||
|
||||
@@ -219,7 +219,7 @@ git filter-branch --commit-filter '
|
||||
fi' HEAD
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The function 'skip_commits' is defined as follows:
|
||||
The function 'skip_commit' is defined as follows:
|
||||
|
||||
--------------------------
|
||||
skip_commit()
|
||||
|
||||
@@ -168,7 +168,7 @@ git-format-patch origin::
|
||||
is created in the current directory.
|
||||
|
||||
git-format-patch \--root origin::
|
||||
Extract all commits which that leads to 'origin' since the
|
||||
Extract all commits that lead to 'origin' since the
|
||||
inception of the project.
|
||||
|
||||
git-format-patch -M -B origin::
|
||||
|
||||
@@ -11,26 +11,27 @@ SYNOPSIS
|
||||
[verse]
|
||||
'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]...
|
||||
[-m <msg>] <remote> <remote>...
|
||||
'git-merge' <msg> HEAD <remote>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This is the top-level interface to the merge machinery
|
||||
which drives multiple merge strategy scripts.
|
||||
|
||||
The second syntax (<msg> `HEAD` <remote>) is supported for
|
||||
historical reasons. Do not use it from the command line or in
|
||||
new scripts. It is the same as `git merge -m <msg> <remote>`.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
include::merge-options.txt[]
|
||||
|
||||
<msg>::
|
||||
-m <msg>::
|
||||
The commit message to be used for the merge commit (in case
|
||||
it is created). The `git-fmt-merge-msg` script can be used
|
||||
to give a good default for automated `git-merge` invocations.
|
||||
|
||||
<head>::
|
||||
Our branch head commit. This has to be `HEAD`, so new
|
||||
syntax does not require it
|
||||
|
||||
<remote>::
|
||||
Other branch head merged into our branch. You need at
|
||||
least one <remote>. Specifying more than one <remote>
|
||||
|
||||
@@ -25,12 +25,18 @@ OPTIONS
|
||||
-t or --tool=<tool>::
|
||||
Use the merge resolution program specified by <tool>.
|
||||
Valid merge tools are:
|
||||
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, and opendiff
|
||||
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
|
||||
+
|
||||
If a merge resolution program is not specified, 'git mergetool'
|
||||
will use the configuration variable merge.tool. If the
|
||||
configuration variable merge.tool is not set, 'git mergetool'
|
||||
will pick a suitable default.
|
||||
+
|
||||
You can explicitly provide a full path to the tool by setting the
|
||||
configuration variable mergetool.<tool>.path. For example, you
|
||||
can configure the absolute path to kdiff3 by setting
|
||||
mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool
|
||||
is available in PATH.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
@@ -34,6 +34,7 @@ SYNOPSIS
|
||||
[ \--pretty | \--header ]
|
||||
[ \--bisect ]
|
||||
[ \--bisect-vars ]
|
||||
[ \--bisect-all ]
|
||||
[ \--merge ]
|
||||
[ \--reverse ]
|
||||
[ \--walk-reflogs ]
|
||||
@@ -354,6 +355,21 @@ the expected number of commits to be tested if `bisect_rev`
|
||||
turns out to be bad to `bisect_bad`, and the number of commits
|
||||
we are bisecting right now to `bisect_all`.
|
||||
|
||||
--bisect-all::
|
||||
|
||||
This outputs all the commit objects between the included and excluded
|
||||
commits, ordered by their distance to the included and excluded
|
||||
commits. The farthest from them is displayed first. (This is the only
|
||||
one displayed by `--bisect`.)
|
||||
|
||||
This is useful because it makes it easy to choose a good commit to
|
||||
test when you want to avoid to test some of them for some reason (they
|
||||
may not compile for example).
|
||||
|
||||
This option can be used along with `--bisect-vars`, in this case,
|
||||
after all the sorted commit objects, there will be the same text as if
|
||||
`--bisect-vars` had been used alone.
|
||||
|
||||
--
|
||||
|
||||
Commit Ordering
|
||||
|
||||
@@ -159,6 +159,9 @@ sendemail.aliasfiletype::
|
||||
Format of the file(s) specified in sendemail.aliasesfile. Must be
|
||||
one of 'mutt', 'mailrc', 'pine', or 'gnus'.
|
||||
|
||||
sendemail.to::
|
||||
Email address (or alias) to always send to.
|
||||
|
||||
sendemail.cccmd::
|
||||
Command to execute to generate per patch file specific "Cc:"s.
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ Alternative/Augmentative Porcelains
|
||||
providing generally smoother user experience than the "raw" Core GIT
|
||||
itself and indeed many other version control systems.
|
||||
|
||||
Cogito is no longer maintained as most of its functionality
|
||||
is now in core GIT.
|
||||
|
||||
|
||||
- *pg* (http://www.spearce.org/category/projects/scm/pg/)
|
||||
|
||||
@@ -33,7 +36,7 @@ Alternative/Augmentative Porcelains
|
||||
- *StGit* (http://www.procode.org/stgit/)
|
||||
|
||||
Stacked GIT provides a quilt-like patch management functionality in the
|
||||
GIT environment. You can easily manage your patches in the scope of GIT
|
||||
GIT environment. You can easily manage your patches in the scope of GIT
|
||||
until they get merged upstream.
|
||||
|
||||
|
||||
|
||||
@@ -46,10 +46,12 @@ 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.5.txt[1.5.3.5],
|
||||
link:RelNotes-1.5.3.4.txt[1.5.3.4],
|
||||
link:RelNotes-1.5.3.3.txt[1.5.3.3],
|
||||
link:RelNotes-1.5.3.2.txt[1.5.3.2],
|
||||
link:RelNotes-1.5.3.1.txt[1.5.3.1].
|
||||
link:RelNotes-1.5.3.1.txt[1.5.3.1],
|
||||
link:RelNotes-1.5.3.txt[1.5.3].
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.2.5.txt[1.5.2.5],
|
||||
|
||||
14
Makefile
14
Makefile
@@ -225,8 +225,7 @@ SCRIPT_SH = \
|
||||
SCRIPT_PERL = \
|
||||
git-add--interactive.perl \
|
||||
git-archimport.perl git-cvsimport.perl git-relink.perl \
|
||||
git-cvsserver.perl git-remote.perl \
|
||||
git-svnimport.perl git-cvsexportcommit.perl \
|
||||
git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
|
||||
git-send-email.perl git-svn.perl
|
||||
|
||||
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
@@ -290,7 +289,7 @@ LIB_H = \
|
||||
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
|
||||
spawn-pipe.h \
|
||||
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
|
||||
mailmap.h remote.h transport.h
|
||||
mailmap.h remote.h transport.h diffcore.h hash.h
|
||||
|
||||
DIFF_OBJS = \
|
||||
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
|
||||
@@ -300,7 +299,7 @@ DIFF_OBJS = \
|
||||
LIB_OBJS = \
|
||||
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
|
||||
date.o diff-delta.o entry.o exec_cmd.o ident.o \
|
||||
interpolate.o \
|
||||
interpolate.o hash.o \
|
||||
lockfile.o \
|
||||
spawn-pipe.o \
|
||||
patch-ids.o \
|
||||
@@ -841,7 +840,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
|
||||
|
||||
perl/perl.mak: GIT-CFLAGS
|
||||
perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
|
||||
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
|
||||
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
|
||||
@@ -949,7 +948,6 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
|
||||
|
||||
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
|
||||
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
|
||||
$(DIFF_OBJS): diffcore.h
|
||||
|
||||
$(LIB_FILE): $(LIB_OBJS)
|
||||
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
|
||||
@@ -963,10 +961,6 @@ $(XDIFF_LIB): $(XDIFF_OBJS)
|
||||
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
|
||||
|
||||
|
||||
perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
|
||||
(cd perl && $(PERL_PATH) Makefile.PL \
|
||||
PREFIX='$(prefix_SQ)')
|
||||
|
||||
doc:
|
||||
$(MAKE) -C Documentation all
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "diffcore.h"
|
||||
#include "commit.h"
|
||||
#include "revision.h"
|
||||
#include "run-command.h"
|
||||
|
||||
static const char builtin_add_usage[] =
|
||||
"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <filepattern>...";
|
||||
@@ -44,6 +45,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
|
||||
die("pathspec '%s' did not match any files",
|
||||
pathspec[i]);
|
||||
}
|
||||
free(seen);
|
||||
}
|
||||
|
||||
static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
||||
@@ -106,7 +108,7 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
}
|
||||
}
|
||||
|
||||
static void update(int verbose, const char *prefix, const char **files)
|
||||
void add_files_to_cache(int verbose, const char *prefix, const char **files)
|
||||
{
|
||||
struct rev_info rev;
|
||||
init_revisions(&rev, prefix);
|
||||
@@ -115,8 +117,6 @@ static void update(int verbose, const char *prefix, const char **files)
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
rev.diffopt.format_callback_data = &verbose;
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
run_diff_files(&rev, 0);
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ static void refresh(int verbose, const char **pathspec)
|
||||
if (!seen[i])
|
||||
die("pathspec '%s' did not match any files", pathspec[i]);
|
||||
}
|
||||
free(seen);
|
||||
}
|
||||
|
||||
static int git_add_config(const char *var, const char *value)
|
||||
@@ -149,6 +150,13 @@ static int git_add_config(const char *var, const char *value)
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int interactive_add(void)
|
||||
{
|
||||
const char *argv[2] = { "add--interactive", NULL };
|
||||
|
||||
return run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static struct lock_file lock_file;
|
||||
|
||||
static const char ignore_error[] =
|
||||
@@ -168,12 +176,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
add_interactive++;
|
||||
}
|
||||
if (add_interactive) {
|
||||
const char *args[] = { "add--interactive", NULL };
|
||||
|
||||
if (add_interactive != 1 || argc != 2)
|
||||
if (argc != 2)
|
||||
die("add --interactive does not take any parameters");
|
||||
execv_git_cmd(args);
|
||||
exit(1);
|
||||
exit(interactive_add());
|
||||
}
|
||||
|
||||
git_config(git_add_config);
|
||||
@@ -213,7 +218,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (take_worktree_changes) {
|
||||
update(verbose, prefix, argv + i);
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
add_files_to_cache(verbose, prefix, argv + i);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ struct patch {
|
||||
unsigned int is_rename:1;
|
||||
struct fragment *fragments;
|
||||
char *result;
|
||||
unsigned long resultsize;
|
||||
size_t resultsize;
|
||||
char old_sha1_prefix[41];
|
||||
char new_sha1_prefix[41];
|
||||
struct patch *next;
|
||||
|
||||
@@ -146,12 +146,14 @@ void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
|
||||
buffer = read_sha1_file(sha1, type, sizep);
|
||||
if (buffer && S_ISREG(mode)) {
|
||||
struct strbuf buf;
|
||||
size_t size = 0;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
|
||||
convert_to_working_tree(path, buf.buf, buf.len, &buf);
|
||||
convert_to_archive(path, buf.buf, buf.len, &buf, commit);
|
||||
buffer = strbuf_detach(&buf, sizep);
|
||||
buffer = strbuf_detach(&buf, &size);
|
||||
*sizep = size;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
static int transfer_unpack_limit = -1;
|
||||
static int fetch_unpack_limit = -1;
|
||||
static int unpack_limit = 100;
|
||||
static struct fetch_pack_args args;
|
||||
static struct fetch_pack_args args = {
|
||||
/* .uploadpack = */ "git-upload-pack",
|
||||
};
|
||||
|
||||
static const char fetch_pack_usage[] =
|
||||
"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
|
||||
static const char *uploadpack = "git-upload-pack";
|
||||
|
||||
#define COMPLETE (1U << 0)
|
||||
#define COMMON (1U << 1)
|
||||
@@ -748,7 +749,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
|
||||
st.st_mtime = 0;
|
||||
}
|
||||
|
||||
conn = git_connect(fd, (char *)dest, uploadpack,
|
||||
conn = git_connect(fd, (char *)dest, args.uploadpack,
|
||||
args.verbose ? CONNECT_VERBOSE : 0);
|
||||
if (heads && nr_heads)
|
||||
nr_heads = remove_duplicates(nr_heads, heads);
|
||||
|
||||
@@ -48,15 +48,21 @@ static void add_merge_config(struct ref **head,
|
||||
if (rm)
|
||||
continue;
|
||||
|
||||
/* Not fetched to a tracking branch? We need to fetch
|
||||
/*
|
||||
* Not fetched to a tracking branch? We need to fetch
|
||||
* it anyway to allow this branch's "branch.$name.merge"
|
||||
* to be honored by git-pull.
|
||||
* to be honored by git-pull, but we do not have to
|
||||
* fail if branch.$name.merge is misconfigured to point
|
||||
* at a nonexisting branch. If we were indeed called by
|
||||
* git-pull, it will notice the misconfiguration because
|
||||
* there is no entry in the resulting FETCH_HEAD marked
|
||||
* for merging.
|
||||
*/
|
||||
refspec.src = branch->merge[i]->src;
|
||||
refspec.dst = NULL;
|
||||
refspec.pattern = 0;
|
||||
refspec.force = 0;
|
||||
get_fetch_map(remote_refs, &refspec, tail);
|
||||
get_fetch_map(remote_refs, &refspec, tail, 1);
|
||||
for (rm = *old_tail; rm; rm = rm->next)
|
||||
rm->merge = 1;
|
||||
}
|
||||
@@ -75,7 +81,7 @@ static struct ref *get_ref_map(struct transport *transport,
|
||||
|
||||
if (ref_count || tags) {
|
||||
for (i = 0; i < ref_count; i++) {
|
||||
get_fetch_map(remote_refs, &refs[i], &tail);
|
||||
get_fetch_map(remote_refs, &refs[i], &tail, 0);
|
||||
if (refs[i].dst && refs[i].dst[0])
|
||||
*autotags = 1;
|
||||
}
|
||||
@@ -88,7 +94,7 @@ static struct ref *get_ref_map(struct transport *transport,
|
||||
refspec.dst = "refs/tags/";
|
||||
refspec.pattern = 1;
|
||||
refspec.force = 0;
|
||||
get_fetch_map(remote_refs, &refspec, &tail);
|
||||
get_fetch_map(remote_refs, &refspec, &tail, 0);
|
||||
}
|
||||
} else {
|
||||
/* Use the defaults */
|
||||
@@ -97,7 +103,7 @@ static struct ref *get_ref_map(struct transport *transport,
|
||||
int has_merge = branch_has_merge_config(branch);
|
||||
if (remote && (remote->fetch_refspec_nr || has_merge)) {
|
||||
for (i = 0; i < remote->fetch_refspec_nr; i++) {
|
||||
get_fetch_map(remote_refs, &remote->fetch[i], &tail);
|
||||
get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
|
||||
if (remote->fetch[i].dst &&
|
||||
remote->fetch[i].dst[0])
|
||||
*autotags = 1;
|
||||
@@ -110,11 +116,13 @@ static struct ref *get_ref_map(struct transport *transport,
|
||||
* as given in branch.<name>.remote, we add the
|
||||
* ref given in branch.<name>.merge, too.
|
||||
*/
|
||||
if (has_merge && !strcmp(branch->remote_name,
|
||||
remote->name))
|
||||
if (has_merge &&
|
||||
!strcmp(branch->remote_name, remote->name))
|
||||
add_merge_config(&ref_map, remote_refs, branch, &tail);
|
||||
} else {
|
||||
ref_map = get_remote_ref(remote_refs, "HEAD");
|
||||
if (!ref_map)
|
||||
die("Couldn't find remote ref HEAD");
|
||||
ref_map->merge = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,18 +389,39 @@ static int is_rerere_enabled(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmd_rerere(int argc, const char **argv, const char *prefix)
|
||||
static int setup_rerere(struct path_list *merge_rr)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int i, fd = -1;
|
||||
int fd;
|
||||
|
||||
git_config(git_rerere_config);
|
||||
if (!is_rerere_enabled())
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
|
||||
fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
|
||||
read_rr(&merge_rr);
|
||||
read_rr(merge_rr);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int rerere(void)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int fd;
|
||||
|
||||
fd = setup_rerere(&merge_rr);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
return do_plain_rerere(&merge_rr, fd);
|
||||
}
|
||||
|
||||
int cmd_rerere(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int i, fd;
|
||||
|
||||
fd = setup_rerere(&merge_rr);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
if (argc < 2)
|
||||
return do_plain_rerere(&merge_rr, fd);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "revision.h"
|
||||
#include "list-objects.h"
|
||||
#include "builtin.h"
|
||||
#include "log-tree.h"
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
@@ -38,7 +39,8 @@ static const char rev_list_usage[] =
|
||||
" --left-right\n"
|
||||
" special purpose:\n"
|
||||
" --bisect\n"
|
||||
" --bisect-vars"
|
||||
" --bisect-vars\n"
|
||||
" --bisect-all"
|
||||
;
|
||||
|
||||
static struct rev_info revs;
|
||||
@@ -74,6 +76,7 @@ static void show_commit(struct commit *commit)
|
||||
parents = parents->next;
|
||||
}
|
||||
}
|
||||
show_decorations(commit);
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
putchar(' ');
|
||||
else
|
||||
@@ -278,6 +281,57 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
return best;
|
||||
}
|
||||
|
||||
struct commit_dist {
|
||||
struct commit *commit;
|
||||
int distance;
|
||||
};
|
||||
|
||||
static int compare_commit_dist(const void *a_, const void *b_)
|
||||
{
|
||||
struct commit_dist *a, *b;
|
||||
|
||||
a = (struct commit_dist *)a_;
|
||||
b = (struct commit_dist *)b_;
|
||||
if (a->distance != b->distance)
|
||||
return b->distance - a->distance; /* desc sort */
|
||||
return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
|
||||
}
|
||||
|
||||
static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_dist *array = xcalloc(nr, sizeof(*array));
|
||||
int cnt, i;
|
||||
|
||||
for (p = list, cnt = 0; p; p = p->next) {
|
||||
int distance;
|
||||
unsigned flags = p->item->object.flags;
|
||||
|
||||
if (revs.prune_fn && !(flags & TREECHANGE))
|
||||
continue;
|
||||
distance = weight(p);
|
||||
if (nr - distance < distance)
|
||||
distance = nr - distance;
|
||||
array[cnt].commit = p->item;
|
||||
array[cnt].distance = distance;
|
||||
cnt++;
|
||||
}
|
||||
qsort(array, cnt, sizeof(*array), compare_commit_dist);
|
||||
for (p = list, i = 0; i < cnt; i++) {
|
||||
struct name_decoration *r = xmalloc(sizeof(*r) + 100);
|
||||
struct object *obj = &(array[i].commit->object);
|
||||
|
||||
sprintf(r->name, "dist=%d", array[i].distance);
|
||||
r->next = add_decoration(&name_decoration, obj, r);
|
||||
p->item = array[i].commit;
|
||||
p = p->next;
|
||||
}
|
||||
if (p)
|
||||
p->next = NULL;
|
||||
free(array);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* zero or positive weight is the number of interesting commits it can
|
||||
* reach, including itself. Especially, weight = 0 means it does not
|
||||
@@ -292,7 +346,8 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
|
||||
* or positive distance.
|
||||
*/
|
||||
static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
int nr, int *weights)
|
||||
int nr, int *weights,
|
||||
int find_all)
|
||||
{
|
||||
int n, counted;
|
||||
struct commit_list *p;
|
||||
@@ -351,7 +406,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
clear_distance(list);
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (halfway(p, nr))
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
counted++;
|
||||
}
|
||||
@@ -389,19 +444,22 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
|
||||
weight_set(p, weight(q));
|
||||
|
||||
/* Does it happen to be at exactly half-way? */
|
||||
if (halfway(p, nr))
|
||||
if (!find_all && halfway(p, nr))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
show_list("bisection 2 counted all", counted, nr, list);
|
||||
|
||||
/* Then find the best one */
|
||||
return best_bisection(list, nr);
|
||||
if (!find_all)
|
||||
return best_bisection(list, nr);
|
||||
else
|
||||
return best_bisection_sorted(list, nr);
|
||||
}
|
||||
|
||||
static struct commit_list *find_bisection(struct commit_list *list,
|
||||
int *reaches, int *all)
|
||||
int *reaches, int *all,
|
||||
int find_all)
|
||||
{
|
||||
int nr, on_list;
|
||||
struct commit_list *p, *best, *next, *last;
|
||||
@@ -434,14 +492,13 @@ static struct commit_list *find_bisection(struct commit_list *list,
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
|
||||
/* Do the real work of finding bisection commit. */
|
||||
best = do_find_bisection(list, nr, weights);
|
||||
|
||||
best = do_find_bisection(list, nr, weights, find_all);
|
||||
if (best) {
|
||||
best->next = NULL;
|
||||
if (!find_all)
|
||||
best->next = NULL;
|
||||
*reaches = weight(best);
|
||||
}
|
||||
free(weights);
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
@@ -468,6 +525,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
int i;
|
||||
int read_from_stdin = 0;
|
||||
int bisect_show_vars = 0;
|
||||
int bisect_find_all = 0;
|
||||
|
||||
git_config(git_default_config);
|
||||
init_revisions(&revs, prefix);
|
||||
@@ -490,6 +548,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
bisect_list = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect-all")) {
|
||||
bisect_list = 1;
|
||||
bisect_find_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--bisect-vars")) {
|
||||
bisect_list = 1;
|
||||
bisect_show_vars = 1;
|
||||
@@ -536,9 +599,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
if (bisect_list) {
|
||||
int reaches = reaches, all = all;
|
||||
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all);
|
||||
revs.commits = find_bisection(revs.commits, &reaches, &all,
|
||||
bisect_find_all);
|
||||
if (bisect_show_vars) {
|
||||
int cnt;
|
||||
char hex[41];
|
||||
if (!revs.commits)
|
||||
return 1;
|
||||
/*
|
||||
@@ -550,15 +615,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
* A bisect set of size N has (N-1) commits further
|
||||
* to test, as we already know one bad one.
|
||||
*/
|
||||
cnt = all-reaches;
|
||||
cnt = all - reaches;
|
||||
if (cnt < reaches)
|
||||
cnt = reaches;
|
||||
strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
|
||||
|
||||
if (bisect_find_all) {
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
printf("bisect_rev=%s\n"
|
||||
"bisect_nr=%d\n"
|
||||
"bisect_good=%d\n"
|
||||
"bisect_bad=%d\n"
|
||||
"bisect_all=%d\n",
|
||||
sha1_to_hex(revs.commits->item->object.sha1),
|
||||
hex,
|
||||
cnt - 1,
|
||||
all - reaches - 1,
|
||||
reaches - 1,
|
||||
|
||||
@@ -349,7 +349,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
die ("Error wrapping up %s", defmsg);
|
||||
fprintf(stderr, "Automatic %s failed. "
|
||||
"After resolving the conflicts,\n"
|
||||
"mark the corrected paths with 'git-add <paths>'\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 "
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
extern const char git_version_string[];
|
||||
extern const char git_usage_string[];
|
||||
|
||||
extern void list_common_cmds_help(void);
|
||||
extern void help_unknown_cmd(const char *cmd);
|
||||
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
|
||||
extern void prune_packed_objects(int);
|
||||
|
||||
18
color.c
18
color.c
@@ -135,39 +135,39 @@ int git_config_colorbool(const char *var, const char *value)
|
||||
return git_config_bool(var, value);
|
||||
}
|
||||
|
||||
static int color_vprintf(const char *color, const char *fmt,
|
||||
static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
|
||||
va_list args, const char *trail)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (*color)
|
||||
r += printf("%s", color);
|
||||
r += vprintf(fmt, args);
|
||||
r += fprintf(fp, "%s", color);
|
||||
r += vfprintf(fp, fmt, args);
|
||||
if (*color)
|
||||
r += printf("%s", COLOR_RESET);
|
||||
r += fprintf(fp, "%s", COLOR_RESET);
|
||||
if (trail)
|
||||
r += printf("%s", trail);
|
||||
r += fprintf(fp, "%s", trail);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int color_printf(const char *color, const char *fmt, ...)
|
||||
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int r;
|
||||
va_start(args, fmt);
|
||||
r = color_vprintf(color, fmt, args, NULL);
|
||||
r = color_vfprintf(fp, color, fmt, args, NULL);
|
||||
va_end(args);
|
||||
return r;
|
||||
}
|
||||
|
||||
int color_printf_ln(const char *color, const char *fmt, ...)
|
||||
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int r;
|
||||
va_start(args, fmt);
|
||||
r = color_vprintf(color, fmt, args, "\n");
|
||||
r = color_vfprintf(fp, color, fmt, args, "\n");
|
||||
va_end(args);
|
||||
return r;
|
||||
}
|
||||
|
||||
4
color.h
4
color.h
@@ -6,7 +6,7 @@
|
||||
|
||||
int git_config_colorbool(const char *var, const char *value);
|
||||
void color_parse(const char *var, const char *value, char *dst);
|
||||
int color_printf(const char *color, const char *fmt, ...);
|
||||
int color_printf_ln(const char *color, const char *fmt, ...);
|
||||
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
|
||||
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
|
||||
|
||||
#endif /* COLOR_H */
|
||||
|
||||
5
commit.h
5
commit.h
@@ -128,4 +128,9 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
|
||||
int depth, int shallow_flag, int not_shallow_flag);
|
||||
|
||||
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 rerere(void);
|
||||
|
||||
#endif /* COMMIT_H */
|
||||
|
||||
@@ -346,7 +346,6 @@ __git_commands ()
|
||||
ssh-*) : transport;;
|
||||
stripspace) : plumbing;;
|
||||
svn) : import export;;
|
||||
svnimport) : import;;
|
||||
symbolic-ref) : plumbing;;
|
||||
tar-tree) : deprecated;;
|
||||
unpack-file) : plumbing;;
|
||||
|
||||
@@ -796,6 +796,7 @@ Return the list of files that haven't been handled."
|
||||
(with-current-buffer buffer (erase-buffer))
|
||||
(dolist (info files) (git-set-fileinfo-state info 'uptodate))
|
||||
(git-call-process-env nil nil "rerere")
|
||||
(git-call-process-env nil nil "gc" "--auto")
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf git-status)
|
||||
(message "Committed %s." commit)
|
||||
@@ -842,7 +843,8 @@ Return the list of files that haven't been handled."
|
||||
"Mark all files."
|
||||
(interactive)
|
||||
(unless git-status (error "Not in git-status buffer."))
|
||||
(ewoc-map (lambda (info) (setf (git-fileinfo->marked info) t) t) git-status)
|
||||
(ewoc-map (lambda (info) (unless (git-fileinfo->marked info)
|
||||
(setf (git-fileinfo->marked info) t))) git-status)
|
||||
; move back to goal column after invalidate
|
||||
(when goal-column (move-to-column goal-column)))
|
||||
|
||||
@@ -850,7 +852,9 @@ Return the list of files that haven't been handled."
|
||||
"Unmark all files."
|
||||
(interactive)
|
||||
(unless git-status (error "Not in git-status buffer."))
|
||||
(ewoc-map (lambda (info) (setf (git-fileinfo->marked info) nil) t) git-status)
|
||||
(ewoc-map (lambda (info) (when (git-fileinfo->marked info)
|
||||
(setf (git-fileinfo->marked info) nil)
|
||||
t)) git-status)
|
||||
; move back to goal column after invalidate
|
||||
(when goal-column (move-to-column goal-column)))
|
||||
|
||||
@@ -955,7 +959,7 @@ Return the list of files that haven't been handled."
|
||||
(when modified
|
||||
(apply #'git-call-process-env nil nil "checkout" "HEAD" modified))
|
||||
(git-update-status-files (append added modified) 'uptodate)
|
||||
(git-success-message "Reverted" files))))
|
||||
(git-success-message "Reverted" (git-get-filenames files)))))
|
||||
|
||||
(defun git-resolve-file ()
|
||||
"Resolve conflicts in marked file(s)."
|
||||
@@ -1353,7 +1357,7 @@ Commands:
|
||||
"Update the corresponding git-status buffer when a file is saved.
|
||||
Meant to be used in `after-save-hook'."
|
||||
(let* ((file (expand-file-name buffer-file-name))
|
||||
(dir (condition-case nil (git-get-top-dir (file-name-directory file))))
|
||||
(dir (condition-case nil (git-get-top-dir (file-name-directory file)) (error nil)))
|
||||
(buffer (and dir (git-find-status-buffer dir))))
|
||||
(when buffer
|
||||
(with-current-buffer buffer
|
||||
|
||||
63
diff.c
63
diff.c
@@ -1441,9 +1441,18 @@ struct diff_filespec *alloc_filespec(const char *path)
|
||||
memset(spec, 0, sizeof(*spec));
|
||||
spec->path = (char *)(spec + 1);
|
||||
memcpy(spec->path, path, namelen+1);
|
||||
spec->count = 1;
|
||||
return spec;
|
||||
}
|
||||
|
||||
void free_filespec(struct diff_filespec *spec)
|
||||
{
|
||||
if (!--spec->count) {
|
||||
diff_free_filespec_data(spec);
|
||||
free(spec);
|
||||
}
|
||||
}
|
||||
|
||||
void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
|
||||
unsigned short mode)
|
||||
{
|
||||
@@ -1513,6 +1522,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
|
||||
static int populate_from_stdin(struct diff_filespec *s)
|
||||
{
|
||||
struct strbuf buf;
|
||||
size_t size = 0;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
if (strbuf_read(&buf, 0, 0) < 0)
|
||||
@@ -1520,7 +1530,8 @@ static int populate_from_stdin(struct diff_filespec *s)
|
||||
strerror(errno));
|
||||
|
||||
s->should_munmap = 0;
|
||||
s->data = strbuf_detach(&buf, &s->size);
|
||||
s->data = strbuf_detach(&buf, &size);
|
||||
s->size = size;
|
||||
s->should_free = 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -1610,9 +1621,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
|
||||
*/
|
||||
strbuf_init(&buf, 0);
|
||||
if (convert_to_git(s->path, s->data, s->size, &buf)) {
|
||||
size_t size = 0;
|
||||
munmap(s->data, s->size);
|
||||
s->should_munmap = 0;
|
||||
s->data = strbuf_detach(&buf, &s->size);
|
||||
s->data = strbuf_detach(&buf, &size);
|
||||
s->size = size;
|
||||
s->should_free = 1;
|
||||
}
|
||||
}
|
||||
@@ -2399,10 +2412,8 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
|
||||
|
||||
void diff_free_filepair(struct diff_filepair *p)
|
||||
{
|
||||
diff_free_filespec_data(p->one);
|
||||
diff_free_filespec_data(p->two);
|
||||
free(p->one);
|
||||
free(p->two);
|
||||
free_filespec(p->one);
|
||||
free_filespec(p->two);
|
||||
free(p);
|
||||
}
|
||||
|
||||
@@ -2554,9 +2565,9 @@ void diff_debug_filepair(const struct diff_filepair *p, int i)
|
||||
{
|
||||
diff_debug_filespec(p->one, i, "one");
|
||||
diff_debug_filespec(p->two, i, "two");
|
||||
fprintf(stderr, "score %d, status %c stays %d broken %d\n",
|
||||
fprintf(stderr, "score %d, status %c rename_used %d broken %d\n",
|
||||
p->score, p->status ? p->status : '?',
|
||||
p->source_stays, p->broken_pair);
|
||||
p->one->rename_used, p->broken_pair);
|
||||
}
|
||||
|
||||
void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
|
||||
@@ -2574,8 +2585,8 @@ void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
|
||||
|
||||
static void diff_resolve_rename_copy(void)
|
||||
{
|
||||
int i, j;
|
||||
struct diff_filepair *p, *pp;
|
||||
int i;
|
||||
struct diff_filepair *p;
|
||||
struct diff_queue_struct *q = &diff_queued_diff;
|
||||
|
||||
diff_debug_queue("resolve-rename-copy", q);
|
||||
@@ -2597,27 +2608,21 @@ static void diff_resolve_rename_copy(void)
|
||||
* either in-place edit or rename/copy edit.
|
||||
*/
|
||||
else if (DIFF_PAIR_RENAME(p)) {
|
||||
if (p->source_stays) {
|
||||
p->status = DIFF_STATUS_COPIED;
|
||||
continue;
|
||||
}
|
||||
/* See if there is some other filepair that
|
||||
* copies from the same source as us. If so
|
||||
* we are a copy. Otherwise we are either a
|
||||
* copy if the path stays, or a rename if it
|
||||
* does not, but we already handled "stays" case.
|
||||
/*
|
||||
* A rename might have re-connected a broken
|
||||
* pair up, causing the pathnames to be the
|
||||
* same again. If so, that's not a rename at
|
||||
* all, just a modification..
|
||||
*
|
||||
* Otherwise, see if this source was used for
|
||||
* multiple renames, in which case we decrement
|
||||
* the count, and call it a copy.
|
||||
*/
|
||||
for (j = i + 1; j < q->nr; j++) {
|
||||
pp = q->queue[j];
|
||||
if (strcmp(pp->one->path, p->one->path))
|
||||
continue; /* not us */
|
||||
if (!DIFF_PAIR_RENAME(pp))
|
||||
continue; /* not a rename/copy */
|
||||
/* pp is a rename/copy from the same source */
|
||||
if (!strcmp(p->one->path, p->two->path))
|
||||
p->status = DIFF_STATUS_MODIFIED;
|
||||
else if (--p->one->rename_used > 0)
|
||||
p->status = DIFF_STATUS_COPIED;
|
||||
break;
|
||||
}
|
||||
if (!p->status)
|
||||
else
|
||||
p->status = DIFF_STATUS_RENAMED;
|
||||
}
|
||||
else if (hashcmp(p->one->sha1, p->two->sha1) ||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "cache.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* Table of rename/copy destinations */
|
||||
|
||||
@@ -55,12 +56,10 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
|
||||
static struct diff_rename_src {
|
||||
struct diff_filespec *one;
|
||||
unsigned short score; /* to remember the break score */
|
||||
unsigned src_path_left : 1;
|
||||
} *rename_src;
|
||||
static int rename_src_nr, rename_src_alloc;
|
||||
|
||||
static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
|
||||
int src_path_left,
|
||||
unsigned short score)
|
||||
{
|
||||
int first, last;
|
||||
@@ -92,33 +91,9 @@ static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
|
||||
(rename_src_nr - first - 1) * sizeof(*rename_src));
|
||||
rename_src[first].one = one;
|
||||
rename_src[first].score = score;
|
||||
rename_src[first].src_path_left = src_path_left;
|
||||
return &(rename_src[first]);
|
||||
}
|
||||
|
||||
static int is_exact_match(struct diff_filespec *src,
|
||||
struct diff_filespec *dst,
|
||||
int contents_too)
|
||||
{
|
||||
if (src->sha1_valid && dst->sha1_valid &&
|
||||
!hashcmp(src->sha1, dst->sha1))
|
||||
return 1;
|
||||
if (!contents_too)
|
||||
return 0;
|
||||
if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
|
||||
return 0;
|
||||
if (src->size != dst->size)
|
||||
return 0;
|
||||
if (src->sha1_valid && dst->sha1_valid)
|
||||
return !hashcmp(src->sha1, dst->sha1);
|
||||
if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
|
||||
return 0;
|
||||
if (src->size == dst->size &&
|
||||
!memcmp(src->data, dst->data, src->size))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
|
||||
{
|
||||
int src_len = strlen(src->path), dst_len = strlen(dst->path);
|
||||
@@ -169,6 +144,20 @@ static int estimate_similarity(struct diff_filespec *src,
|
||||
if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Need to check that source and destination sizes are
|
||||
* filled in before comparing them.
|
||||
*
|
||||
* If we already have "cnt_data" filled in, we know it's
|
||||
* all good (avoid checking the size for zero, as that
|
||||
* is a possible size - we really should have a flag to
|
||||
* say whether the size is valid or not!)
|
||||
*/
|
||||
if (!src->cnt_data && diff_populate_filespec(src, 0))
|
||||
return 0;
|
||||
if (!dst->cnt_data && diff_populate_filespec(dst, 0))
|
||||
return 0;
|
||||
|
||||
max_size = ((src->size > dst->size) ? src->size : dst->size);
|
||||
base_size = ((src->size < dst->size) ? src->size : dst->size);
|
||||
delta_size = max_size - base_size;
|
||||
@@ -184,11 +173,6 @@ static int estimate_similarity(struct diff_filespec *src,
|
||||
if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
|
||||
return 0;
|
||||
|
||||
if ((!src->cnt_data && diff_populate_filespec(src, 0))
|
||||
|| (!dst->cnt_data && diff_populate_filespec(dst, 0)))
|
||||
return 0; /* error but caught downstream */
|
||||
|
||||
|
||||
delta_limit = (unsigned long)
|
||||
(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
|
||||
if (diffcore_count_changes(src, dst,
|
||||
@@ -209,27 +193,25 @@ static int estimate_similarity(struct diff_filespec *src,
|
||||
|
||||
static void record_rename_pair(int dst_index, int src_index, int score)
|
||||
{
|
||||
struct diff_filespec *one, *two, *src, *dst;
|
||||
struct diff_filespec *src, *dst;
|
||||
struct diff_filepair *dp;
|
||||
|
||||
if (rename_dst[dst_index].pair)
|
||||
die("internal error: dst already matched.");
|
||||
|
||||
src = rename_src[src_index].one;
|
||||
one = alloc_filespec(src->path);
|
||||
fill_filespec(one, src->sha1, src->mode);
|
||||
src->rename_used++;
|
||||
src->count++;
|
||||
|
||||
dst = rename_dst[dst_index].two;
|
||||
two = alloc_filespec(dst->path);
|
||||
fill_filespec(two, dst->sha1, dst->mode);
|
||||
dst->count++;
|
||||
|
||||
dp = diff_queue(NULL, one, two);
|
||||
dp = diff_queue(NULL, src, dst);
|
||||
dp->renamed_pair = 1;
|
||||
if (!strcmp(src->path, dst->path))
|
||||
dp->score = rename_src[src_index].score;
|
||||
else
|
||||
dp->score = score;
|
||||
dp->source_stays = rename_src[src_index].src_path_left;
|
||||
rename_dst[dst_index].pair = dp;
|
||||
}
|
||||
|
||||
@@ -247,19 +229,155 @@ static int score_compare(const void *a_, const void *b_)
|
||||
return b->score - a->score;
|
||||
}
|
||||
|
||||
static int compute_stays(struct diff_queue_struct *q,
|
||||
struct diff_filespec *one)
|
||||
struct file_similarity {
|
||||
int src_dst, index;
|
||||
struct diff_filespec *filespec;
|
||||
struct file_similarity *next;
|
||||
};
|
||||
|
||||
static int find_identical_files(struct file_similarity *src,
|
||||
struct file_similarity *dst)
|
||||
{
|
||||
int renames = 0;
|
||||
|
||||
/*
|
||||
* Walk over all the destinations ...
|
||||
*/
|
||||
do {
|
||||
struct diff_filespec *one = dst->filespec;
|
||||
struct file_similarity *p, *best;
|
||||
int i = 100;
|
||||
|
||||
/*
|
||||
* .. to find the best source match
|
||||
*/
|
||||
best = NULL;
|
||||
for (p = src; p; p = p->next) {
|
||||
struct diff_filespec *two = p->filespec;
|
||||
|
||||
/* False hash collission? */
|
||||
if (hashcmp(one->sha1, two->sha1))
|
||||
continue;
|
||||
/* Non-regular files? If so, the modes must match! */
|
||||
if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) {
|
||||
if (one->mode != two->mode)
|
||||
continue;
|
||||
}
|
||||
best = p;
|
||||
if (basename_same(one, two))
|
||||
break;
|
||||
|
||||
/* Too many identical alternatives? Pick one */
|
||||
if (!--i)
|
||||
break;
|
||||
}
|
||||
if (best) {
|
||||
record_rename_pair(dst->index, best->index, MAX_SCORE);
|
||||
renames++;
|
||||
}
|
||||
} while ((dst = dst->next) != NULL);
|
||||
return renames;
|
||||
}
|
||||
|
||||
static void free_similarity_list(struct file_similarity *p)
|
||||
{
|
||||
while (p) {
|
||||
struct file_similarity *entry = p;
|
||||
p = p->next;
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static int find_same_files(void *ptr)
|
||||
{
|
||||
int ret;
|
||||
struct file_similarity *p = ptr;
|
||||
struct file_similarity *src = NULL, *dst = NULL;
|
||||
|
||||
/* Split the hash list up into sources and destinations */
|
||||
do {
|
||||
struct file_similarity *entry = p;
|
||||
p = p->next;
|
||||
if (entry->src_dst < 0) {
|
||||
entry->next = src;
|
||||
src = entry;
|
||||
} else {
|
||||
entry->next = dst;
|
||||
dst = entry;
|
||||
}
|
||||
} while (p);
|
||||
|
||||
/*
|
||||
* If we have both sources *and* destinations, see if
|
||||
* we can match them up
|
||||
*/
|
||||
ret = (src && dst) ? find_identical_files(src, dst) : 0;
|
||||
|
||||
/* Free the hashes and return the number of renames found */
|
||||
free_similarity_list(src);
|
||||
free_similarity_list(dst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int hash_filespec(struct diff_filespec *filespec)
|
||||
{
|
||||
unsigned int hash;
|
||||
if (!filespec->sha1_valid) {
|
||||
if (diff_populate_filespec(filespec, 0))
|
||||
return 0;
|
||||
hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
|
||||
}
|
||||
memcpy(&hash, filespec->sha1, sizeof(hash));
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void insert_file_table(struct hash_table *table, int src_dst, int index, struct diff_filespec *filespec)
|
||||
{
|
||||
void **pos;
|
||||
unsigned int hash;
|
||||
struct file_similarity *entry = xmalloc(sizeof(*entry));
|
||||
|
||||
entry->src_dst = src_dst;
|
||||
entry->index = index;
|
||||
entry->filespec = filespec;
|
||||
entry->next = NULL;
|
||||
|
||||
hash = hash_filespec(filespec);
|
||||
pos = insert_hash(hash, entry, table);
|
||||
|
||||
/* We already had an entry there? */
|
||||
if (pos) {
|
||||
entry->next = *pos;
|
||||
*pos = entry;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find exact renames first.
|
||||
*
|
||||
* The first round matches up the up-to-date entries,
|
||||
* and then during the second round we try to match
|
||||
* cache-dirty entries as well.
|
||||
*/
|
||||
static int find_exact_renames(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
if (strcmp(one->path, p->two->path))
|
||||
continue;
|
||||
if (DIFF_PAIR_RENAME(p)) {
|
||||
return 0; /* something else is renamed into this */
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
struct hash_table file_table;
|
||||
|
||||
init_hash(&file_table);
|
||||
for (i = 0; i < rename_src_nr; i++)
|
||||
insert_file_table(&file_table, -1, i, rename_src[i].one);
|
||||
|
||||
for (i = 0; i < rename_dst_nr; i++)
|
||||
insert_file_table(&file_table, 1, i, rename_dst[i].two);
|
||||
|
||||
/* Find the renames */
|
||||
i = for_each_hash(&file_table, find_same_files);
|
||||
|
||||
/* .. and free the hash data structure */
|
||||
free_hash(&file_table);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void diffcore_rename(struct diff_options *options)
|
||||
@@ -270,12 +388,11 @@ void diffcore_rename(struct diff_options *options)
|
||||
struct diff_queue_struct *q = &diff_queued_diff;
|
||||
struct diff_queue_struct outq;
|
||||
struct diff_score *mx;
|
||||
int i, j, rename_count, contents_too;
|
||||
int i, j, rename_count;
|
||||
int num_create, num_src, dst_cnt;
|
||||
|
||||
if (!minimum_score)
|
||||
minimum_score = DEFAULT_RENAME_SCORE;
|
||||
rename_count = 0;
|
||||
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
@@ -289,81 +406,66 @@ void diffcore_rename(struct diff_options *options)
|
||||
locate_rename_dst(p->two, 1);
|
||||
}
|
||||
else if (!DIFF_FILE_VALID(p->two)) {
|
||||
/* If the source is a broken "delete", and
|
||||
/*
|
||||
* If the source is a broken "delete", and
|
||||
* they did not really want to get broken,
|
||||
* that means the source actually stays.
|
||||
* So we increment the "rename_used" score
|
||||
* by one, to indicate ourselves as a user
|
||||
*/
|
||||
int stays = (p->broken_pair && !p->score);
|
||||
register_rename_src(p->one, stays, p->score);
|
||||
if (p->broken_pair && !p->score)
|
||||
p->one->rename_used++;
|
||||
register_rename_src(p->one, p->score);
|
||||
}
|
||||
else if (detect_rename == DIFF_DETECT_COPY) {
|
||||
/*
|
||||
* Increment the "rename_used" score by
|
||||
* one, to indicate ourselves as a user.
|
||||
*/
|
||||
p->one->rename_used++;
|
||||
register_rename_src(p->one, p->score);
|
||||
}
|
||||
else if (detect_rename == DIFF_DETECT_COPY)
|
||||
register_rename_src(p->one, 1, p->score);
|
||||
}
|
||||
if (rename_dst_nr == 0 || rename_src_nr == 0)
|
||||
goto cleanup; /* nothing to do */
|
||||
|
||||
/*
|
||||
* We really want to cull the candidates list early
|
||||
* with cheap tests in order to avoid doing deltas.
|
||||
*/
|
||||
rename_count = find_exact_renames();
|
||||
|
||||
/* Did we only want exact renames? */
|
||||
if (minimum_score == MAX_SCORE)
|
||||
goto cleanup;
|
||||
|
||||
/*
|
||||
* Calculate how many renames are left (but all the source
|
||||
* files still remain as options for rename/copies!)
|
||||
*/
|
||||
num_create = (rename_dst_nr - rename_count);
|
||||
num_src = rename_src_nr;
|
||||
|
||||
/* All done? */
|
||||
if (!num_create)
|
||||
goto cleanup;
|
||||
|
||||
/*
|
||||
* This basically does a test for the rename matrix not
|
||||
* growing larger than a "rename_limit" square matrix, ie:
|
||||
*
|
||||
* rename_dst_nr * rename_src_nr > rename_limit * rename_limit
|
||||
* num_create * num_src > rename_limit * rename_limit
|
||||
*
|
||||
* but handles the potential overflow case specially (and we
|
||||
* assume at least 32-bit integers)
|
||||
*/
|
||||
if (rename_limit <= 0 || rename_limit > 32767)
|
||||
rename_limit = 32767;
|
||||
if (rename_dst_nr > rename_limit && rename_src_nr > rename_limit)
|
||||
if (num_create > rename_limit && num_src > rename_limit)
|
||||
goto cleanup;
|
||||
if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit)
|
||||
if (num_create * num_src > rename_limit * rename_limit)
|
||||
goto cleanup;
|
||||
|
||||
/* We really want to cull the candidates list early
|
||||
* with cheap tests in order to avoid doing deltas.
|
||||
* The first round matches up the up-to-date entries,
|
||||
* and then during the second round we try to match
|
||||
* cache-dirty entries as well.
|
||||
*/
|
||||
for (contents_too = 0; contents_too < 2; contents_too++) {
|
||||
for (i = 0; i < rename_dst_nr; i++) {
|
||||
struct diff_filespec *two = rename_dst[i].two;
|
||||
if (rename_dst[i].pair)
|
||||
continue; /* dealt with an earlier round */
|
||||
for (j = 0; j < rename_src_nr; j++) {
|
||||
int k;
|
||||
struct diff_filespec *one = rename_src[j].one;
|
||||
if (!is_exact_match(one, two, contents_too))
|
||||
continue;
|
||||
|
||||
/* see if there is a basename match, too */
|
||||
for (k = j; k < rename_src_nr; k++) {
|
||||
one = rename_src[k].one;
|
||||
if (basename_same(one, two) &&
|
||||
is_exact_match(one, two,
|
||||
contents_too)) {
|
||||
j = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
record_rename_pair(i, j, (int)MAX_SCORE);
|
||||
rename_count++;
|
||||
break; /* we are done with this entry */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Have we run out the created file pool? If so we can avoid
|
||||
* doing the delta matrix altogether.
|
||||
*/
|
||||
if (rename_count == rename_dst_nr)
|
||||
goto cleanup;
|
||||
|
||||
if (minimum_score == MAX_SCORE)
|
||||
goto cleanup;
|
||||
|
||||
num_create = (rename_dst_nr - rename_count);
|
||||
num_src = rename_src_nr;
|
||||
mx = xmalloc(sizeof(*mx) * num_create * num_src);
|
||||
for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
|
||||
int base = dst_cnt * num_src;
|
||||
@@ -452,16 +554,7 @@ void diffcore_rename(struct diff_options *options)
|
||||
pair_to_free = p;
|
||||
}
|
||||
else {
|
||||
for (j = 0; j < rename_dst_nr; j++) {
|
||||
if (!rename_dst[j].pair)
|
||||
continue;
|
||||
if (strcmp(rename_dst[j].pair->
|
||||
one->path,
|
||||
p->one->path))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (j < rename_dst_nr)
|
||||
if (p->one->rename_used)
|
||||
/* this path remains */
|
||||
pair_to_free = p;
|
||||
}
|
||||
@@ -487,27 +580,8 @@ void diffcore_rename(struct diff_options *options)
|
||||
*q = outq;
|
||||
diff_debug_queue("done collapsing", q);
|
||||
|
||||
/* We need to see which rename source really stays here;
|
||||
* earlier we only checked if the path is left in the result,
|
||||
* but even if a path remains in the result, if that is coming
|
||||
* from copying something else on top of it, then the original
|
||||
* source is lost and does not stay.
|
||||
*/
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
if (DIFF_PAIR_RENAME(p) && p->source_stays) {
|
||||
/* If one appears as the target of a rename-copy,
|
||||
* then mark p->source_stays = 0; otherwise
|
||||
* leave it as is.
|
||||
*/
|
||||
p->source_stays = compute_stays(q, p->one);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < rename_dst_nr; i++) {
|
||||
diff_free_filespec_data(rename_dst[i].two);
|
||||
free(rename_dst[i].two);
|
||||
}
|
||||
for (i = 0; i < rename_dst_nr; i++)
|
||||
free_filespec(rename_dst[i].two);
|
||||
|
||||
free(rename_dst);
|
||||
rename_dst = NULL;
|
||||
|
||||
@@ -29,7 +29,9 @@ struct diff_filespec {
|
||||
void *cnt_data;
|
||||
const char *funcname_pattern_ident;
|
||||
unsigned long size;
|
||||
int count; /* Reference count */
|
||||
int xfrm_flags; /* for use by the xfrm */
|
||||
int rename_used; /* Count of rename users */
|
||||
unsigned short mode; /* file mode */
|
||||
unsigned sha1_valid : 1; /* if true, use sha1 and trust mode;
|
||||
* if false, use the name and read from
|
||||
@@ -43,6 +45,7 @@ struct diff_filespec {
|
||||
};
|
||||
|
||||
extern struct diff_filespec *alloc_filespec(const char *);
|
||||
extern void free_filespec(struct diff_filespec *);
|
||||
extern void fill_filespec(struct diff_filespec *, const unsigned char *,
|
||||
unsigned short);
|
||||
|
||||
@@ -56,7 +59,6 @@ struct diff_filepair {
|
||||
struct diff_filespec *two;
|
||||
unsigned short int score;
|
||||
char status; /* M C R N D U (see Documentation/diff-format.txt) */
|
||||
unsigned source_stays : 1; /* all of R/C are copies */
|
||||
unsigned broken_pair : 1;
|
||||
unsigned renamed_pair : 1;
|
||||
unsigned is_unmerged : 1;
|
||||
|
||||
59
dir.c
59
dir.c
@@ -118,14 +118,32 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int no_wildcard(const char *string)
|
||||
{
|
||||
return string[strcspn(string, "*?[{")] == '\0';
|
||||
}
|
||||
|
||||
void add_exclude(const char *string, const char *base,
|
||||
int baselen, struct exclude_list *which)
|
||||
{
|
||||
struct exclude *x = xmalloc(sizeof (*x));
|
||||
|
||||
x->to_exclude = 1;
|
||||
if (*string == '!') {
|
||||
x->to_exclude = 0;
|
||||
string++;
|
||||
}
|
||||
x->pattern = string;
|
||||
x->patternlen = strlen(string);
|
||||
x->base = base;
|
||||
x->baselen = baselen;
|
||||
x->flags = 0;
|
||||
if (!strchr(string, '/'))
|
||||
x->flags |= EXC_FLAG_NODIR;
|
||||
if (no_wildcard(string))
|
||||
x->flags |= EXC_FLAG_NOWILDCARD;
|
||||
if (*string == '*' && no_wildcard(string+1))
|
||||
x->flags |= EXC_FLAG_ENDSWITH;
|
||||
if (which->nr == which->alloc) {
|
||||
which->alloc = alloc_nr(which->alloc);
|
||||
which->excludes = xrealloc(which->excludes,
|
||||
@@ -209,7 +227,7 @@ void pop_exclude_per_directory(struct dir_struct *dir, int stk)
|
||||
* Return 1 for exclude, 0 for include and -1 for undecided.
|
||||
*/
|
||||
static int excluded_1(const char *pathname,
|
||||
int pathlen,
|
||||
int pathlen, const char *basename,
|
||||
struct exclude_list *el)
|
||||
{
|
||||
int i;
|
||||
@@ -218,19 +236,21 @@ static int excluded_1(const char *pathname,
|
||||
for (i = el->nr - 1; 0 <= i; i--) {
|
||||
struct exclude *x = el->excludes[i];
|
||||
const char *exclude = x->pattern;
|
||||
int to_exclude = 1;
|
||||
int to_exclude = x->to_exclude;
|
||||
|
||||
if (*exclude == '!') {
|
||||
to_exclude = 0;
|
||||
exclude++;
|
||||
}
|
||||
|
||||
if (!strchr(exclude, '/')) {
|
||||
if (x->flags & EXC_FLAG_NODIR) {
|
||||
/* match basename */
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = (basename) ? basename+1 : pathname;
|
||||
if (fnmatch(exclude, basename, 0) == 0)
|
||||
return to_exclude;
|
||||
if (x->flags & EXC_FLAG_NOWILDCARD) {
|
||||
if (!strcmp(exclude, basename))
|
||||
return to_exclude;
|
||||
} else if (x->flags & EXC_FLAG_ENDSWITH) {
|
||||
if (x->patternlen - 1 <= pathlen &&
|
||||
!strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
|
||||
return to_exclude;
|
||||
} else {
|
||||
if (fnmatch(exclude, basename, 0) == 0)
|
||||
return to_exclude;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* match with FNM_PATHNAME:
|
||||
@@ -246,9 +266,14 @@ static int excluded_1(const char *pathname,
|
||||
strncmp(pathname, x->base, baselen))
|
||||
continue;
|
||||
|
||||
if (fnmatch(exclude, pathname+baselen,
|
||||
FNM_PATHNAME) == 0)
|
||||
return to_exclude;
|
||||
if (x->flags & EXC_FLAG_NOWILDCARD) {
|
||||
if (!strcmp(exclude, pathname + baselen))
|
||||
return to_exclude;
|
||||
} else {
|
||||
if (fnmatch(exclude, pathname+baselen,
|
||||
FNM_PATHNAME) == 0)
|
||||
return to_exclude;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,9 +284,11 @@ int excluded(struct dir_struct *dir, const char *pathname)
|
||||
{
|
||||
int pathlen = strlen(pathname);
|
||||
int st;
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = (basename) ? basename+1 : pathname;
|
||||
|
||||
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
|
||||
switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) {
|
||||
switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
|
||||
7
dir.h
7
dir.h
@@ -17,13 +17,20 @@ struct dir_entry {
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
|
||||
#define EXC_FLAG_NODIR 1
|
||||
#define EXC_FLAG_NOWILDCARD 2
|
||||
#define EXC_FLAG_ENDSWITH 4
|
||||
|
||||
struct exclude_list {
|
||||
int nr;
|
||||
int alloc;
|
||||
struct exclude {
|
||||
const char *pattern;
|
||||
int patternlen;
|
||||
const char *base;
|
||||
int baselen;
|
||||
int to_exclude;
|
||||
int flags;
|
||||
} **excludes;
|
||||
};
|
||||
|
||||
|
||||
4
entry.c
4
entry.c
@@ -119,8 +119,10 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
|
||||
*/
|
||||
strbuf_init(&buf, 0);
|
||||
if (convert_to_working_tree(ce->name, new, size, &buf)) {
|
||||
size_t newsize = 0;
|
||||
free(new);
|
||||
new = strbuf_detach(&buf, &size);
|
||||
new = strbuf_detach(&buf, &newsize);
|
||||
size = newsize;
|
||||
}
|
||||
|
||||
if (to_tempfile) {
|
||||
|
||||
194
exec_cmd.c
194
exec_cmd.c
@@ -5,7 +5,7 @@
|
||||
#define MAX_ARGS 32
|
||||
|
||||
extern char **environ;
|
||||
static const char *current_exec_path;
|
||||
static const char *argv_exec_path;
|
||||
|
||||
static const char *builtin_exec_path(void)
|
||||
{
|
||||
@@ -41,9 +41,9 @@ static const char *builtin_exec_path(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void git_set_exec_path(const char *exec_path)
|
||||
void git_set_argv_exec_path(const char *exec_path)
|
||||
{
|
||||
current_exec_path = exec_path;
|
||||
argv_exec_path = exec_path;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ const char *git_exec_path(void)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
if (current_exec_path)
|
||||
return current_exec_path;
|
||||
if (argv_exec_path)
|
||||
return argv_exec_path;
|
||||
|
||||
env = getenv(EXEC_PATH_ENVIRONMENT);
|
||||
if (env && *env) {
|
||||
@@ -63,85 +63,73 @@ const char *git_exec_path(void)
|
||||
return builtin_exec_path();
|
||||
}
|
||||
|
||||
static void add_path(struct strbuf *out, const char *path)
|
||||
{
|
||||
if (path && *path) {
|
||||
if (is_absolute_path(path))
|
||||
strbuf_addstr(out, path);
|
||||
else
|
||||
strbuf_addstr(out, make_absolute_path(path));
|
||||
|
||||
#ifdef __MINGW32__
|
||||
strbuf_addch(out, ';');
|
||||
#else
|
||||
strbuf_addch(out, ':');
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void setup_path(const char *cmd_path)
|
||||
{
|
||||
const char *old_path = getenv("PATH");
|
||||
struct strbuf new_path;
|
||||
|
||||
strbuf_init(&new_path, 0);
|
||||
|
||||
add_path(&new_path, argv_exec_path);
|
||||
add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
|
||||
add_path(&new_path, builtin_exec_path());
|
||||
add_path(&new_path, cmd_path);
|
||||
|
||||
if (old_path)
|
||||
strbuf_addstr(&new_path, old_path);
|
||||
else
|
||||
strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
|
||||
|
||||
setenv("PATH", new_path.buf, 1);
|
||||
|
||||
strbuf_release(&new_path);
|
||||
}
|
||||
|
||||
int execv_git_cmd(const char **argv)
|
||||
{
|
||||
char git_command[PATH_MAX + 1];
|
||||
int i;
|
||||
const char *paths[] = { current_exec_path,
|
||||
getenv(EXEC_PATH_ENVIRONMENT),
|
||||
builtin_exec_path() };
|
||||
struct strbuf cmd;
|
||||
const char *tmp;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(paths); ++i) {
|
||||
size_t len;
|
||||
int rc;
|
||||
const char *exec_dir = paths[i];
|
||||
const char *tmp;
|
||||
strbuf_init(&cmd, 0);
|
||||
strbuf_addf(&cmd, "git-%s", argv[0]);
|
||||
|
||||
if (!exec_dir || !*exec_dir) continue;
|
||||
/*
|
||||
* argv[0] must be the git command, but the argv array
|
||||
* belongs to the caller, and may be reused in
|
||||
* subsequent loop iterations. Save argv[0] and
|
||||
* restore it on error.
|
||||
*/
|
||||
tmp = argv[0];
|
||||
argv[0] = cmd.buf;
|
||||
|
||||
if (!is_absolute_path(exec_dir)) {
|
||||
if (!getcwd(git_command, sizeof(git_command))) {
|
||||
fprintf(stderr, "git: cannot determine "
|
||||
"current directory: %s\n",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
len = strlen(git_command);
|
||||
trace_argv_printf(argv, -1, "trace: exec:");
|
||||
|
||||
/* Trivial cleanup */
|
||||
while (!prefixcmp(exec_dir, "./")) {
|
||||
exec_dir += 2;
|
||||
while (*exec_dir == '/')
|
||||
exec_dir++;
|
||||
}
|
||||
/* execvp() can only ever return if it fails */
|
||||
execvp(cmd.buf, (char **)argv);
|
||||
|
||||
rc = snprintf(git_command + len,
|
||||
sizeof(git_command) - len, "/%s",
|
||||
exec_dir);
|
||||
if (rc < 0 || rc >= sizeof(git_command) - len) {
|
||||
fprintf(stderr, "git: command name given "
|
||||
"is too long.\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (strlen(exec_dir) + 1 > sizeof(git_command)) {
|
||||
fprintf(stderr, "git: command name given "
|
||||
"is too long.\n");
|
||||
break;
|
||||
}
|
||||
strcpy(git_command, exec_dir);
|
||||
}
|
||||
trace_printf("trace: exec failed: %s\n", strerror(errno));
|
||||
|
||||
len = strlen(git_command);
|
||||
rc = snprintf(git_command + len, sizeof(git_command) - len,
|
||||
"/git-%s", argv[0]);
|
||||
if (rc < 0 || rc >= sizeof(git_command) - len) {
|
||||
fprintf(stderr,
|
||||
"git: command name given is too long.\n");
|
||||
break;
|
||||
}
|
||||
argv[0] = tmp;
|
||||
|
||||
/* argv[0] must be the git command, but the argv array
|
||||
* belongs to the caller, and my be reused in
|
||||
* subsequent loop iterations. Save argv[0] and
|
||||
* restore it on error.
|
||||
*/
|
||||
strbuf_release(&cmd);
|
||||
|
||||
tmp = argv[0];
|
||||
argv[0] = git_command;
|
||||
|
||||
trace_argv_printf(argv, -1, "trace: exec:");
|
||||
|
||||
/* execve() can only ever return if it fails */
|
||||
execve(git_command, (char **)argv, environ);
|
||||
|
||||
trace_printf("trace: exec failed: %s\n", strerror(errno));
|
||||
|
||||
argv[0] = tmp;
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -170,64 +158,12 @@ int execl_git_cmd(const char *cmd,...)
|
||||
|
||||
int spawnv_git_cmd(const char **argv, int pin[2], int pout[2])
|
||||
{
|
||||
char cmd[100];
|
||||
int i, rc;
|
||||
pid_t pid;
|
||||
const char *paths[] = { current_exec_path,
|
||||
getenv(EXEC_PATH_ENVIRONMENT),
|
||||
builtin_exec_path() };
|
||||
char p[3][PATH_MAX + 1];
|
||||
char *usedpaths[4], **up = usedpaths;
|
||||
struct strbuf cmd;
|
||||
const char *tmp;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(paths); ++i) {
|
||||
size_t len;
|
||||
const char *exec_dir = paths[i];
|
||||
|
||||
if (!exec_dir || !*exec_dir) continue;
|
||||
|
||||
if (!is_absolute_path(exec_dir)) {
|
||||
if (!getcwd(p[i], sizeof(p[i]))) {
|
||||
fprintf(stderr, "git: cannot determine "
|
||||
"current directory: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
len = strlen(p[i]);
|
||||
|
||||
/* Trivial cleanup */
|
||||
while (!strncmp(exec_dir, "./", 2)) {
|
||||
exec_dir += 2;
|
||||
while (*exec_dir == '/')
|
||||
exec_dir++;
|
||||
}
|
||||
|
||||
rc = snprintf(p[i] + len,
|
||||
sizeof(p[i]) - len, "/%s",
|
||||
exec_dir);
|
||||
if (rc < 0 || rc >= sizeof(p[i]) - len) {
|
||||
fprintf(stderr, "git: command name given "
|
||||
"is too long.\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (strlen(exec_dir) + 1 > sizeof(p[i])) {
|
||||
fprintf(stderr, "git: command name given "
|
||||
"is too long.\n");
|
||||
return -1;
|
||||
}
|
||||
strcpy(p[i], exec_dir);
|
||||
}
|
||||
*up++ = p[i];
|
||||
}
|
||||
*up = NULL;
|
||||
|
||||
rc = snprintf(cmd, sizeof(cmd), "git-%s", argv[0]);
|
||||
if (rc < 0 || rc >= sizeof(cmd)) {
|
||||
fprintf(stderr,
|
||||
"git: command name given is too long.\n");
|
||||
return -1;
|
||||
}
|
||||
strbuf_init(&cmd, 0);
|
||||
strbuf_addf(&cmd, "git-%s", argv[0]);
|
||||
|
||||
/* argv[0] must be the git command, but the argv array
|
||||
* belongs to the caller. Save argv[0] and
|
||||
@@ -235,11 +171,11 @@ int spawnv_git_cmd(const char **argv, int pin[2], int pout[2])
|
||||
*/
|
||||
|
||||
tmp = argv[0];
|
||||
argv[0] = cmd;
|
||||
argv[0] = cmd.buf;
|
||||
|
||||
trace_argv_printf(argv, -1, "trace: exec:");
|
||||
|
||||
pid = spawnvppe_pipe(cmd, argv, environ, usedpaths,
|
||||
pid = spawnvpe_pipe(cmd.buf, argv, environ,
|
||||
pin, pout);
|
||||
|
||||
argv[0] = tmp;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#ifndef GIT_EXEC_CMD_H
|
||||
#define GIT_EXEC_CMD_H
|
||||
|
||||
extern void git_set_exec_path(const char *exec_path);
|
||||
extern void git_set_argv_exec_path(const char *exec_path);
|
||||
extern const char* git_exec_path(void);
|
||||
extern void setup_path(const char *);
|
||||
extern int execv_git_cmd(const char **argv); /* NULL terminated */
|
||||
extern int execl_git_cmd(const char *cmd, ...);
|
||||
extern int spawnv_git_cmd(const char **argv, int pin[2], int pout[2]);
|
||||
|
||||
@@ -1616,6 +1616,7 @@ static void cmd_data(struct strbuf *sb)
|
||||
char *term = xstrdup(command_buf.buf + 5 + 2);
|
||||
size_t term_len = command_buf.len - 5 - 2;
|
||||
|
||||
strbuf_detach(&command_buf, NULL);
|
||||
for (;;) {
|
||||
if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
|
||||
die("EOF in data (terminator '%s' not found)", term);
|
||||
|
||||
252
git-bisect.sh
252
git-bisect.sh
@@ -1,12 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
|
||||
USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
|
||||
LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
|
||||
reset bisect state and start bisection.
|
||||
git bisect bad [<rev>]
|
||||
mark <rev> a known-bad revision.
|
||||
git bisect good [<rev>...]
|
||||
mark <rev>... known-good revisions.
|
||||
git bisect skip [<rev>...]
|
||||
mark <rev>... untestable revisions.
|
||||
git bisect next
|
||||
find next bisection to test and check it out.
|
||||
git bisect reset [<branch>]
|
||||
@@ -64,7 +66,7 @@ bisect_start() {
|
||||
branch=`cat "$GIT_DIR/head-name"`
|
||||
else
|
||||
branch=master
|
||||
fi
|
||||
fi
|
||||
git checkout $branch || exit
|
||||
;;
|
||||
refs/heads/*)
|
||||
@@ -95,75 +97,74 @@ bisect_start() {
|
||||
arg="$1"
|
||||
case "$arg" in
|
||||
--)
|
||||
shift
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
|
||||
rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
|
||||
test $has_double_dash -eq 1 &&
|
||||
die "'$arg' does not appear to be a valid revision"
|
||||
break
|
||||
}
|
||||
if [ $bad_seen -eq 0 ]; then
|
||||
bad_seen=1
|
||||
bisect_write_bad "$rev"
|
||||
else
|
||||
bisect_write_good "$rev"
|
||||
fi
|
||||
shift
|
||||
case $bad_seen in
|
||||
0) state='bad' ; bad_seen=1 ;;
|
||||
*) state='good' ;;
|
||||
esac
|
||||
bisect_write "$state" "$rev" 'nolog'
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
sq "$@" >"$GIT_DIR/BISECT_NAMES"
|
||||
echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_bad() {
|
||||
bisect_write() {
|
||||
state="$1"
|
||||
rev="$2"
|
||||
nolog="$3"
|
||||
case "$state" in
|
||||
bad) tag="$state" ;;
|
||||
good|skip) tag="$state"-"$rev" ;;
|
||||
*) die "Bad bisect_write argument: $state" ;;
|
||||
esac
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
|
||||
echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
bisect_state() {
|
||||
bisect_autostart
|
||||
case "$#" in
|
||||
0)
|
||||
rev=$(git rev-parse --verify HEAD) ;;
|
||||
1)
|
||||
rev=$(git rev-parse --verify "$1^{commit}") ;;
|
||||
state=$1
|
||||
case "$#,$state" in
|
||||
0,*)
|
||||
die "Please call 'bisect_state' with at least one argument." ;;
|
||||
1,bad|1,good|1,skip)
|
||||
rev=$(git rev-parse --verify HEAD) ||
|
||||
die "Bad rev input: HEAD"
|
||||
bisect_write "$state" "$rev" ;;
|
||||
2,bad)
|
||||
rev=$(git rev-parse --verify "$2^{commit}") ||
|
||||
die "Bad rev input: $2"
|
||||
bisect_write "$state" "$rev" ;;
|
||||
*,good|*,skip)
|
||||
shift
|
||||
revs=$(git rev-parse --revs-only --no-flags "$@") &&
|
||||
test '' != "$revs" || die "Bad rev input: $@"
|
||||
for rev in $revs
|
||||
do
|
||||
rev=$(git rev-parse --verify "$rev^{commit}") ||
|
||||
die "Bad rev commit: $rev^{commit}"
|
||||
bisect_write "$state" "$rev"
|
||||
done ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac || exit
|
||||
bisect_write_bad "$rev"
|
||||
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_write_bad() {
|
||||
rev="$1"
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
|
||||
echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
bisect_good() {
|
||||
bisect_autostart
|
||||
case "$#" in
|
||||
0) revs=$(git rev-parse --verify HEAD) || exit ;;
|
||||
*) revs=$(git rev-parse --revs-only --no-flags "$@") &&
|
||||
test '' != "$revs" || die "Bad rev input: $@" ;;
|
||||
esac
|
||||
for rev in $revs
|
||||
do
|
||||
rev=$(git rev-parse --verify "$rev^{commit}") || exit
|
||||
bisect_write_good "$rev"
|
||||
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
|
||||
done
|
||||
bisect_auto_next
|
||||
}
|
||||
|
||||
bisect_write_good() {
|
||||
rev="$1"
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
|
||||
echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
}
|
||||
|
||||
bisect_next_check() {
|
||||
missing_good= missing_bad=
|
||||
git show-ref -q --verify refs/bisect/bad || missing_bad=t
|
||||
@@ -206,17 +207,97 @@ bisect_auto_next() {
|
||||
bisect_next_check && bisect_next || :
|
||||
}
|
||||
|
||||
filter_skipped() {
|
||||
_eval="$1"
|
||||
_skip="$2"
|
||||
|
||||
if [ -z "$_skip" ]; then
|
||||
eval $_eval
|
||||
return
|
||||
fi
|
||||
|
||||
# Let's parse the output of:
|
||||
# "git rev-list --bisect-vars --bisect-all ..."
|
||||
eval $_eval | while read hash line
|
||||
do
|
||||
case "$VARS,$FOUND,$TRIED,$hash" in
|
||||
# We display some vars.
|
||||
1,*,*,*) echo "$hash $line" ;;
|
||||
|
||||
# Split line.
|
||||
,*,*,---*) ;;
|
||||
|
||||
# We had nothing to search.
|
||||
,,,bisect_rev*)
|
||||
echo "bisect_rev="
|
||||
VARS=1
|
||||
;;
|
||||
|
||||
# We did not find a good bisect rev.
|
||||
# This should happen only if the "bad"
|
||||
# commit is also a "skip" commit.
|
||||
,,*,bisect_rev*)
|
||||
echo "bisect_rev=$TRIED"
|
||||
VARS=1
|
||||
;;
|
||||
|
||||
# We are searching.
|
||||
,,*,*)
|
||||
TRIED="${TRIED:+$TRIED|}$hash"
|
||||
case "$_skip" in
|
||||
*$hash*) ;;
|
||||
*)
|
||||
echo "bisect_rev=$hash"
|
||||
echo "bisect_tried=\"$TRIED\""
|
||||
FOUND=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
# We have already found a rev to be tested.
|
||||
,1,*,bisect_rev*) VARS=1 ;;
|
||||
,1,*,*) ;;
|
||||
|
||||
# ???
|
||||
*) die "filter_skipped error " \
|
||||
"VARS: '$VARS' " \
|
||||
"FOUND: '$FOUND' " \
|
||||
"TRIED: '$TRIED' " \
|
||||
"hash: '$hash' " \
|
||||
"line: '$line'"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
exit_if_skipped_commits () {
|
||||
_tried=$1
|
||||
if expr "$_tried" : ".*[|].*" > /dev/null ; then
|
||||
echo "There are only 'skip'ped commit left to test."
|
||||
echo "The first bad commit could be any of:"
|
||||
echo "$_tried" | sed -e 's/[|]/\n/g'
|
||||
echo "We cannot bisect more!"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
bisect_next() {
|
||||
case "$#" in 0) ;; *) usage ;; esac
|
||||
case "$#" in 0) ;; *) usage ;; esac
|
||||
bisect_autostart
|
||||
bisect_next_check good
|
||||
|
||||
skip=$(git for-each-ref --format='%(objectname)' \
|
||||
"refs/bisect/skip-*" | tr '[\012]' ' ') || exit
|
||||
|
||||
BISECT_OPT=''
|
||||
test -n "$skip" && BISECT_OPT='--bisect-all'
|
||||
|
||||
bad=$(git rev-parse --verify refs/bisect/bad) &&
|
||||
good=$(git for-each-ref --format='^%(objectname)' \
|
||||
"refs/bisect/good-*" | tr '[\012]' ' ') &&
|
||||
eval="git rev-list --bisect-vars $good $bad --" &&
|
||||
eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
|
||||
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
|
||||
eval=$(eval "$eval") &&
|
||||
eval=$(filter_skipped "$eval" "$skip") &&
|
||||
eval "$eval" || exit
|
||||
|
||||
if [ -z "$bisect_rev" ]; then
|
||||
@@ -224,11 +305,16 @@ bisect_next() {
|
||||
exit 1
|
||||
fi
|
||||
if [ "$bisect_rev" = "$bad" ]; then
|
||||
exit_if_skipped_commits "$bisect_tried"
|
||||
echo "$bisect_rev is first bad commit"
|
||||
git diff-tree --pretty $bisect_rev
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# We should exit here only if the "bad"
|
||||
# commit is also a "skip" commit (see above).
|
||||
exit_if_skipped_commits "$bisect_rev"
|
||||
|
||||
echo "Bisecting: $bisect_nr revisions left to test after this"
|
||||
echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
|
||||
git checkout -q new-bisect || exit
|
||||
@@ -250,12 +336,10 @@ bisect_reset() {
|
||||
else
|
||||
branch=master
|
||||
fi ;;
|
||||
1) git show-ref --verify --quiet -- "refs/heads/$1" || {
|
||||
echo >&2 "$1 does not seem to be a valid branch"
|
||||
exit 1
|
||||
}
|
||||
1) git show-ref --verify --quiet -- "refs/heads/$1" ||
|
||||
die "$1 does not seem to be a valid branch"
|
||||
branch="$1" ;;
|
||||
*)
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
if git checkout "$branch"; then
|
||||
@@ -273,10 +357,7 @@ bisect_clean_state() {
|
||||
}
|
||||
|
||||
bisect_replay () {
|
||||
test -r "$1" || {
|
||||
echo >&2 "cannot read $1 for replaying"
|
||||
exit 1
|
||||
}
|
||||
test -r "$1" || die "cannot read $1 for replaying"
|
||||
bisect_reset
|
||||
while read bisect command rev
|
||||
do
|
||||
@@ -284,21 +365,11 @@ bisect_replay () {
|
||||
case "$command" in
|
||||
start)
|
||||
cmd="bisect_start $rev"
|
||||
eval "$cmd"
|
||||
;;
|
||||
good)
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
|
||||
echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
;;
|
||||
bad)
|
||||
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
|
||||
echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
|
||||
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
|
||||
;;
|
||||
eval "$cmd" ;;
|
||||
good|bad|skip)
|
||||
bisect_write "$command" "$rev" ;;
|
||||
*)
|
||||
echo >&2 "?? what are you talking about?"
|
||||
exit 1 ;;
|
||||
die "?? what are you talking about?" ;;
|
||||
esac
|
||||
done <"$1"
|
||||
bisect_auto_next
|
||||
@@ -320,24 +391,31 @@ bisect_run () {
|
||||
exit $res
|
||||
fi
|
||||
|
||||
# Use "bisect_good" or "bisect_bad"
|
||||
# depending on run success or failure.
|
||||
if [ $res -gt 0 ]; then
|
||||
next_bisect='bisect_bad'
|
||||
# Find current state depending on run success or failure.
|
||||
# A special exit code of 125 means cannot test.
|
||||
if [ $res -eq 125 ]; then
|
||||
state='skip'
|
||||
elif [ $res -gt 0 ]; then
|
||||
state='bad'
|
||||
else
|
||||
next_bisect='bisect_good'
|
||||
state='good'
|
||||
fi
|
||||
|
||||
# We have to use a subshell because bisect_good or
|
||||
# bisect_bad functions can exit.
|
||||
( $next_bisect > "$GIT_DIR/BISECT_RUN" )
|
||||
# We have to use a subshell because "bisect_state" can exit.
|
||||
( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
|
||||
res=$?
|
||||
|
||||
cat "$GIT_DIR/BISECT_RUN"
|
||||
|
||||
if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
|
||||
> /dev/null; then
|
||||
echo >&2 "bisect run cannot continue any more"
|
||||
exit $res
|
||||
fi
|
||||
|
||||
if [ $res -ne 0 ]; then
|
||||
echo >&2 "bisect run failed:"
|
||||
echo >&2 "$next_bisect exited with error code $res"
|
||||
echo >&2 "'bisect_state $state' exited with error code $res"
|
||||
exit $res
|
||||
fi
|
||||
|
||||
@@ -359,10 +437,8 @@ case "$#" in
|
||||
case "$cmd" in
|
||||
start)
|
||||
bisect_start "$@" ;;
|
||||
bad)
|
||||
bisect_bad "$@" ;;
|
||||
good)
|
||||
bisect_good "$@" ;;
|
||||
bad|good|skip)
|
||||
bisect_state "$cmd" "$@" ;;
|
||||
next)
|
||||
# Not sure we want "next" at the UI level anymore.
|
||||
bisect_next "$@" ;;
|
||||
|
||||
@@ -65,7 +65,8 @@ get_repo_base() {
|
||||
) 2>/dev/null
|
||||
}
|
||||
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" -o \
|
||||
"`git config --bool http.sslVerify`" = false ]; then
|
||||
curl_extra_args="-k"
|
||||
fi
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ foreach my $line (@commit) {
|
||||
}
|
||||
}
|
||||
|
||||
my $noparent = "0000000000000000000000000000000000000000";
|
||||
if ($parent) {
|
||||
my $found;
|
||||
# double check that it's a valid parent
|
||||
@@ -95,8 +96,10 @@ if ($parent) {
|
||||
} else { # we don't have a parent from the cmdline...
|
||||
if (@parents == 1) { # it's safe to get it from the commit
|
||||
$parent = $parents[0];
|
||||
} else { # or perhaps not!
|
||||
die "This commit has more than one parent -- please name the parent you want to use explicitly";
|
||||
} elsif (@parents == 0) { # there is no parent
|
||||
$parent = $noparent;
|
||||
} else { # cannot choose automatically from multiple parents
|
||||
die "This commit has more than one parent -- please name the parent you want to use explicitly";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +119,11 @@ if ($opt_a) {
|
||||
}
|
||||
close MSG;
|
||||
|
||||
`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
|
||||
if ($parent eq $noparent) {
|
||||
`git-diff-tree --binary -p --root $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
|
||||
} else {
|
||||
`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
|
||||
}
|
||||
|
||||
## apply non-binary changes
|
||||
|
||||
|
||||
@@ -67,9 +67,10 @@ tmpdir=$tmp-d
|
||||
|
||||
case "$peek_repo" in
|
||||
http://* | https://* | ftp://* )
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
|
||||
curl_extra_args="-k"
|
||||
fi
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" -o \
|
||||
"`git config --bool http.sslVerify`" = false ]; then
|
||||
curl_extra_args="-k"
|
||||
fi
|
||||
if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
|
||||
"`git config --bool http.noEPSV`" = true ]; then
|
||||
curl_extra_args="${curl_extra_args} --disable-epsv"
|
||||
|
||||
107
git-mergetool.sh
107
git-mergetool.sh
@@ -192,10 +192,10 @@ merge_file () {
|
||||
case "$merge_tool" in
|
||||
kdiff3)
|
||||
if base_present ; then
|
||||
(kdiff3 --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
|
||||
("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
|
||||
-o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
|
||||
else
|
||||
(kdiff3 --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
|
||||
("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
|
||||
-o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
|
||||
fi
|
||||
status=$?
|
||||
@@ -203,35 +203,35 @@ merge_file () {
|
||||
;;
|
||||
tkdiff)
|
||||
if base_present ; then
|
||||
tkdiff -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
|
||||
"$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
|
||||
else
|
||||
tkdiff -o "$path" -- "$LOCAL" "$REMOTE"
|
||||
"$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE"
|
||||
fi
|
||||
status=$?
|
||||
save_backup
|
||||
;;
|
||||
meld|vimdiff)
|
||||
touch "$BACKUP"
|
||||
$merge_tool -- "$LOCAL" "$path" "$REMOTE"
|
||||
"$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE"
|
||||
check_unchanged
|
||||
save_backup
|
||||
;;
|
||||
gvimdiff)
|
||||
touch "$BACKUP"
|
||||
gvimdiff -f -- "$LOCAL" "$path" "$REMOTE"
|
||||
"$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE"
|
||||
check_unchanged
|
||||
save_backup
|
||||
;;
|
||||
xxdiff)
|
||||
touch "$BACKUP"
|
||||
if base_present ; then
|
||||
xxdiff -X --show-merged-pane \
|
||||
"$merge_tool_path" -X --show-merged-pane \
|
||||
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
|
||||
-R 'Accel.Search: "Ctrl+F"' \
|
||||
-R 'Accel.SearchForward: "Ctrl-G"' \
|
||||
--merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
|
||||
else
|
||||
xxdiff -X --show-merged-pane \
|
||||
"$merge_tool_path" -X --show-merged-pane \
|
||||
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
|
||||
-R 'Accel.Search: "Ctrl+F"' \
|
||||
-R 'Accel.SearchForward: "Ctrl-G"' \
|
||||
@@ -243,18 +243,28 @@ merge_file () {
|
||||
opendiff)
|
||||
touch "$BACKUP"
|
||||
if base_present; then
|
||||
opendiff "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
|
||||
"$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
|
||||
else
|
||||
opendiff "$LOCAL" "$REMOTE" -merge "$path" | cat
|
||||
"$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat
|
||||
fi
|
||||
check_unchanged
|
||||
save_backup
|
||||
;;
|
||||
ecmerge)
|
||||
touch "$BACKUP"
|
||||
if base_present; then
|
||||
"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path"
|
||||
else
|
||||
"$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path"
|
||||
fi
|
||||
check_unchanged
|
||||
save_backup
|
||||
;;
|
||||
emerge)
|
||||
if base_present ; then
|
||||
emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
|
||||
"$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
|
||||
else
|
||||
emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
|
||||
"$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
|
||||
fi
|
||||
status=$?
|
||||
save_backup
|
||||
@@ -297,17 +307,38 @@ do
|
||||
shift
|
||||
done
|
||||
|
||||
valid_tool() {
|
||||
case "$1" in
|
||||
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
|
||||
;; # happy
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
init_merge_tool_path() {
|
||||
merge_tool_path=`git config mergetool.$1.path`
|
||||
if test -z "$merge_tool_path" ; then
|
||||
case "$1" in
|
||||
emerge)
|
||||
merge_tool_path=emacs
|
||||
;;
|
||||
*)
|
||||
merge_tool_path=$1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
if test -z "$merge_tool"; then
|
||||
merge_tool=`git config merge.tool`
|
||||
case "$merge_tool" in
|
||||
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | "")
|
||||
;; # happy
|
||||
*)
|
||||
if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
|
||||
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
|
||||
echo >&2 "Resetting to default..."
|
||||
unset merge_tool
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$merge_tool" ; then
|
||||
@@ -329,40 +360,30 @@ if test -z "$merge_tool" ; then
|
||||
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
|
||||
echo "merge tool candidates: $merge_tool_candidates"
|
||||
for i in $merge_tool_candidates; do
|
||||
if test $i = emerge ; then
|
||||
cmd=emacs
|
||||
else
|
||||
cmd=$i
|
||||
fi
|
||||
if type $cmd > /dev/null 2>&1; then
|
||||
init_merge_tool_path $i
|
||||
if type "$merge_tool_path" > /dev/null 2>&1; then
|
||||
merge_tool=$i
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test -z "$merge_tool" ; then
|
||||
echo "No available merge resolution programs available."
|
||||
echo "No known merge resolution program available."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! valid_tool "$merge_tool"; then
|
||||
echo >&2 "Unknown merge_tool $merge_tool"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
init_merge_tool_path "$merge_tool"
|
||||
|
||||
if ! type "$merge_tool_path" > /dev/null 2>&1; then
|
||||
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$merge_tool" in
|
||||
kdiff3|tkdiff|meld|xxdiff|vimdiff|gvimdiff|opendiff)
|
||||
if ! type "$merge_tool" > /dev/null 2>&1; then
|
||||
echo "The merge tool $merge_tool is not available"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
emerge)
|
||||
if ! type "emacs" > /dev/null 2>&1; then
|
||||
echo "Emacs is not available"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Unknown merge tool: $merge_tool"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if test $# -eq 0 ; then
|
||||
files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
|
||||
|
||||
14
git-pull.sh
14
git-pull.sh
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Fetch one or more remote refs and merge it/them into the current HEAD.
|
||||
|
||||
USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [<fetch-options>] <repo> <head>...'
|
||||
USAGE='[-n | --no-summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
|
||||
LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
@@ -15,7 +15,7 @@ cd_to_toplevel
|
||||
test -z "$(git ls-files -u)" ||
|
||||
die "You are in the middle of a conflicted merge."
|
||||
|
||||
strategy_args= no_summary= no_commit= squash=
|
||||
strategy_args= no_summary= no_commit= squash= no_ff=
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
@@ -27,8 +27,16 @@ do
|
||||
;;
|
||||
--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
|
||||
no_commit=--no-commit ;;
|
||||
--c|--co|--com|--comm|--commi|--commit)
|
||||
no_commit=--commit ;;
|
||||
--sq|--squ|--squa|--squas|--squash)
|
||||
squash=--squash ;;
|
||||
--no-sq|--no-squ|--no-squa|--no-squas|--no-squash)
|
||||
squash=--no-squash ;;
|
||||
--ff)
|
||||
no_ff=--ff ;;
|
||||
--no-ff)
|
||||
no_ff=--no-ff ;;
|
||||
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
|
||||
--strateg=*|--strategy=*|\
|
||||
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
|
||||
@@ -133,5 +141,5 @@ then
|
||||
fi
|
||||
|
||||
merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
|
||||
exec git-merge $no_summary $no_commit $squash $strategy_args \
|
||||
exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \
|
||||
"$merge_name" HEAD $merge_head
|
||||
|
||||
@@ -116,7 +116,7 @@ pick_one () {
|
||||
sha1=$(git rev-parse --short $sha1)
|
||||
output warn Fast forward to $sha1
|
||||
else
|
||||
output git cherry-pick $STRATEGY "$@"
|
||||
output git cherry-pick "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -172,6 +172,8 @@ pick_one_preserving_merges () {
|
||||
author_script=$(get_author_ident_from_commit $sha1)
|
||||
eval "$author_script"
|
||||
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" \
|
||||
@@ -184,7 +186,7 @@ pick_one_preserving_merges () {
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
output git cherry-pick $STRATEGY "$@" ||
|
||||
output git cherry-pick "$@" ||
|
||||
die_with_patch $sha1 "Could not pick $sha1"
|
||||
;;
|
||||
esac
|
||||
@@ -387,7 +389,6 @@ do
|
||||
output git reset --hard && do_rest
|
||||
;;
|
||||
-s|--strategy)
|
||||
shift
|
||||
case "$#,$1" in
|
||||
*,*=*)
|
||||
STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;;
|
||||
|
||||
@@ -59,7 +59,7 @@ continue_merge () {
|
||||
die "$RESOLVEMSG"
|
||||
fi
|
||||
|
||||
cmt=`cat $dotest/current`
|
||||
cmt=`cat "$dotest/current"`
|
||||
if ! git diff-index --quiet HEAD
|
||||
then
|
||||
if ! git-commit -C "$cmt"
|
||||
@@ -84,14 +84,14 @@ continue_merge () {
|
||||
}
|
||||
|
||||
call_merge () {
|
||||
cmt="$(cat $dotest/cmt.$1)"
|
||||
cmt="$(cat "$dotest/cmt.$1")"
|
||||
echo "$cmt" > "$dotest/current"
|
||||
hd=$(git rev-parse --verify HEAD)
|
||||
cmt_name=$(git symbolic-ref HEAD)
|
||||
msgnum=$(cat $dotest/msgnum)
|
||||
end=$(cat $dotest/end)
|
||||
msgnum=$(cat "$dotest/msgnum")
|
||||
end=$(cat "$dotest/end")
|
||||
eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
|
||||
eval GITHEAD_$hd='"$(cat $dotest/onto_name)"'
|
||||
eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
|
||||
export GITHEAD_$cmt GITHEAD_$hd
|
||||
git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
|
||||
rv=$?
|
||||
@@ -140,10 +140,10 @@ do
|
||||
}
|
||||
if test -d "$dotest"
|
||||
then
|
||||
prev_head="`cat $dotest/prev_head`"
|
||||
end="`cat $dotest/end`"
|
||||
msgnum="`cat $dotest/msgnum`"
|
||||
onto="`cat $dotest/onto`"
|
||||
prev_head=$(cat "$dotest/prev_head")
|
||||
end=$(cat "$dotest/end")
|
||||
msgnum=$(cat "$dotest/msgnum")
|
||||
onto=$(cat "$dotest/onto")
|
||||
continue_merge
|
||||
while test "$msgnum" -le "$end"
|
||||
do
|
||||
@@ -160,11 +160,11 @@ do
|
||||
if test -d "$dotest"
|
||||
then
|
||||
git rerere clear
|
||||
prev_head="`cat $dotest/prev_head`"
|
||||
end="`cat $dotest/end`"
|
||||
msgnum="`cat $dotest/msgnum`"
|
||||
prev_head=$(cat "$dotest/prev_head")
|
||||
end=$(cat "$dotest/end")
|
||||
msgnum=$(cat "$dotest/msgnum")
|
||||
msgnum=$(($msgnum + 1))
|
||||
onto="`cat $dotest/onto`"
|
||||
onto=$(cat "$dotest/onto")
|
||||
while test "$msgnum" -le "$end"
|
||||
do
|
||||
call_merge "$msgnum"
|
||||
|
||||
@@ -244,7 +244,8 @@ sub show_remote {
|
||||
print "* remote $name\n";
|
||||
print " URL: $info->{'URL'}\n";
|
||||
for my $branchname (sort keys %$branch) {
|
||||
next if ($branch->{$branchname}{'REMOTE'} ne $name);
|
||||
next unless (defined $branch->{$branchname}{'REMOTE'} &&
|
||||
$branch->{$branchname}{'REMOTE'} eq $name);
|
||||
my @merged = map {
|
||||
s|^refs/heads/||;
|
||||
$_;
|
||||
|
||||
@@ -191,6 +191,7 @@ my %config_settings = (
|
||||
"smtpserverport" => \$smtp_server_port,
|
||||
"smtpuser" => \$smtp_authuser,
|
||||
"smtppass" => \$smtp_authpass,
|
||||
"to" => \@to,
|
||||
"cccmd" => \$cc_cmd,
|
||||
"aliasfiletype" => \$aliasfiletype,
|
||||
"bcc" => \@bcclist,
|
||||
|
||||
@@ -110,7 +110,7 @@ esac
|
||||
if [ -z "$SUBDIRECTORY_OK" ]
|
||||
then
|
||||
: ${GIT_DIR=.git}
|
||||
GIT_DIR=$(GIT_DIR="$GIT_DIR" git rev-parse --git-dir) || {
|
||||
test -z "$(git rev-parse --show-cdup)" || {
|
||||
exit=$?
|
||||
echo >&2 "You need to run this command from the toplevel of the working tree."
|
||||
exit $exit
|
||||
|
||||
56
git.c
56
git.c
@@ -6,32 +6,6 @@
|
||||
const char git_usage_string[] =
|
||||
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
|
||||
|
||||
static void prepend_to_path(const char *dir, int len)
|
||||
{
|
||||
const char *old_path = getenv("PATH");
|
||||
char *path;
|
||||
int path_len = len;
|
||||
|
||||
if (!old_path)
|
||||
old_path = "/usr/local/bin:/usr/bin:/bin";
|
||||
|
||||
path_len = len + strlen(old_path) + 1;
|
||||
|
||||
path = xmalloc(path_len + 1);
|
||||
|
||||
memcpy(path, dir, len);
|
||||
#ifdef __MINGW32__
|
||||
path[len] = ';';
|
||||
#else
|
||||
path[len] = ':';
|
||||
#endif
|
||||
memcpy(path + len + 1, old_path, path_len - len);
|
||||
|
||||
setenv("PATH", path, 1);
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
static int handle_options(const char*** argv, int* argc, int* envchanged)
|
||||
{
|
||||
int handled = 0;
|
||||
@@ -55,7 +29,7 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
|
||||
if (!prefixcmp(cmd, "--exec-path")) {
|
||||
cmd += 11;
|
||||
if (*cmd == '=')
|
||||
git_set_exec_path(cmd + 1);
|
||||
git_set_argv_exec_path(cmd + 1);
|
||||
else {
|
||||
puts(git_exec_path());
|
||||
exit(0);
|
||||
@@ -421,7 +395,7 @@ int main(int argc, const char **argv)
|
||||
{
|
||||
const char *cmd = argv[0] ? argv[0] : "git-help";
|
||||
char *slash = strrchr(cmd, '/');
|
||||
const char *exec_path = NULL;
|
||||
const char *cmd_path = NULL;
|
||||
int done_alias = 0;
|
||||
|
||||
/*
|
||||
@@ -435,10 +409,7 @@ int main(int argc, const char **argv)
|
||||
#endif
|
||||
if (slash) {
|
||||
*slash++ = 0;
|
||||
if (is_absolute_path(cmd))
|
||||
exec_path = cmd;
|
||||
else
|
||||
exec_path = xstrdup(make_absolute_path(cmd));
|
||||
cmd_path = cmd;
|
||||
cmd = slash;
|
||||
}
|
||||
|
||||
@@ -467,23 +438,20 @@ int main(int argc, const char **argv)
|
||||
if (!prefixcmp(argv[0], "--"))
|
||||
argv[0] += 2;
|
||||
} else {
|
||||
/* Default command: "help" */
|
||||
argv[0] = "help";
|
||||
argc = 1;
|
||||
/* The user didn't specify a command; give them help */
|
||||
printf("usage: %s\n\n", git_usage_string);
|
||||
list_common_cmds_help();
|
||||
exit(1);
|
||||
}
|
||||
cmd = argv[0];
|
||||
|
||||
/*
|
||||
* We execute external git command via execv_git_cmd(),
|
||||
* which looks at "--exec-path" option, GIT_EXEC_PATH
|
||||
* environment, and $(gitexecdir) in Makefile while built,
|
||||
* in this order. For scripted commands, we prepend
|
||||
* the value of the exec_path variable to the PATH.
|
||||
* We use PATH to find git commands, but we prepend some higher
|
||||
* precidence paths: the "--exec-path" option, the GIT_EXEC_PATH
|
||||
* environment, and the $(gitexecdir) from the Makefile at build
|
||||
* time.
|
||||
*/
|
||||
if (exec_path)
|
||||
prepend_to_path(exec_path, strlen(exec_path));
|
||||
exec_path = git_exec_path();
|
||||
prepend_to_path(exec_path, strlen(exec_path));
|
||||
setup_path(cmd_path);
|
||||
|
||||
while (1) {
|
||||
/* See if it's an internal command */
|
||||
|
||||
@@ -116,7 +116,7 @@ GITWEB_CONFIG file:
|
||||
$feature{'pickaxe'}{'default'} = [1];
|
||||
$feature{'pickaxe'}{'override'} = 1;
|
||||
|
||||
$feature{'snapshot'}{'default'} = ['x-gzip', 'gz', 'gzip'];
|
||||
$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
|
||||
$feature{'snapshot'}{'override'} = 1;
|
||||
|
||||
|
||||
|
||||
@@ -846,6 +846,23 @@ sub chop_str {
|
||||
return "$body$tail";
|
||||
}
|
||||
|
||||
# takes the same arguments as chop_str, but also wraps a <span> around the
|
||||
# result with a title attribute if it does get chopped. Additionally, the
|
||||
# string is HTML-escaped.
|
||||
sub chop_and_escape_str {
|
||||
my $str = shift;
|
||||
my $len = shift;
|
||||
my $add_len = shift || 10;
|
||||
|
||||
my $chopped = chop_str($str, $len, $add_len);
|
||||
if ($chopped eq $str) {
|
||||
return esc_html($chopped);
|
||||
} else {
|
||||
return qq{<span title="} . esc_html($str) . qq{">} .
|
||||
esc_html($chopped) . qq{</span>};
|
||||
}
|
||||
}
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## functions returning short strings
|
||||
|
||||
@@ -2000,6 +2017,19 @@ sub parse_difftree_raw_line {
|
||||
return wantarray ? %res : \%res;
|
||||
}
|
||||
|
||||
# wrapper: return parsed line of git-diff-tree "raw" output
|
||||
# (the argument might be raw line, or parsed info)
|
||||
sub parsed_difftree_line {
|
||||
my $line_or_ref = shift;
|
||||
|
||||
if (ref($line_or_ref) eq "HASH") {
|
||||
# pre-parsed (or generated by hand)
|
||||
return $line_or_ref;
|
||||
} else {
|
||||
return parse_difftree_raw_line($line_or_ref);
|
||||
}
|
||||
}
|
||||
|
||||
# parse line of git-ls-tree output
|
||||
sub parse_ls_tree_line ($;%) {
|
||||
my $line = shift;
|
||||
@@ -2043,6 +2073,7 @@ sub parse_from_to_diffinfo {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# ordinary (not combined) diff
|
||||
$from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
|
||||
if ($diffinfo->{'status'} ne "A") { # not new (added) file
|
||||
$from->{'href'} = href(action=>"blob", hash_base=>$hash_parent,
|
||||
@@ -2766,6 +2797,7 @@ sub git_print_tree_entry {
|
||||
## ......................................................................
|
||||
## functions printing large fragments of HTML
|
||||
|
||||
# get pre-image filenames for merge (combined) diff
|
||||
sub fill_from_file_info {
|
||||
my ($diff, @parents) = @_;
|
||||
|
||||
@@ -2782,28 +2814,25 @@ sub fill_from_file_info {
|
||||
return $diff;
|
||||
}
|
||||
|
||||
# parameters can be strings, or references to arrays of strings
|
||||
sub from_ids_eq {
|
||||
my ($a, $b) = @_;
|
||||
|
||||
if (ref($a) eq "ARRAY" && ref($b) eq "ARRAY" && @$a == @$b) {
|
||||
for (my $i = 0; $i < @$a; ++$i) {
|
||||
return 0 unless ($a->[$i] eq $b->[$i]);
|
||||
}
|
||||
return 1;
|
||||
} elsif (!ref($a) && !ref($b)) {
|
||||
return $a eq $b;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
# is current raw difftree line of file deletion
|
||||
sub is_deleted {
|
||||
my $diffinfo = shift;
|
||||
|
||||
return $diffinfo->{'to_id'} eq ('0' x 40);
|
||||
}
|
||||
|
||||
# does patch correspond to [previous] difftree raw line
|
||||
# $diffinfo - hashref of parsed raw diff format
|
||||
# $patchinfo - hashref of parsed patch diff format
|
||||
# (the same keys as in $diffinfo)
|
||||
sub is_patch_split {
|
||||
my ($diffinfo, $patchinfo) = @_;
|
||||
|
||||
return defined $diffinfo && defined $patchinfo
|
||||
&& ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'};
|
||||
}
|
||||
|
||||
|
||||
sub git_difftree_body {
|
||||
my ($difftree, $hash, @parents) = @_;
|
||||
my ($parent) = $parents[0];
|
||||
@@ -2840,13 +2869,7 @@ sub git_difftree_body {
|
||||
my $alternate = 1;
|
||||
my $patchno = 0;
|
||||
foreach my $line (@{$difftree}) {
|
||||
my $diff;
|
||||
if (ref($line) eq "HASH") {
|
||||
# pre-parsed (or generated by hand)
|
||||
$diff = $line;
|
||||
} else {
|
||||
$diff = parse_difftree_raw_line($line);
|
||||
}
|
||||
my $diff = parsed_difftree_line($line);
|
||||
|
||||
if ($alternate) {
|
||||
print "<tr class=\"dark\">\n";
|
||||
@@ -3117,10 +3140,12 @@ sub git_patchset_body {
|
||||
my ($fd, $difftree, $hash, @hash_parents) = @_;
|
||||
my ($hash_parent) = $hash_parents[0];
|
||||
|
||||
my $is_combined = (@hash_parents > 1);
|
||||
my $patch_idx = 0;
|
||||
my $patch_number = 0;
|
||||
my $patch_line;
|
||||
my $diffinfo;
|
||||
my $to_name;
|
||||
my (%from, %to);
|
||||
|
||||
print "<div class=\"patchset\">\n";
|
||||
@@ -3134,73 +3159,46 @@ sub git_patchset_body {
|
||||
|
||||
PATCH:
|
||||
while ($patch_line) {
|
||||
my @diff_header;
|
||||
my ($from_id, $to_id);
|
||||
|
||||
# git diff header
|
||||
#assert($patch_line =~ m/^diff /) if DEBUG;
|
||||
#assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
|
||||
$patch_number++;
|
||||
push @diff_header, $patch_line;
|
||||
|
||||
# extended diff header
|
||||
EXTENDED_HEADER:
|
||||
while ($patch_line = <$fd>) {
|
||||
chomp $patch_line;
|
||||
|
||||
last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
|
||||
|
||||
if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
|
||||
$from_id = $1;
|
||||
$to_id = $2;
|
||||
} elsif ($patch_line =~ m/^index ((?:[0-9a-fA-F]{40},)+[0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
|
||||
$from_id = [ split(',', $1) ];
|
||||
$to_id = $2;
|
||||
}
|
||||
|
||||
push @diff_header, $patch_line;
|
||||
# parse "git diff" header line
|
||||
if ($patch_line =~ m/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/) {
|
||||
# $1 is from_name, which we do not use
|
||||
$to_name = unquote($2);
|
||||
$to_name =~ s!^b/!!;
|
||||
} elsif ($patch_line =~ m/^diff --(cc|combined) ("?.*"?)$/) {
|
||||
# $1 is 'cc' or 'combined', which we do not use
|
||||
$to_name = unquote($2);
|
||||
} else {
|
||||
$to_name = undef;
|
||||
}
|
||||
my $last_patch_line = $patch_line;
|
||||
|
||||
# check if current patch belong to current raw line
|
||||
# and parse raw git-diff line if needed
|
||||
if (defined $diffinfo &&
|
||||
defined $from_id && defined $to_id &&
|
||||
from_ids_eq($diffinfo->{'from_id'}, $from_id) &&
|
||||
$diffinfo->{'to_id'} eq $to_id) {
|
||||
if (is_patch_split($diffinfo, { 'to_file' => $to_name })) {
|
||||
# this is continuation of a split patch
|
||||
print "<div class=\"patch cont\">\n";
|
||||
} else {
|
||||
# advance raw git-diff output if needed
|
||||
$patch_idx++ if defined $diffinfo;
|
||||
|
||||
# read and prepare patch information
|
||||
$diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
|
||||
|
||||
# compact combined diff output can have some patches skipped
|
||||
# find which patch (using pathname of result) we are at now
|
||||
my $to_name;
|
||||
if ($diff_header[0] =~ m!^diff --cc "?(.*)"?$!) {
|
||||
$to_name = $1;
|
||||
}
|
||||
|
||||
do {
|
||||
# read and prepare patch information
|
||||
if (ref($difftree->[$patch_idx]) eq "HASH") {
|
||||
# pre-parsed (or generated by hand)
|
||||
$diffinfo = $difftree->[$patch_idx];
|
||||
} else {
|
||||
$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
|
||||
}
|
||||
|
||||
# check if current raw line has no patch (it got simplified)
|
||||
if (defined $to_name && $to_name ne $diffinfo->{'to_file'}) {
|
||||
# find which patch (using pathname of result) we are at now;
|
||||
if ($is_combined) {
|
||||
while ($to_name ne $diffinfo->{'to_file'}) {
|
||||
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
|
||||
format_diff_cc_simplified($diffinfo, @hash_parents) .
|
||||
"</div>\n"; # class="patch"
|
||||
|
||||
$patch_idx++;
|
||||
$patch_number++;
|
||||
|
||||
last if $patch_idx > $#$difftree;
|
||||
$diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
|
||||
}
|
||||
} until (!defined $to_name || $to_name eq $diffinfo->{'to_file'} ||
|
||||
$patch_idx > $#$difftree);
|
||||
}
|
||||
|
||||
# modifies %from, %to hashes
|
||||
parse_from_to_diffinfo($diffinfo, \%from, \%to, @hash_parents);
|
||||
@@ -3210,30 +3208,36 @@ sub git_patchset_body {
|
||||
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
|
||||
}
|
||||
|
||||
# git diff header
|
||||
#assert($patch_line =~ m/^diff /) if DEBUG;
|
||||
#assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
|
||||
$patch_number++;
|
||||
# print "git diff" header
|
||||
$patch_line = shift @diff_header;
|
||||
print format_git_diff_header_line($patch_line, $diffinfo,
|
||||
\%from, \%to);
|
||||
|
||||
# print extended diff header
|
||||
print "<div class=\"diff extended_header\">\n" if (@diff_header > 0);
|
||||
print "<div class=\"diff extended_header\">\n";
|
||||
EXTENDED_HEADER:
|
||||
foreach $patch_line (@diff_header) {
|
||||
while ($patch_line = <$fd>) {
|
||||
chomp $patch_line;
|
||||
|
||||
last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
|
||||
|
||||
print format_extended_diff_header_line($patch_line, $diffinfo,
|
||||
\%from, \%to);
|
||||
}
|
||||
print "</div>\n" if (@diff_header > 0); # class="diff extended_header"
|
||||
print "</div>\n"; # class="diff extended_header"
|
||||
|
||||
# from-file/to-file diff header
|
||||
$patch_line = $last_patch_line;
|
||||
if (! $patch_line) {
|
||||
print "</div>\n"; # class="patch"
|
||||
last PATCH;
|
||||
}
|
||||
next PATCH if ($patch_line =~ m/^diff /);
|
||||
#assert($patch_line =~ m/^---/) if DEBUG;
|
||||
#assert($patch_line eq $last_patch_line) if DEBUG;
|
||||
|
||||
my $last_patch_line = $patch_line;
|
||||
$patch_line = <$fd>;
|
||||
chomp $patch_line;
|
||||
#assert($patch_line =~ m/^\+\+\+/) if DEBUG;
|
||||
@@ -3258,16 +3262,11 @@ sub git_patchset_body {
|
||||
|
||||
# for compact combined (--cc) format, with chunk and patch simpliciaction
|
||||
# patchset might be empty, but there might be unprocessed raw lines
|
||||
for ($patch_idx++ if $patch_number > 0;
|
||||
for (++$patch_idx if $patch_number > 0;
|
||||
$patch_idx < @$difftree;
|
||||
$patch_idx++) {
|
||||
++$patch_idx) {
|
||||
# read and prepare patch information
|
||||
if (ref($difftree->[$patch_idx]) eq "HASH") {
|
||||
# pre-parsed (or generated by hand)
|
||||
$diffinfo = $difftree->[$patch_idx];
|
||||
} else {
|
||||
$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
|
||||
}
|
||||
$diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
|
||||
|
||||
# generate anchor for "patch" links in difftree / whatchanged part
|
||||
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
|
||||
@@ -3395,7 +3394,7 @@ sub git_project_list_body {
|
||||
"<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
|
||||
-class => "list", -title => $pr->{'descr_long'}},
|
||||
esc_html($pr->{'descr'})) . "</td>\n" .
|
||||
"<td><i>" . esc_html(chop_str($pr->{'owner'}, 15)) . "</i></td>\n";
|
||||
"<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
|
||||
print "<td class=\"". age_class($pr->{'age'}) . "\">" .
|
||||
(defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
|
||||
"<td class=\"link\">" .
|
||||
@@ -3437,9 +3436,10 @@ sub git_shortlog_body {
|
||||
print "<tr class=\"light\">\n";
|
||||
}
|
||||
$alternate ^= 1;
|
||||
my $author = chop_and_escape_str($co{'author_name'}, 10);
|
||||
# git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
|
||||
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
|
||||
"<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
|
||||
"<td><i>" . $author . "</i></td>\n" .
|
||||
"<td>";
|
||||
print format_subject_html($co{'title'}, $co{'title_short'},
|
||||
href(action=>"commit", hash=>$commit), $ref);
|
||||
@@ -3487,9 +3487,10 @@ sub git_history_body {
|
||||
print "<tr class=\"light\">\n";
|
||||
}
|
||||
$alternate ^= 1;
|
||||
# shortlog uses chop_str($co{'author_name'}, 10)
|
||||
my $author = chop_and_escape_str($co{'author_name'}, 15, 3);
|
||||
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
|
||||
# shortlog uses chop_str($co{'author_name'}, 10)
|
||||
"<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "</i></td>\n" .
|
||||
"<td><i>" . $author . "</i></td>\n" .
|
||||
"<td>";
|
||||
# originally git_history used chop_str($co{'title'}, 50)
|
||||
print format_subject_html($co{'title'}, $co{'title_short'},
|
||||
@@ -3643,11 +3644,12 @@ sub git_search_grep_body {
|
||||
print "<tr class=\"light\">\n";
|
||||
}
|
||||
$alternate ^= 1;
|
||||
my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
|
||||
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
|
||||
"<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
|
||||
"<td><i>" . $author . "</i></td>\n" .
|
||||
"<td>" .
|
||||
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"},
|
||||
esc_html(chop_str($co{'title'}, 50)) . "<br/>");
|
||||
chop_and_escape_str($co{'title'}, 50) . "<br/>");
|
||||
my $comment = $co{'comment'};
|
||||
foreach my $line (@$comment) {
|
||||
if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
|
||||
@@ -5157,12 +5159,13 @@ sub git_search {
|
||||
print "<tr class=\"light\">\n";
|
||||
}
|
||||
$alternate ^= 1;
|
||||
my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
|
||||
print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
|
||||
"<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
|
||||
"<td><i>" . $author . "</i></td>\n" .
|
||||
"<td>" .
|
||||
$cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
|
||||
-class => "list subject"},
|
||||
esc_html(chop_str($co{'title'}, 50)) . "<br/>");
|
||||
chop_and_escape_str($co{'title'}, 50) . "<br/>");
|
||||
while (my $setref = shift @files) {
|
||||
my %set = %$setref;
|
||||
print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
|
||||
|
||||
110
hash.c
Normal file
110
hash.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Some generic hashing helpers.
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "hash.h"
|
||||
|
||||
/*
|
||||
* Look up a hash entry in the hash table. Return the pointer to
|
||||
* the existing entry, or the empty slot if none existed. The caller
|
||||
* can then look at the (*ptr) to see whether it existed or not.
|
||||
*/
|
||||
static struct hash_table_entry *lookup_hash_entry(unsigned int hash, struct hash_table *table)
|
||||
{
|
||||
unsigned int size = table->size, nr = hash % size;
|
||||
struct hash_table_entry *array = table->array;
|
||||
|
||||
while (array[nr].ptr) {
|
||||
if (array[nr].hash == hash)
|
||||
break;
|
||||
nr++;
|
||||
if (nr >= size)
|
||||
nr = 0;
|
||||
}
|
||||
return array + nr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Insert a new hash entry pointer into the table.
|
||||
*
|
||||
* If that hash entry already existed, return the pointer to
|
||||
* the existing entry (and the caller can create a list of the
|
||||
* pointers or do anything else). If it didn't exist, return
|
||||
* NULL (and the caller knows the pointer has been inserted).
|
||||
*/
|
||||
static void **insert_hash_entry(unsigned int hash, void *ptr, struct hash_table *table)
|
||||
{
|
||||
struct hash_table_entry *entry = lookup_hash_entry(hash, table);
|
||||
|
||||
if (!entry->ptr) {
|
||||
entry->ptr = ptr;
|
||||
entry->hash = hash;
|
||||
table->nr++;
|
||||
return NULL;
|
||||
}
|
||||
return &entry->ptr;
|
||||
}
|
||||
|
||||
static void grow_hash_table(struct hash_table *table)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int old_size = table->size, new_size;
|
||||
struct hash_table_entry *old_array = table->array, *new_array;
|
||||
|
||||
new_size = alloc_nr(old_size);
|
||||
new_array = xcalloc(sizeof(struct hash_table_entry), new_size);
|
||||
table->size = new_size;
|
||||
table->array = new_array;
|
||||
table->nr = 0;
|
||||
for (i = 0; i < old_size; i++) {
|
||||
unsigned int hash = old_array[i].hash;
|
||||
void *ptr = old_array[i].ptr;
|
||||
if (ptr)
|
||||
insert_hash_entry(hash, ptr, table);
|
||||
}
|
||||
free(old_array);
|
||||
}
|
||||
|
||||
void *lookup_hash(unsigned int hash, struct hash_table *table)
|
||||
{
|
||||
if (!table->array)
|
||||
return NULL;
|
||||
return &lookup_hash_entry(hash, table)->ptr;
|
||||
}
|
||||
|
||||
void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table)
|
||||
{
|
||||
unsigned int nr = table->nr;
|
||||
if (nr >= table->size/2)
|
||||
grow_hash_table(table);
|
||||
return insert_hash_entry(hash, ptr, table);
|
||||
}
|
||||
|
||||
int for_each_hash(struct hash_table *table, int (*fn)(void *))
|
||||
{
|
||||
int sum = 0;
|
||||
unsigned int i;
|
||||
unsigned int size = table->size;
|
||||
struct hash_table_entry *array = table->array;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
void *ptr = array->ptr;
|
||||
array++;
|
||||
if (ptr) {
|
||||
int val = fn(ptr);
|
||||
if (val < 0)
|
||||
return val;
|
||||
sum += val;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
void free_hash(struct hash_table *table)
|
||||
{
|
||||
free(table->array);
|
||||
table->array = NULL;
|
||||
table->size = 0;
|
||||
table->nr = 0;
|
||||
}
|
||||
43
hash.h
Normal file
43
hash.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef HASH_H
|
||||
#define HASH_H
|
||||
|
||||
/*
|
||||
* These are some simple generic hash table helper functions.
|
||||
* Not necessarily suitable for all users, but good for things
|
||||
* where you want to just keep track of a list of things, and
|
||||
* have a good hash to use on them.
|
||||
*
|
||||
* It keeps the hash table at roughly 50-75% free, so the memory
|
||||
* cost of the hash table itself is roughly
|
||||
*
|
||||
* 3 * 2*sizeof(void *) * nr_of_objects
|
||||
*
|
||||
* bytes.
|
||||
*
|
||||
* FIXME: on 64-bit architectures, we waste memory. It would be
|
||||
* good to have just 32-bit pointers, requiring a special allocator
|
||||
* for hashed entries or something.
|
||||
*/
|
||||
struct hash_table_entry {
|
||||
unsigned int hash;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
struct hash_table {
|
||||
unsigned int size, nr;
|
||||
struct hash_table_entry *array;
|
||||
};
|
||||
|
||||
extern void *lookup_hash(unsigned int hash, struct hash_table *table);
|
||||
extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table);
|
||||
extern int for_each_hash(struct hash_table *table, int (*fn)(void *));
|
||||
extern void free_hash(struct hash_table *table);
|
||||
|
||||
static inline void init_hash(struct hash_table *table)
|
||||
{
|
||||
table->size = 0;
|
||||
table->nr = 0;
|
||||
table->array = NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
186
help.c
186
help.c
@@ -37,24 +37,25 @@ static inline void mput_char(char c, unsigned int num)
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
static struct cmdname {
|
||||
size_t len;
|
||||
char name[1];
|
||||
} **cmdname;
|
||||
static int cmdname_alloc, cmdname_cnt;
|
||||
static struct cmdnames {
|
||||
int alloc;
|
||||
int cnt;
|
||||
struct cmdname {
|
||||
size_t len;
|
||||
char name[1];
|
||||
} **names;
|
||||
} main_cmds, other_cmds;
|
||||
|
||||
static void add_cmdname(const char *name, int len)
|
||||
static void add_cmdname(struct cmdnames *cmds, const char *name, int len)
|
||||
{
|
||||
struct cmdname *ent;
|
||||
if (cmdname_alloc <= cmdname_cnt) {
|
||||
cmdname_alloc = cmdname_alloc + 200;
|
||||
cmdname = xrealloc(cmdname, cmdname_alloc * sizeof(*cmdname));
|
||||
}
|
||||
ent = xmalloc(sizeof(*ent) + len);
|
||||
struct cmdname *ent = xmalloc(sizeof(*ent) + len);
|
||||
|
||||
ent->len = len;
|
||||
memcpy(ent->name, name, len);
|
||||
ent->name[len] = 0;
|
||||
cmdname[cmdname_cnt++] = ent;
|
||||
|
||||
ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
|
||||
cmds->names[cmds->cnt++] = ent;
|
||||
}
|
||||
|
||||
static int cmdname_compare(const void *a_, const void *b_)
|
||||
@@ -64,7 +65,42 @@ static int cmdname_compare(const void *a_, const void *b_)
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
static void pretty_print_string_list(struct cmdname **cmdname, int longest)
|
||||
static void uniq(struct cmdnames *cmds)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (!cmds->cnt)
|
||||
return;
|
||||
|
||||
for (i = j = 1; i < cmds->cnt; i++)
|
||||
if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
|
||||
cmds->names[j++] = cmds->names[i];
|
||||
|
||||
cmds->cnt = j;
|
||||
}
|
||||
|
||||
static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) {
|
||||
int ci, cj, ei;
|
||||
int cmp;
|
||||
|
||||
ci = cj = ei = 0;
|
||||
while (ci < cmds->cnt && ei < excludes->cnt) {
|
||||
cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
|
||||
if (cmp < 0)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
else if (cmp == 0)
|
||||
ci++, ei++;
|
||||
else if (cmp > 0)
|
||||
ei++;
|
||||
}
|
||||
|
||||
while (ci < cmds->cnt)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
|
||||
cmds->cnt = cj;
|
||||
}
|
||||
|
||||
static void pretty_print_string_list(struct cmdnames *cmds, int longest)
|
||||
{
|
||||
int cols = 1, rows;
|
||||
int space = longest + 1; /* min 1 SP between words */
|
||||
@@ -73,9 +109,7 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest)
|
||||
|
||||
if (space < max_cols)
|
||||
cols = max_cols / space;
|
||||
rows = (cmdname_cnt + cols - 1) / cols;
|
||||
|
||||
qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
|
||||
rows = (cmds->cnt + cols - 1) / cols;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
printf(" ");
|
||||
@@ -83,71 +117,112 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest)
|
||||
for (j = 0; j < cols; j++) {
|
||||
int n = j * rows + i;
|
||||
int size = space;
|
||||
if (n >= cmdname_cnt)
|
||||
if (n >= cmds->cnt)
|
||||
break;
|
||||
if (j == cols-1 || n + rows >= cmdname_cnt)
|
||||
if (j == cols-1 || n + rows >= cmds->cnt)
|
||||
size = 1;
|
||||
printf("%-*s", size, cmdname[n]->name);
|
||||
printf("%-*s", size, cmds->names[n]->name);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
static void list_commands(const char *exec_path, const char *pattern)
|
||||
static unsigned int list_commands_in_dir(struct cmdnames *cmds,
|
||||
const char *path)
|
||||
{
|
||||
unsigned int longest = 0;
|
||||
char path[PATH_MAX];
|
||||
int dirlen;
|
||||
DIR *dir = opendir(exec_path);
|
||||
const char *prefix = "git-";
|
||||
int prefix_len = strlen(prefix);
|
||||
DIR *dir = opendir(path);
|
||||
struct dirent *de;
|
||||
|
||||
if (!dir) {
|
||||
fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dirlen = strlen(exec_path);
|
||||
if (PATH_MAX - 20 < dirlen) {
|
||||
fprintf(stderr, "git: insanely long exec-path '%s'\n",
|
||||
exec_path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(path, exec_path, dirlen);
|
||||
path[dirlen++] = '/';
|
||||
if (!dir || chdir(path))
|
||||
return 0;
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
struct stat st;
|
||||
int entlen;
|
||||
|
||||
if (prefixcmp(de->d_name, "git-"))
|
||||
if (prefixcmp(de->d_name, prefix))
|
||||
continue;
|
||||
strcpy(path+dirlen, de->d_name);
|
||||
if (stat(path, &st) || /* stat, not lstat */
|
||||
|
||||
if (stat(de->d_name, &st) || /* stat, not lstat */
|
||||
!S_ISREG(st.st_mode) ||
|
||||
!(st.st_mode & S_IXUSR))
|
||||
continue;
|
||||
|
||||
entlen = strlen(de->d_name);
|
||||
entlen = strlen(de->d_name) - prefix_len;
|
||||
if (has_extension(de->d_name, ".exe"))
|
||||
entlen -= 4;
|
||||
|
||||
if (longest < entlen)
|
||||
longest = entlen;
|
||||
|
||||
add_cmdname(de->d_name + 4, entlen-4);
|
||||
add_cmdname(cmds, de->d_name + prefix_len, entlen);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
printf("git commands available in '%s'\n", exec_path);
|
||||
printf("----------------------------");
|
||||
mput_char('-', strlen(exec_path));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(cmdname, longest - 4);
|
||||
putchar('\n');
|
||||
return longest;
|
||||
}
|
||||
|
||||
static void list_common_cmds_help(void)
|
||||
static void list_commands(void)
|
||||
{
|
||||
unsigned int longest = 0;
|
||||
unsigned int len;
|
||||
const char *env_path = getenv("PATH");
|
||||
char *paths, *path, *colon;
|
||||
const char *exec_path = git_exec_path();
|
||||
|
||||
if (exec_path)
|
||||
longest = list_commands_in_dir(&main_cmds, exec_path);
|
||||
|
||||
if (!env_path) {
|
||||
fprintf(stderr, "PATH not set\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
path = paths = xstrdup(env_path);
|
||||
while (1) {
|
||||
if ((colon = strchr(path, ':')))
|
||||
*colon = 0;
|
||||
|
||||
len = list_commands_in_dir(&other_cmds, path);
|
||||
if (len > longest)
|
||||
longest = len;
|
||||
|
||||
if (!colon)
|
||||
break;
|
||||
path = colon + 1;
|
||||
}
|
||||
free(paths);
|
||||
|
||||
qsort(main_cmds.names, main_cmds.cnt,
|
||||
sizeof(*main_cmds.names), cmdname_compare);
|
||||
uniq(&main_cmds);
|
||||
|
||||
qsort(other_cmds.names, other_cmds.cnt,
|
||||
sizeof(*other_cmds.names), cmdname_compare);
|
||||
uniq(&other_cmds);
|
||||
exclude_cmds(&other_cmds, &main_cmds);
|
||||
|
||||
if (main_cmds.cnt) {
|
||||
printf("available git commands in '%s'\n", exec_path);
|
||||
printf("----------------------------");
|
||||
mput_char('-', strlen(exec_path));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(&main_cmds, longest);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
if (other_cmds.cnt) {
|
||||
printf("git commands available from elsewhere on your $PATH\n");
|
||||
printf("---------------------------------------------------\n");
|
||||
pretty_print_string_list(&other_cmds, longest);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void list_common_cmds_help(void)
|
||||
{
|
||||
int i, longest = 0;
|
||||
|
||||
@@ -185,8 +260,7 @@ static void show_man_page(const char *git_cmd)
|
||||
|
||||
void help_unknown_cmd(const char *cmd)
|
||||
{
|
||||
printf("git: '%s' is not a git-command\n\n", cmd);
|
||||
list_common_cmds_help();
|
||||
fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -199,19 +273,17 @@ int cmd_version(int argc, const char **argv, const char *prefix)
|
||||
int cmd_help(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
const char *help_cmd = argc > 1 ? argv[1] : NULL;
|
||||
const char *exec_path = git_exec_path();
|
||||
|
||||
if (!help_cmd) {
|
||||
printf("usage: %s\n\n", git_usage_string);
|
||||
list_common_cmds_help();
|
||||
exit(1);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
|
||||
printf("usage: %s\n\n", git_usage_string);
|
||||
if(exec_path)
|
||||
list_commands(exec_path, "git-*");
|
||||
exit(1);
|
||||
list_commands();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
@@ -15,7 +15,7 @@ static void show_parents(struct commit *commit, int abbrev)
|
||||
}
|
||||
}
|
||||
|
||||
static void show_decorations(struct commit *commit)
|
||||
void show_decorations(struct commit *commit)
|
||||
{
|
||||
const char *prefix;
|
||||
struct name_decoration *decoration;
|
||||
|
||||
@@ -12,5 +12,6 @@ int log_tree_diff_flush(struct rev_info *);
|
||||
int log_tree_commit(struct rev_info *, struct commit *);
|
||||
int log_tree_opt_parse(struct rev_info *, const char **, int);
|
||||
void show_log(struct rev_info *opt, const char *sep);
|
||||
void show_decorations(struct commit *commit);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1572,7 +1572,7 @@ static int merge(struct commit *h1,
|
||||
{
|
||||
struct commit_list *iter;
|
||||
struct commit *merged_common_ancestors;
|
||||
struct tree *mrtree;
|
||||
struct tree *mrtree = mrtree;
|
||||
int clean;
|
||||
|
||||
if (show(4)) {
|
||||
|
||||
20
remote.c
20
remote.c
@@ -857,7 +857,7 @@ struct ref *get_remote_ref(struct ref *remote_refs, const char *name)
|
||||
struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
|
||||
|
||||
if (!ref)
|
||||
die("Couldn't find remote ref %s\n", name);
|
||||
return NULL;
|
||||
|
||||
return copy_ref(ref);
|
||||
}
|
||||
@@ -889,20 +889,24 @@ static struct ref *get_local_ref(const char *name)
|
||||
|
||||
int get_fetch_map(struct ref *remote_refs,
|
||||
const struct refspec *refspec,
|
||||
struct ref ***tail)
|
||||
struct ref ***tail,
|
||||
int missing_ok)
|
||||
{
|
||||
struct ref *ref_map, *rm;
|
||||
|
||||
if (refspec->pattern) {
|
||||
ref_map = get_expanded_map(remote_refs, refspec);
|
||||
} else {
|
||||
ref_map = get_remote_ref(remote_refs,
|
||||
refspec->src[0] ?
|
||||
refspec->src : "HEAD");
|
||||
const char *name = refspec->src[0] ? refspec->src : "HEAD";
|
||||
|
||||
ref_map->peer_ref = get_local_ref(refspec->dst);
|
||||
if (ref_map->peer_ref && refspec->force)
|
||||
ref_map->peer_ref->force = 1;
|
||||
ref_map = get_remote_ref(remote_refs, name);
|
||||
if (!missing_ok && !ref_map)
|
||||
die("Couldn't find remote ref %s", name);
|
||||
if (ref_map) {
|
||||
ref_map->peer_ref = get_local_ref(refspec->dst);
|
||||
if (ref_map->peer_ref && refspec->force)
|
||||
ref_map->peer_ref->force = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (rm = ref_map; rm; rm = rm->next) {
|
||||
|
||||
5
remote.h
5
remote.h
@@ -67,9 +67,12 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
* *tail is the pointer to the tail pointer of the list of results
|
||||
* beforehand, and will be set to the tail pointer of the list of
|
||||
* results afterward.
|
||||
*
|
||||
* missing_ok is usually false, but when we are adding branch.$name.merge
|
||||
* it is Ok if the branch is not at the remote anymore.
|
||||
*/
|
||||
int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec,
|
||||
struct ref ***tail);
|
||||
struct ref ***tail, int missing_ok);
|
||||
|
||||
struct ref *get_remote_ref(struct ref *remote_refs, const char *name);
|
||||
|
||||
|
||||
50
send-pack.c
50
send-pack.c
@@ -178,6 +178,35 @@ static int receive_status(int in)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_tracking_ref(struct remote *remote, struct ref *ref)
|
||||
{
|
||||
struct refspec rs;
|
||||
int will_delete_ref;
|
||||
|
||||
rs.src = ref->name;
|
||||
rs.dst = NULL;
|
||||
|
||||
if (!ref->peer_ref)
|
||||
return;
|
||||
|
||||
will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
|
||||
|
||||
if (!will_delete_ref &&
|
||||
!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1))
|
||||
return;
|
||||
|
||||
if (!remote_find_tracking(remote, &rs)) {
|
||||
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
|
||||
if (is_null_sha1(ref->peer_ref->new_sha1)) {
|
||||
if (delete_ref(rs.dst, NULL))
|
||||
error("Failed to delete");
|
||||
} else
|
||||
update_ref("update by push", rs.dst,
|
||||
ref->new_sha1, NULL, 0, 0);
|
||||
free(rs.dst);
|
||||
}
|
||||
}
|
||||
|
||||
static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec)
|
||||
{
|
||||
struct ref *ref;
|
||||
@@ -306,22 +335,6 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
|
||||
fprintf(stderr, "\n from %s\n to %s\n",
|
||||
old_hex, new_hex);
|
||||
}
|
||||
if (remote && !dry_run) {
|
||||
struct refspec rs;
|
||||
rs.src = ref->name;
|
||||
rs.dst = NULL;
|
||||
if (!remote_find_tracking(remote, &rs)) {
|
||||
fprintf(stderr, " Also local %s\n", rs.dst);
|
||||
if (will_delete_ref) {
|
||||
if (delete_ref(rs.dst, NULL)) {
|
||||
error("Failed to delete");
|
||||
}
|
||||
} else
|
||||
update_ref("update by push", rs.dst,
|
||||
ref->new_sha1, NULL, 0, 0);
|
||||
free(rs.dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packet_flush(out);
|
||||
@@ -334,6 +347,11 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
|
||||
ret = -4;
|
||||
}
|
||||
|
||||
if (!dry_run && remote && ret == 0) {
|
||||
for (ref = remote_refs; ref; ref = ref->next)
|
||||
update_tracking_ref(remote, ref);
|
||||
}
|
||||
|
||||
if (!new_refs && ret == 0)
|
||||
fprintf(stderr, "Everything up-to-date\n");
|
||||
return ret;
|
||||
|
||||
16
sha1_file.c
16
sha1_file.c
@@ -529,13 +529,15 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
||||
munmap(idx_map, idx_size);
|
||||
return error("wrong index v2 file size in %s", path);
|
||||
}
|
||||
if (idx_size != min_size) {
|
||||
/* make sure we can deal with large pack offsets */
|
||||
off_t x = 0x7fffffffUL, y = 0xffffffffUL;
|
||||
if (x > (x + 1) || y > (y + 1)) {
|
||||
munmap(idx_map, idx_size);
|
||||
return error("pack too large for current definition of off_t in %s", path);
|
||||
}
|
||||
if (idx_size != min_size &&
|
||||
/*
|
||||
* make sure we can deal with large pack offsets.
|
||||
* 31-bit signed offset won't be enough, neither
|
||||
* 32-bit unsigned one will be.
|
||||
*/
|
||||
(sizeof(off_t) <= 4)) {
|
||||
munmap(idx_map, idx_size);
|
||||
return error("pack too large for current definition of off_t in %s", path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
shell.c
8
shell.c
@@ -24,17 +24,11 @@ static int do_cvs_cmd(const char *me, char *arg)
|
||||
const char *cvsserver_argv[3] = {
|
||||
"cvsserver", "server", NULL
|
||||
};
|
||||
const char *oldpath = getenv("PATH");
|
||||
struct strbuf newpath = STRBUF_INIT;
|
||||
|
||||
if (!arg || strcmp(arg, "server"))
|
||||
die("git-cvsserver only handles server: %s", arg);
|
||||
|
||||
strbuf_addstr(&newpath, git_exec_path());
|
||||
strbuf_addch(&newpath, ':');
|
||||
strbuf_addstr(&newpath, oldpath);
|
||||
|
||||
setenv("PATH", strbuf_detach(&newpath, NULL), 1);
|
||||
setup_path(NULL);
|
||||
|
||||
return execv_git_cmd(cvsserver_argv);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ int main(int argc, char **argv)
|
||||
ntohl(off64[1]);
|
||||
off64_nr++;
|
||||
}
|
||||
printf("%llu %s (%08x)\n", (unsigned long long) offset,
|
||||
printf("%" PRIuMAX " %s (%08x)\n", (uintmax_t) offset,
|
||||
sha1_to_hex(entries[i].sha1),
|
||||
ntohl(entries[i].crc));
|
||||
}
|
||||
|
||||
@@ -254,4 +254,32 @@ test_expect_success 'push with dry-run' '
|
||||
check_push_result $old_commit heads/master
|
||||
'
|
||||
|
||||
test_expect_success 'push updates local refs' '
|
||||
|
||||
rm -rf parent child &&
|
||||
mkdir parent && cd parent && git init &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
cd .. &&
|
||||
git clone parent child && cd child &&
|
||||
echo two >foo && git commit -a -m two &&
|
||||
git push &&
|
||||
test $(git rev-parse master) = $(git rev-parse remotes/origin/master)
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'push does not update local refs on failure' '
|
||||
|
||||
rm -rf parent child &&
|
||||
mkdir parent && cd parent && git init &&
|
||||
echo one >foo && git add foo && git commit -m one &&
|
||||
echo exit 1 >.git/hooks/pre-receive &&
|
||||
chmod +x .git/hooks/pre-receive &&
|
||||
cd .. &&
|
||||
git clone parent child && cd child &&
|
||||
echo two >foo && git commit -a -m two || exit 1
|
||||
git push && exit 1
|
||||
test $(git rev-parse master) != $(git rev-parse remotes/origin/master)
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -71,6 +71,63 @@ test_expect_success 'bisect start with one bad and good' '
|
||||
git bisect next
|
||||
'
|
||||
|
||||
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
|
||||
# but $HASH2 is bad,
|
||||
# so we should find $HASH2 as the first bad commit
|
||||
test_expect_success 'bisect skip: successfull result' '
|
||||
git bisect reset &&
|
||||
git bisect start $HASH4 $HASH1 &&
|
||||
git bisect skip &&
|
||||
git bisect bad > my_bisect_log.txt &&
|
||||
grep "$HASH2 is first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
# $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2
|
||||
# so we should not be able to tell the first bad commit
|
||||
# among $HASH2, $HASH3 and $HASH4
|
||||
test_expect_success 'bisect skip: cannot tell between 3 commits' '
|
||||
git bisect start $HASH4 $HASH1 &&
|
||||
git bisect skip || return 1
|
||||
|
||||
if git bisect skip > my_bisect_log.txt
|
||||
then
|
||||
echo Oops, should have failed.
|
||||
false
|
||||
else
|
||||
test $? -eq 2 &&
|
||||
grep "first bad commit could be any of" my_bisect_log.txt &&
|
||||
! grep $HASH1 my_bisect_log.txt &&
|
||||
grep $HASH2 my_bisect_log.txt &&
|
||||
grep $HASH3 my_bisect_log.txt &&
|
||||
grep $HASH4 my_bisect_log.txt &&
|
||||
git bisect reset
|
||||
fi
|
||||
'
|
||||
|
||||
# $HASH1 is good, $HASH4 is bad, we skip $HASH3
|
||||
# but $HASH2 is good,
|
||||
# so we should not be able to tell the first bad commit
|
||||
# among $HASH3 and $HASH4
|
||||
test_expect_success 'bisect skip: cannot tell between 2 commits' '
|
||||
git bisect start $HASH4 $HASH1 &&
|
||||
git bisect skip || return 1
|
||||
|
||||
if git bisect good > my_bisect_log.txt
|
||||
then
|
||||
echo Oops, should have failed.
|
||||
false
|
||||
else
|
||||
test $? -eq 2 &&
|
||||
grep "first bad commit could be any of" my_bisect_log.txt &&
|
||||
! grep $HASH1 my_bisect_log.txt &&
|
||||
! grep $HASH2 my_bisect_log.txt &&
|
||||
grep $HASH3 my_bisect_log.txt &&
|
||||
grep $HASH4 my_bisect_log.txt &&
|
||||
git bisect reset
|
||||
fi
|
||||
'
|
||||
|
||||
# We want to automatically find the commit that
|
||||
# introduced "Another" into hello.
|
||||
test_expect_success \
|
||||
@@ -99,6 +156,67 @@ test_expect_success \
|
||||
grep "$HASH4 is first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset'
|
||||
|
||||
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
|
||||
# but $HASH4 is good,
|
||||
# so we should find $HASH5 as the first bad commit
|
||||
HASH5=
|
||||
test_expect_success 'bisect skip: add line and then a new test' '
|
||||
add_line_into_file "5: Another new line." hello &&
|
||||
HASH5=$(git rev-parse --verify HEAD) &&
|
||||
git bisect start $HASH5 $HASH1 &&
|
||||
git bisect skip &&
|
||||
git bisect good > my_bisect_log.txt &&
|
||||
grep "$HASH5 is first bad commit" my_bisect_log.txt &&
|
||||
git bisect log > log_to_replay.txt
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
test_expect_success 'bisect skip and bisect replay' '
|
||||
git bisect replay log_to_replay.txt > my_bisect_log.txt &&
|
||||
grep "$HASH5 is first bad commit" my_bisect_log.txt &&
|
||||
git bisect reset
|
||||
'
|
||||
|
||||
HASH6=
|
||||
test_expect_success 'bisect run & skip: cannot tell between 2' '
|
||||
add_line_into_file "6: Yet a line." hello &&
|
||||
HASH6=$(git rev-parse --verify HEAD) &&
|
||||
echo "#"\!"/bin/sh" > test_script.sh &&
|
||||
echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
|
||||
echo "grep line hello > /dev/null" >> test_script.sh &&
|
||||
echo "test \$? -ne 0" >> test_script.sh &&
|
||||
chmod +x test_script.sh &&
|
||||
git bisect start $HASH6 $HASH1 &&
|
||||
if git bisect run ./test_script.sh > my_bisect_log.txt
|
||||
then
|
||||
echo Oops, should have failed.
|
||||
false
|
||||
else
|
||||
test $? -eq 2 &&
|
||||
grep "first bad commit could be any of" my_bisect_log.txt &&
|
||||
! grep $HASH3 my_bisect_log.txt &&
|
||||
! grep $HASH6 my_bisect_log.txt &&
|
||||
grep $HASH4 my_bisect_log.txt &&
|
||||
grep $HASH5 my_bisect_log.txt
|
||||
fi
|
||||
'
|
||||
|
||||
HASH7=
|
||||
test_expect_success 'bisect run & skip: find first bad' '
|
||||
git bisect reset &&
|
||||
add_line_into_file "7: Should be the last line." hello &&
|
||||
HASH7=$(git rev-parse --verify HEAD) &&
|
||||
echo "#"\!"/bin/sh" > test_script.sh &&
|
||||
echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
|
||||
echo "tail -1 hello | grep day > /dev/null && exit 125" >> test_script.sh &&
|
||||
echo "grep Yet hello > /dev/null" >> test_script.sh &&
|
||||
echo "test \$? -ne 0" >> test_script.sh &&
|
||||
chmod +x test_script.sh &&
|
||||
git bisect start $HASH7 $HASH1 &&
|
||||
git bisect run ./test_script.sh > my_bisect_log.txt &&
|
||||
grep "$HASH6 is first bad commit" my_bisect_log.txt
|
||||
'
|
||||
|
||||
#
|
||||
#
|
||||
test_done
|
||||
|
||||
@@ -4,6 +4,8 @@ test_description='GIT_EDITOR, core.editor, and stuff'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
OLD_TERM="$TERM"
|
||||
|
||||
for i in GIT_EDITOR core_editor EDITOR VISUAL vi
|
||||
do
|
||||
cat >e-$i.sh <<-EOF
|
||||
@@ -88,4 +90,6 @@ do
|
||||
'
|
||||
done
|
||||
|
||||
TERM="$OLD_TERM"
|
||||
|
||||
test_done
|
||||
|
||||
@@ -59,15 +59,12 @@ esac
|
||||
# '
|
||||
# . ./test-lib.sh
|
||||
|
||||
error () {
|
||||
echo "* error: $*"
|
||||
trap - exit
|
||||
exit 1
|
||||
}
|
||||
|
||||
say () {
|
||||
echo "* $*"
|
||||
}
|
||||
[ "x$TERM" != "xdumb" ] &&
|
||||
[ -t 1 ] &&
|
||||
tput bold >/dev/null 2>&1 &&
|
||||
tput setaf 1 >/dev/null 2>&1 &&
|
||||
tput sgr0 >/dev/null 2>&1 &&
|
||||
color=t
|
||||
|
||||
test "${test_description}" != "" ||
|
||||
error "Test script did not set test_description."
|
||||
@@ -84,6 +81,10 @@ do
|
||||
exit 0 ;;
|
||||
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
|
||||
verbose=t; shift ;;
|
||||
-q|--q|--qu|--qui|--quie|--quiet)
|
||||
quiet=t; shift ;;
|
||||
--no-color)
|
||||
color=; shift ;;
|
||||
--no-python)
|
||||
# noop now...
|
||||
shift ;;
|
||||
@@ -92,6 +93,37 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
if test -n "$color"; then
|
||||
say_color () {
|
||||
case "$1" in
|
||||
error) tput bold; tput setaf 1;; # bold red
|
||||
skip) tput bold; tput setaf 2;; # bold green
|
||||
pass) tput setaf 2;; # green
|
||||
info) tput setaf 3;; # brown
|
||||
*) test -n "$quiet" && return;;
|
||||
esac
|
||||
shift
|
||||
echo "* $*"
|
||||
tput sgr0
|
||||
}
|
||||
else
|
||||
say_color() {
|
||||
test -z "$1" && test -n "$quiet" && return
|
||||
shift
|
||||
echo "* $*"
|
||||
}
|
||||
fi
|
||||
|
||||
error () {
|
||||
say_color error "error: $*"
|
||||
trap - exit
|
||||
exit 1
|
||||
}
|
||||
|
||||
say () {
|
||||
say_color info "$*"
|
||||
}
|
||||
|
||||
exec 5>&1
|
||||
if test "$verbose" = "t"
|
||||
then
|
||||
@@ -122,13 +154,13 @@ test_tick () {
|
||||
|
||||
test_ok_ () {
|
||||
test_count=$(expr "$test_count" + 1)
|
||||
say " ok $test_count: $@"
|
||||
say_color "" " ok $test_count: $@"
|
||||
}
|
||||
|
||||
test_failure_ () {
|
||||
test_count=$(expr "$test_count" + 1)
|
||||
test_failure=$(expr "$test_failure" + 1);
|
||||
say "FAIL $test_count: $1"
|
||||
say_color error "FAIL $test_count: $1"
|
||||
shift
|
||||
echo "$@" | sed -e 's/^/ /'
|
||||
test "$immediate" = "" || { trap - exit; exit 1; }
|
||||
@@ -158,9 +190,9 @@ test_skip () {
|
||||
done
|
||||
case "$to_skip" in
|
||||
t)
|
||||
say >&3 "skipping test: $@"
|
||||
say_color skip >&3 "skipping test: $@"
|
||||
test_count=$(expr "$test_count" + 1)
|
||||
say "skip $test_count: $1"
|
||||
say_color skip "skip $test_count: $1"
|
||||
: true
|
||||
;;
|
||||
*)
|
||||
@@ -247,11 +279,11 @@ test_done () {
|
||||
# The Makefile provided will clean this test area so
|
||||
# we will leave things as they are.
|
||||
|
||||
say "passed all $test_count test(s)"
|
||||
say_color pass "passed all $test_count test(s)"
|
||||
exit 0 ;;
|
||||
|
||||
*)
|
||||
say "failed $test_failure among $test_count test(s)"
|
||||
say_color error "failed $test_failure among $test_count test(s)"
|
||||
exit 1 ;;
|
||||
|
||||
esac
|
||||
@@ -300,8 +332,8 @@ do
|
||||
done
|
||||
case "$to_skip" in
|
||||
t)
|
||||
say >&3 "skipping test $this_test altogether"
|
||||
say "skip all tests in $this_test"
|
||||
say_color skip >&3 "skipping test $this_test altogether"
|
||||
say_color skip "skip all tests in $this_test"
|
||||
test_done
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -58,7 +58,7 @@ perl -e '
|
||||
if (/\s$/) {
|
||||
bad_line("trailing whitespace", $_);
|
||||
}
|
||||
if (/^\s* /) {
|
||||
if (/^\s* \t/) {
|
||||
bad_line("indent SP followed by a TAB", $_);
|
||||
}
|
||||
if (/^(?:[<>=]){7}/) {
|
||||
|
||||
88
wt-status.c
88
wt-status.c
@@ -52,31 +52,34 @@ void wt_status_prepare(struct wt_status *s)
|
||||
head = resolve_ref("HEAD", sha1, 0, NULL);
|
||||
s->branch = head ? xstrdup(head) : NULL;
|
||||
s->reference = "HEAD";
|
||||
s->fp = stdout;
|
||||
s->index_file = get_index_file();
|
||||
}
|
||||
|
||||
static void wt_status_print_cached_header(const char *reference)
|
||||
static void wt_status_print_cached_header(struct wt_status *s)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER);
|
||||
color_printf_ln(c, "# Changes to be committed:");
|
||||
if (reference) {
|
||||
color_printf_ln(c, "# (use \"git reset %s <file>...\" to unstage)", reference);
|
||||
color_fprintf_ln(s->fp, c, "# Changes to be committed:");
|
||||
if (s->reference) {
|
||||
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
|
||||
} else {
|
||||
color_printf_ln(c, "# (use \"git rm --cached <file>...\" to unstage)");
|
||||
color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
|
||||
}
|
||||
color_printf_ln(c, "#");
|
||||
color_fprintf_ln(s->fp, c, "#");
|
||||
}
|
||||
|
||||
static void wt_status_print_header(const char *main, const char *sub)
|
||||
static void wt_status_print_header(struct wt_status *s,
|
||||
const char *main, const char *sub)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER);
|
||||
color_printf_ln(c, "# %s:", main);
|
||||
color_printf_ln(c, "# (%s)", sub);
|
||||
color_printf_ln(c, "#");
|
||||
color_fprintf_ln(s->fp, c, "# %s:", main);
|
||||
color_fprintf_ln(s->fp, c, "# (%s)", sub);
|
||||
color_fprintf_ln(s->fp, c, "#");
|
||||
}
|
||||
|
||||
static void wt_status_print_trailer(void)
|
||||
static void wt_status_print_trailer(struct wt_status *s)
|
||||
{
|
||||
color_printf_ln(color(WT_STATUS_HEADER), "#");
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
||||
}
|
||||
|
||||
static const char *quote_crlf(const char *in, char *buf, size_t sz)
|
||||
@@ -108,7 +111,8 @@ static const char *quote_crlf(const char *in, char *buf, size_t sz)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wt_status_print_filepair(int t, struct diff_filepair *p)
|
||||
static void wt_status_print_filepair(struct wt_status *s,
|
||||
int t, struct diff_filepair *p)
|
||||
{
|
||||
const char *c = color(t);
|
||||
const char *one, *two;
|
||||
@@ -117,36 +121,36 @@ static void wt_status_print_filepair(int t, struct diff_filepair *p)
|
||||
one = quote_crlf(p->one->path, onebuf, sizeof(onebuf));
|
||||
two = quote_crlf(p->two->path, twobuf, sizeof(twobuf));
|
||||
|
||||
color_printf(color(WT_STATUS_HEADER), "#\t");
|
||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_ADDED:
|
||||
color_printf(c, "new file: %s", one);
|
||||
color_fprintf(s->fp, c, "new file: %s", one);
|
||||
break;
|
||||
case DIFF_STATUS_COPIED:
|
||||
color_printf(c, "copied: %s -> %s", one, two);
|
||||
color_fprintf(s->fp, c, "copied: %s -> %s", one, two);
|
||||
break;
|
||||
case DIFF_STATUS_DELETED:
|
||||
color_printf(c, "deleted: %s", one);
|
||||
color_fprintf(s->fp, c, "deleted: %s", one);
|
||||
break;
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
color_printf(c, "modified: %s", one);
|
||||
color_fprintf(s->fp, c, "modified: %s", one);
|
||||
break;
|
||||
case DIFF_STATUS_RENAMED:
|
||||
color_printf(c, "renamed: %s -> %s", one, two);
|
||||
color_fprintf(s->fp, c, "renamed: %s -> %s", one, two);
|
||||
break;
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
color_printf(c, "typechange: %s", one);
|
||||
color_fprintf(s->fp, c, "typechange: %s", one);
|
||||
break;
|
||||
case DIFF_STATUS_UNKNOWN:
|
||||
color_printf(c, "unknown: %s", one);
|
||||
color_fprintf(s->fp, c, "unknown: %s", one);
|
||||
break;
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
color_printf(c, "unmerged: %s", one);
|
||||
color_fprintf(s->fp, c, "unmerged: %s", one);
|
||||
break;
|
||||
default:
|
||||
die("bug: unhandled diff status %c", p->status);
|
||||
}
|
||||
printf("\n");
|
||||
fprintf(s->fp, "\n");
|
||||
}
|
||||
|
||||
static void wt_status_print_updated_cb(struct diff_queue_struct *q,
|
||||
@@ -160,14 +164,14 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,
|
||||
if (q->queue[i]->status == 'U')
|
||||
continue;
|
||||
if (!shown_header) {
|
||||
wt_status_print_cached_header(s->reference);
|
||||
wt_status_print_cached_header(s);
|
||||
s->commitable = 1;
|
||||
shown_header = 1;
|
||||
}
|
||||
wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]);
|
||||
wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
|
||||
}
|
||||
if (shown_header)
|
||||
wt_status_print_trailer();
|
||||
wt_status_print_trailer(s);
|
||||
}
|
||||
|
||||
static void wt_status_print_changed_cb(struct diff_queue_struct *q,
|
||||
@@ -184,18 +188,18 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
|
||||
msg = use_add_rm_msg;
|
||||
break;
|
||||
}
|
||||
wt_status_print_header("Changed but not updated", msg);
|
||||
wt_status_print_header(s, "Changed but not updated", msg);
|
||||
}
|
||||
for (i = 0; i < q->nr; i++)
|
||||
wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
|
||||
wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
|
||||
if (q->nr)
|
||||
wt_status_print_trailer();
|
||||
wt_status_print_trailer(s);
|
||||
}
|
||||
|
||||
static void wt_read_cache(struct wt_status *s)
|
||||
{
|
||||
discard_cache();
|
||||
read_cache();
|
||||
read_cache_from(s->index_file);
|
||||
}
|
||||
|
||||
static void wt_status_print_initial(struct wt_status *s)
|
||||
@@ -206,16 +210,16 @@ static void wt_status_print_initial(struct wt_status *s)
|
||||
wt_read_cache(s);
|
||||
if (active_nr) {
|
||||
s->commitable = 1;
|
||||
wt_status_print_cached_header(NULL);
|
||||
wt_status_print_cached_header(s);
|
||||
}
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
color_printf(color(WT_STATUS_HEADER), "#\t");
|
||||
color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s",
|
||||
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)));
|
||||
}
|
||||
if (active_nr)
|
||||
wt_status_print_trailer();
|
||||
wt_status_print_trailer(s);
|
||||
}
|
||||
|
||||
static void wt_status_print_updated(struct wt_status *s)
|
||||
@@ -282,12 +286,12 @@ static void wt_status_print_untracked(struct wt_status *s)
|
||||
}
|
||||
if (!shown_header) {
|
||||
s->workdir_untracked = 1;
|
||||
wt_status_print_header("Untracked files",
|
||||
wt_status_print_header(s, "Untracked files",
|
||||
use_add_to_include_msg);
|
||||
shown_header = 1;
|
||||
}
|
||||
color_printf(color(WT_STATUS_HEADER), "#\t");
|
||||
color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s",
|
||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%.*s",
|
||||
ent->len, ent->name);
|
||||
}
|
||||
}
|
||||
@@ -317,14 +321,14 @@ void wt_status_print(struct wt_status *s)
|
||||
branch_name = "";
|
||||
on_what = "Not currently on any branch.";
|
||||
}
|
||||
color_printf_ln(color(WT_STATUS_HEADER),
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
|
||||
"# %s%s", on_what, branch_name);
|
||||
}
|
||||
|
||||
if (s->is_initial) {
|
||||
color_printf_ln(color(WT_STATUS_HEADER), "#");
|
||||
color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit");
|
||||
color_printf_ln(color(WT_STATUS_HEADER), "#");
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
|
||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
||||
wt_status_print_initial(s);
|
||||
}
|
||||
else {
|
||||
@@ -338,7 +342,7 @@ void wt_status_print(struct wt_status *s)
|
||||
wt_status_print_verbose(s);
|
||||
if (!s->commitable) {
|
||||
if (s->amend)
|
||||
printf("# No changes\n");
|
||||
fprintf(s->fp, "# No changes\n");
|
||||
else if (s->workdir_dirty)
|
||||
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
|
||||
else if (s->workdir_untracked)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef STATUS_H
|
||||
#define STATUS_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
enum color_wt_status {
|
||||
WT_STATUS_HEADER,
|
||||
WT_STATUS_UPDATED,
|
||||
@@ -19,6 +21,8 @@ struct wt_status {
|
||||
int commitable;
|
||||
int workdir_dirty;
|
||||
int workdir_untracked;
|
||||
const char *index_file;
|
||||
FILE *fp;
|
||||
};
|
||||
|
||||
int git_status_config(const char *var, const char *value);
|
||||
|
||||
Reference in New Issue
Block a user