Merge commit '74359821' into js/reflog-delete
* commit '74359821': (128 commits) tests: introduce test_must_fail Fix builtin checkout crashing when given an invalid path templates/Makefile: don't depend on local umask setting Correct name of diff_flush() in API documentation Start preparing for 1.5.4.4 format-patch: remove a leftover debugging message completion: support format-patch's --cover-letter option Eliminate confusing "won't bisect on seeked tree" failure builtin-reflog.c: don't install new reflog on write failure send-email: fix In-Reply-To regression git-svn: Don't prompt for client cert password everytime. git.el: Do not display empty directories. Fix 'git cvsexportcommit -w $cvsdir ...' when used with relative $GIT_DIR Add testcase for 'git cvsexportcommit -w $cvsdir ...' with relative $GIT_DIR Prompt to continue when editing during rebase --interactive Documentation/git svn log: add a note about timezones. git-p4: Support usage of perforce client spec git-p4: git-p4 submit cleanups. git-p4: Removed git-p4 submit --direct. git-p4: Clean up git-p4 submit's log message handling. ...
This commit is contained in:
commit
f830d45b9f
27
Documentation/RelNotes-1.5.4.3.txt
Normal file
27
Documentation/RelNotes-1.5.4.3.txt
Normal file
@ -0,0 +1,27 @@
|
||||
GIT v1.5.4.3 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.4.2
|
||||
--------------------
|
||||
|
||||
* RPM spec used to pull in everything with 'git'. This has been
|
||||
changed so that 'git' package contains just the core parts,
|
||||
and we now supply 'git-all' metapackage to slurp in everything.
|
||||
This should match end user's expectation better.
|
||||
|
||||
* When some refs failed to update, git-push reported "failure"
|
||||
which was unclear if some other refs were updated or all of
|
||||
them failed atomically (the answer is the former). Reworded
|
||||
the message to clarify this.
|
||||
|
||||
* "git clone" from a repository whose HEAD was misconfigured
|
||||
did not set up the remote properly. Now it tries to do
|
||||
better.
|
||||
|
||||
* Updated git-push documentation to clarify what "matching"
|
||||
means, in order to reduce user confusion.
|
||||
|
||||
* Updated git-add documentation to clarify "add -u" operates in
|
||||
the current subdirectory you are in, just like other commands.
|
||||
|
||||
* git-gui updates to work on OSX and Windows better.
|
26
Documentation/RelNotes-1.5.4.4.txt
Normal file
26
Documentation/RelNotes-1.5.4.4.txt
Normal file
@ -0,0 +1,26 @@
|
||||
GIT v1.5.4.4 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.5.4.3
|
||||
--------------------
|
||||
|
||||
* "git cvsexportcommit -w $cvsdir" misbehaved when GIT_DIR is set to a
|
||||
relative directory.
|
||||
|
||||
* "git http-push" had an invalid memory access that could lead it to
|
||||
segfault.
|
||||
|
||||
* When "git rebase -i" gave control back to the user for a commit that is
|
||||
marked to be edited, it just said "modify it with commit --amend",
|
||||
without saying what to do to continue after modifying it. Give an
|
||||
explicit instruction to run "rebase --continue" to be more helpful.
|
||||
|
||||
* "git send-email" in 1.5.4.3 issued a bogus empty In-Reply-To: header.
|
||||
|
||||
Also included are a handful documentation updates.
|
||||
|
||||
---
|
||||
exec >/var/tmp/1
|
||||
echo O=$(git describe maint)
|
||||
O=v1.5.4.3
|
||||
git shortlog --no-merges $O..maint
|
@ -353,6 +353,10 @@ core.whitespace::
|
||||
error (enabled by default).
|
||||
* `indent-with-non-tab` treats a line that is indented with 8 or more
|
||||
space characters as an error (not enabled by default).
|
||||
* `cr-at-eol` treats a carriage-return at the end of line as
|
||||
part of the line terminator, i.e. with it, `trailing-space`
|
||||
does not trigger if the character before such a carriage-return
|
||||
is not a whitespace (not enabled by default).
|
||||
|
||||
alias.*::
|
||||
Command aliases for the linkgit:git[1] command wrapper - e.g.
|
||||
@ -375,10 +379,14 @@ apply.whitespace::
|
||||
|
||||
branch.autosetupmerge::
|
||||
Tells `git-branch` and `git-checkout` to setup new branches
|
||||
so that linkgit:git-pull[1] will appropriately merge from that
|
||||
remote branch. Note that even if this option is not set,
|
||||
so that linkgit:git-pull[1] will appropriately merge from the
|
||||
starting point branch. Note that even if this option is not set,
|
||||
this behavior can be chosen per-branch using the `--track`
|
||||
and `--no-track` options. This option defaults to true.
|
||||
and `--no-track` options. The valid settings are: `false` -- no
|
||||
automatic setup is done; `true` -- automatic setup is done when the
|
||||
starting point is a remote branch; `always` -- automatic setup is
|
||||
done when the starting point is either a local branch or remote
|
||||
branch. This option defaults to true.
|
||||
|
||||
branch.<name>.remote::
|
||||
When in branch <name>, it tells `git fetch` which remote to fetch.
|
||||
@ -808,6 +816,8 @@ pack.threads::
|
||||
warning. This is meant to reduce packing time on multiprocessor
|
||||
machines. The required amount of memory for the delta search window
|
||||
is however multiplied by the number of threads.
|
||||
Specifying 0 will cause git to auto-detect the number of CPU's
|
||||
and set the number of threads accordingly.
|
||||
|
||||
pack.indexVersion::
|
||||
Specify the default pack index version. Valid values are 1 for
|
||||
@ -893,6 +903,17 @@ tar.umask::
|
||||
archiving user's umask will be used instead. See umask(2) and
|
||||
linkgit:git-archive[1].
|
||||
|
||||
url.<base>.insteadOf::
|
||||
Any URL that starts with this value will be rewritten to
|
||||
start, instead, with <base>. In cases where some site serves a
|
||||
large number of repositories, and serves them with multiple
|
||||
access methods, and some users need to use different access
|
||||
methods, this feature allows people to specify any of the
|
||||
equivalent URLs and have git automatically rewrite the URL to
|
||||
the best alternative for the particular user, even for a
|
||||
never-before-seen repository on the site. When more than one
|
||||
insteadOf strings match a given URL, the longest match is used.
|
||||
|
||||
user.email::
|
||||
Your email address to be recorded in any newly created commits.
|
||||
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
|
||||
|
@ -170,6 +170,14 @@ endif::git-format-patch[]
|
||||
Swap two inputs; that is, show differences from index or
|
||||
on-disk file to tree contents.
|
||||
|
||||
--relative[=<path>]::
|
||||
When run from a subdirectory of the project, it can be
|
||||
told to exclude changes outside the directory and show
|
||||
pathnames relative to it with this option. When you are
|
||||
not in a subdirectory (e.g. in a bare repository), you
|
||||
can name which subdirectory to make the output relative
|
||||
to by giving a <path> as an argument.
|
||||
|
||||
--text::
|
||||
Treat all files as text.
|
||||
|
||||
|
@ -138,7 +138,7 @@ aborts in the middle,. You can recover from this in one of two ways:
|
||||
|
||||
The command refuses to process new mailboxes while `.dotest`
|
||||
directory exists, so if you decide to start over from scratch,
|
||||
run `rm -f .dotest` before running the command with mailbox
|
||||
run `rm -f -r .dotest` before running the command with mailbox
|
||||
names.
|
||||
|
||||
|
||||
|
@ -35,11 +35,10 @@ working tree to it; use "git checkout <newbranch>" to switch to the
|
||||
new branch.
|
||||
|
||||
When a local branch is started off a remote branch, git sets up the
|
||||
branch so that linkgit:git-pull[1] will appropriately merge from that
|
||||
remote branch. If this behavior is not desired, it is possible to
|
||||
disable it using the global `branch.autosetupmerge` configuration
|
||||
flag. That setting can be overridden by using the `--track`
|
||||
and `--no-track` options.
|
||||
branch so that linkgit:git-pull[1] will appropriately merge from
|
||||
the remote branch. This behavior may be changed via the global
|
||||
`branch.autosetupmerge` configuration flag. That setting can be
|
||||
overridden by using the `--track` and `--no-track` options.
|
||||
|
||||
With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
|
||||
If <oldbranch> had a corresponding reflog, it is renamed to match
|
||||
@ -105,20 +104,19 @@ OPTIONS
|
||||
Display the full sha1s in output listing rather than abbreviating them.
|
||||
|
||||
--track::
|
||||
Set up configuration so that git-pull will automatically
|
||||
retrieve data from the remote branch. Use this if you always
|
||||
pull from the same remote branch into the new branch, or if you
|
||||
don't want to use "git pull <repository> <refspec>" explicitly.
|
||||
This behavior is the default. Set the
|
||||
branch.autosetupmerge configuration variable to false if you
|
||||
want git-checkout and git-branch to always behave as if
|
||||
'--no-track' were given.
|
||||
When creating a new branch, set up configuration so that git-pull
|
||||
will automatically retrieve data from the start point, which must be
|
||||
a branch. Use this if you always pull from the same upstream branch
|
||||
into the new branch, and if you don't want to use "git pull
|
||||
<repository> <refspec>" explicitly. This behavior is the default
|
||||
when the start point is a remote branch. Set the
|
||||
branch.autosetupmerge configuration variable to `false` if you want
|
||||
git-checkout and git-branch to always behave as if '--no-track' were
|
||||
given. Set it to `always` if you want this behavior when the
|
||||
start-point is either a local or remote branch.
|
||||
|
||||
--no-track::
|
||||
When a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will not retrieve data
|
||||
from the remote branch, ignoring the branch.autosetupmerge
|
||||
configuration variable.
|
||||
Ignore the branch.autosetupmerge configuration variable.
|
||||
|
||||
<branchname>::
|
||||
The name of the branch to create or delete.
|
||||
|
@ -99,36 +99,62 @@ Assume two repositories exist as R1 on machine A, and R2 on machine B.
|
||||
For whatever reason, direct connection between A and B is not allowed,
|
||||
but we can move data from A to B via some mechanism (CD, email, etc).
|
||||
We want to update R2 with developments made on branch master in R1.
|
||||
|
||||
To create the bundle you have to specify the basis. You have some options:
|
||||
|
||||
- Without basis.
|
||||
+
|
||||
This is useful when sending the whole history.
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master
|
||||
------------
|
||||
|
||||
- Using temporally tags.
|
||||
+
|
||||
We set a tag in R1 (lastR2bundle) after the previous such transport,
|
||||
and move it afterwards to help build the bundle.
|
||||
|
||||
in R1 on A:
|
||||
|
||||
------------
|
||||
$ git-bundle create mybundle master ^lastR2bundle
|
||||
$ git tag -f lastR2bundle master
|
||||
------------
|
||||
|
||||
(move mybundle from A to B by some mechanism)
|
||||
- Using a tag present in both repositories
|
||||
|
||||
in R2 on B:
|
||||
------------
|
||||
$ git bundle create mybundle master ^v1.0.0
|
||||
------------
|
||||
|
||||
- A basis based on time.
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master --since=10.days.ago
|
||||
------------
|
||||
|
||||
- With a limit on the number of commits
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master -n 10
|
||||
------------
|
||||
|
||||
Then you move mybundle from A to B, and in R2 on B:
|
||||
|
||||
------------
|
||||
$ git-bundle verify mybundle
|
||||
$ git-fetch mybundle refspec
|
||||
$ git-fetch mybundle master:localRef
|
||||
------------
|
||||
|
||||
where refspec is refInBundle:localRef
|
||||
|
||||
|
||||
Also, with something like this in your config:
|
||||
With something like this in the config in R2:
|
||||
|
||||
------------------------
|
||||
[remote "bundle"]
|
||||
url = /home/me/tmp/file.bdl
|
||||
fetch = refs/heads/*:refs/remotes/origin/*
|
||||
------------------------
|
||||
|
||||
You can first sneakernet the bundle file to ~/tmp/file.bdl and
|
||||
then these commands:
|
||||
then these commands on machine B:
|
||||
|
||||
------------
|
||||
$ git ls-remote bundle
|
||||
|
@ -48,21 +48,19 @@ OPTIONS
|
||||
may restrict the characters allowed in a branch name.
|
||||
|
||||
--track::
|
||||
When -b is given and a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will automatically
|
||||
retrieve data from the remote branch. Use this if you always
|
||||
pull from the same remote branch into the new branch, or if you
|
||||
don't want to use "git pull <repository> <refspec>" explicitly.
|
||||
This behavior is the default. Set the
|
||||
branch.autosetupmerge configuration variable to false if you
|
||||
want git-checkout and git-branch to always behave as if
|
||||
'--no-track' were given.
|
||||
When creating a new branch, set up configuration so that git-pull
|
||||
will automatically retrieve data from the start point, which must be
|
||||
a branch. Use this if you always pull from the same upstream branch
|
||||
into the new branch, and if you don't want to use "git pull
|
||||
<repository> <refspec>" explicitly. This behavior is the default
|
||||
when the start point is a remote branch. Set the
|
||||
branch.autosetupmerge configuration variable to `false` if you want
|
||||
git-checkout and git-branch to always behave as if '--no-track' were
|
||||
given. Set it to `always` if you want this behavior when the
|
||||
start-point is either a local or remote branch.
|
||||
|
||||
--no-track::
|
||||
When -b is given and a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will not retrieve data
|
||||
from the remote branch, ignoring the branch.autosetupmerge
|
||||
configuration variable.
|
||||
Ignore the branch.autosetupmerge configuration variable.
|
||||
|
||||
-l::
|
||||
Create the new branch's reflog. This activates recording of
|
||||
|
@ -45,6 +45,11 @@ OPTIONS
|
||||
candidates to describe the input committish consider
|
||||
up to <n> candidates. Increasing <n> above 10 will take
|
||||
slightly longer but may produce a more accurate result.
|
||||
An <n> of 0 will cause only exact matches to be output.
|
||||
|
||||
--exact-match::
|
||||
Only output exact matches (a tag directly references the
|
||||
supplied commit). This is a synonym for --candidates=0.
|
||||
|
||||
--debug::
|
||||
Verbosely display information about the searching strategy
|
||||
|
@ -56,7 +56,9 @@ notable exception of the commit filter, for technical reasons).
|
||||
Prior to that, the $GIT_COMMIT environment variable will be set to contain
|
||||
the id of the commit being rewritten. Also, GIT_AUTHOR_NAME,
|
||||
GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL,
|
||||
and GIT_COMMITTER_DATE are set according to the current commit.
|
||||
and GIT_COMMITTER_DATE are set according to the current commit. If any
|
||||
evaluation of <command> returns a non-zero exit status, the whole operation
|
||||
will be aborted.
|
||||
|
||||
A 'map' function is available that takes an "original sha1 id" argument
|
||||
and outputs a "rewritten sha1 id" if the commit has been already
|
||||
@ -197,7 +199,7 @@ happened). If this is not the case, use:
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
git filter-branch --parent-filter \
|
||||
'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' HEAD
|
||||
'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
or even simpler:
|
||||
@ -240,6 +242,15 @@ committed a merge between P1 and P2, it will be propagated properly
|
||||
and all children of the merge will become merge commits with P1,P2
|
||||
as their parents instead of the merge commit.
|
||||
|
||||
You can rewrite the commit log messages using `--message-filter`. For
|
||||
example, `git-svn-id` strings in a repository created by `git-svn` can
|
||||
be removed this way:
|
||||
|
||||
-------------------------------------------------------
|
||||
git filter-branch --message-filter '
|
||||
sed -e "/^git-svn-id:/d"
|
||||
'
|
||||
-------------------------------------------------------
|
||||
|
||||
To restrict rewriting to only part of the history, specify a revision
|
||||
range in addition to the new branch name. The new branch name will
|
||||
|
@ -10,13 +10,15 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]
|
||||
[--attach[=<boundary>] | --inline[=<boundary>]]
|
||||
[-s | --signoff] [<common diff options>]
|
||||
[-n | --numbered | -N | --no-numbered]
|
||||
[--start-number <n>] [--numbered-files]
|
||||
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
|
||||
[--ignore-if-in-upstream]
|
||||
[--subject-prefix=Subject-Prefix]
|
||||
[--attach[=<boundary>] | --inline[=<boundary>]]
|
||||
[-s | --signoff] [<common diff options>]
|
||||
[-n | --numbered | -N | --no-numbered]
|
||||
[--start-number <n>] [--numbered-files]
|
||||
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
|
||||
[--ignore-if-in-upstream]
|
||||
[--subject-prefix=Subject-Prefix]
|
||||
[--cc=<email>]
|
||||
[--cover-letter]
|
||||
[ <since> | <revision range> ]
|
||||
|
||||
DESCRIPTION
|
||||
@ -135,6 +137,15 @@ include::diff-options.txt[]
|
||||
allows for useful naming of a patch series, and can be
|
||||
combined with the --numbered option.
|
||||
|
||||
--cc=<email>::
|
||||
Add a "Cc:" header to the email headers. This is in addition
|
||||
to any configured headers, and may be used multiple times.
|
||||
|
||||
--cover-letter::
|
||||
Generate a cover letter template. You still have to fill in
|
||||
a description, but the shortlog and the diffstat will be
|
||||
generated for you.
|
||||
|
||||
--suffix=.<sfx>::
|
||||
Instead of using `.patch` as the suffix for generated
|
||||
filenames, use specified suffix. A common alternative is
|
||||
|
@ -177,6 +177,8 @@ base-name::
|
||||
This is meant to reduce packing time on multiprocessor machines.
|
||||
The required amount of memory for the delta search window is
|
||||
however multiplied by the number of threads.
|
||||
Specifying 0 will cause git to auto-detect the number of CPU's
|
||||
and set the number of threads accordingly.
|
||||
|
||||
--index-version=<version>[,<offset>]::
|
||||
This is intended to be used by the test suite only. It allows
|
||||
|
@ -31,6 +31,7 @@ SYNOPSIS
|
||||
[ \--(author|committer|grep)=<pattern> ]
|
||||
[ \--regexp-ignore-case | \-i ]
|
||||
[ \--extended-regexp | \-E ]
|
||||
[ \--fixed-strings | \-F ]
|
||||
[ \--date={local|relative|default|iso|rfc|short} ]
|
||||
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
|
||||
[ \--pretty | \--header ]
|
||||
|
@ -159,6 +159,10 @@ New features:
|
||||
our version of --pretty=oneline
|
||||
--
|
||||
+
|
||||
NOTE: SVN itself only stores times in UTC and nothing else. The regular svn
|
||||
client converts the UTC time to the local time (or based on the TZ=
|
||||
environment). This command has the same behaviour.
|
||||
+
|
||||
Any other arguments are passed directly to `git log'
|
||||
|
||||
'blame'::
|
||||
|
@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.5.4.2/git.html[documentation for release 1.5.4.2]
|
||||
* link:v1.5.4.3/git.html[documentation for release 1.5.4.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.4.3.txt[1.5.4.3],
|
||||
link:RelNotes-1.5.4.2.txt[1.5.4.2],
|
||||
link:RelNotes-1.5.4.1.txt[1.5.4.1],
|
||||
link:RelNotes-1.5.4.txt[1.5.4].
|
||||
|
@ -153,6 +153,11 @@ limiting may be applied.
|
||||
Consider the limiting patterns to be extended regular expressions
|
||||
instead of the default basic regular expressions.
|
||||
|
||||
-F, --fixed-strings::
|
||||
|
||||
Consider the limiting patterns to be fixed strings (don't interpret
|
||||
pattern as a regular expression).
|
||||
|
||||
--remove-empty::
|
||||
|
||||
Stop when a given path disappears from the tree.
|
||||
|
@ -39,7 +39,7 @@ Calling sequence
|
||||
* Once you finish feeding the pairs of files, call `diffcore_std()`.
|
||||
This will tell the diffcore library to go ahead and do its work.
|
||||
|
||||
* Calling `diffcore_flush()` will produce the output.
|
||||
* Calling `diff_flush()` will produce the output.
|
||||
|
||||
|
||||
Data structures
|
||||
|
@ -44,3 +44,26 @@ endif::git-clone[]
|
||||
ifdef::git-clone[]
|
||||
They are equivalent, except the former implies --local option.
|
||||
endif::git-clone[]
|
||||
|
||||
|
||||
If there are a large number of similarly-named remote repositories and
|
||||
you want to use a different format for them (such that the URLs you
|
||||
use will be rewritten into URLs that work), you can create a
|
||||
configuration section of the form:
|
||||
|
||||
------------
|
||||
[url "<actual url base>"]
|
||||
insteadOf = <other url base>
|
||||
------------
|
||||
|
||||
For example, with this:
|
||||
|
||||
------------
|
||||
[url "git://git.host.xz/"]
|
||||
insteadOf = host.xz:/path/to/
|
||||
insteadOf = work:
|
||||
------------
|
||||
|
||||
a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
|
||||
rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
|
||||
|
||||
|
@ -16,7 +16,8 @@ elif test -d .git &&
|
||||
case "$VN" in
|
||||
*$LF*) (exit 1) ;;
|
||||
v[0-9]*)
|
||||
git diff-index --quiet HEAD || VN="$VN-dirty" ;;
|
||||
test -z "$(git diff-index --name-only HEAD)" ||
|
||||
VN="$VN-dirty" ;;
|
||||
esac
|
||||
then
|
||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||
|
19
Makefile
19
Makefile
@ -226,7 +226,7 @@ BASIC_CFLAGS =
|
||||
BASIC_LDFLAGS =
|
||||
|
||||
SCRIPT_SH = \
|
||||
git-bisect.sh git-checkout.sh \
|
||||
git-bisect.sh \
|
||||
git-clone.sh \
|
||||
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
|
||||
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
|
||||
@ -265,23 +265,23 @@ PROGRAMS = \
|
||||
git-upload-pack$X \
|
||||
git-pack-redundant$X git-var$X \
|
||||
git-merge-tree$X git-imap-send$X \
|
||||
git-merge-recursive$X \
|
||||
$(EXTRA_PROGRAMS)
|
||||
|
||||
# Empty...
|
||||
EXTRA_PROGRAMS =
|
||||
|
||||
# List built-in command $C whose implementation cmd_$C() is not in
|
||||
# builtin-$C.o but is linked in as part of some other command.
|
||||
BUILT_INS = \
|
||||
git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
|
||||
git-get-tar-commit-id$X git-init$X git-repo-config$X \
|
||||
git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
|
||||
git-merge-subtree$X \
|
||||
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
|
||||
|
||||
# what 'all' will build and 'install' will install, in gitexecdir
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
|
||||
|
||||
ALL_PROGRAMS += git-merge-subtree$X
|
||||
|
||||
# what 'all' will build but not install in gitexecdir
|
||||
OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
|
||||
|
||||
@ -327,7 +327,8 @@ LIB_OBJS = \
|
||||
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
|
||||
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
|
||||
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
|
||||
transport.o bundle.o walker.o parse-options.o ws.o archive.o
|
||||
transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
|
||||
alias.o
|
||||
|
||||
BUILTIN_OBJS = \
|
||||
builtin-add.o \
|
||||
@ -339,6 +340,7 @@ BUILTIN_OBJS = \
|
||||
builtin-bundle.o \
|
||||
builtin-cat-file.o \
|
||||
builtin-check-attr.o \
|
||||
builtin-checkout.o \
|
||||
builtin-checkout-index.o \
|
||||
builtin-check-ref-format.o \
|
||||
builtin-clean.o \
|
||||
@ -369,6 +371,7 @@ BUILTIN_OBJS = \
|
||||
builtin-merge-base.o \
|
||||
builtin-merge-file.o \
|
||||
builtin-merge-ours.o \
|
||||
builtin-merge-recursive.o \
|
||||
builtin-mv.o \
|
||||
builtin-name-rev.o \
|
||||
builtin-pack-objects.o \
|
||||
@ -741,6 +744,7 @@ endif
|
||||
ifdef THREADED_DELTA_SEARCH
|
||||
BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
|
||||
EXTLIBS += -lpthread
|
||||
LIB_OBJS += thread-utils.o
|
||||
endif
|
||||
|
||||
ifeq ($(TCLTK_PATH),)
|
||||
@ -838,9 +842,6 @@ help.o: help.c common-cmds.h GIT-CFLAGS
|
||||
'-DGIT_MAN_PATH="$(mandir_SQ)"' \
|
||||
'-DGIT_INFO_PATH="$(infodir_SQ)"' $<
|
||||
|
||||
git-merge-subtree$X: git-merge-recursive$X
|
||||
$(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
|
||||
|
||||
$(BUILT_INS): git$X
|
||||
$(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
|
||||
|
||||
@ -1102,7 +1103,7 @@ git.spec: git.spec.in
|
||||
mv $@+ $@
|
||||
|
||||
GIT_TARNAME=git-$(GIT_VERSION)
|
||||
dist: git.spec git-archive configure
|
||||
dist: git.spec git-archive$(X) configure
|
||||
./git-archive --format=tar \
|
||||
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
|
||||
@mkdir -p $(GIT_TARNAME)
|
||||
|
22
alias.c
Normal file
22
alias.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include "cache.h"
|
||||
|
||||
static const char *alias_key;
|
||||
static char *alias_val;
|
||||
static int alias_lookup_cb(const char *k, const char *v)
|
||||
{
|
||||
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
|
||||
if (!v)
|
||||
return config_error_nonbool(k);
|
||||
alias_val = xstrdup(v);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *alias_lookup(const char *alias)
|
||||
{
|
||||
alias_key = alias;
|
||||
alias_val = NULL;
|
||||
git_config(alias_lookup_cb);
|
||||
return alias_val;
|
||||
}
|
152
branch.c
Normal file
152
branch.c
Normal file
@ -0,0 +1,152 @@
|
||||
#include "cache.h"
|
||||
#include "branch.h"
|
||||
#include "refs.h"
|
||||
#include "remote.h"
|
||||
#include "commit.h"
|
||||
|
||||
struct tracking {
|
||||
struct refspec spec;
|
||||
char *src;
|
||||
const char *remote;
|
||||
int matches;
|
||||
};
|
||||
|
||||
static int find_tracked_branch(struct remote *remote, void *priv)
|
||||
{
|
||||
struct tracking *tracking = priv;
|
||||
|
||||
if (!remote_find_tracking(remote, &tracking->spec)) {
|
||||
if (++tracking->matches == 1) {
|
||||
tracking->src = tracking->spec.src;
|
||||
tracking->remote = remote->name;
|
||||
} else {
|
||||
free(tracking->spec.src);
|
||||
if (tracking->src) {
|
||||
free(tracking->src);
|
||||
tracking->src = NULL;
|
||||
}
|
||||
}
|
||||
tracking->spec.src = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when new_ref is branched off of orig_ref, and tries
|
||||
* to infer the settings for branch.<new_ref>.{remote,merge} from the
|
||||
* config.
|
||||
*/
|
||||
static int setup_tracking(const char *new_ref, const char *orig_ref,
|
||||
enum branch_track track)
|
||||
{
|
||||
char key[1024];
|
||||
struct tracking tracking;
|
||||
|
||||
if (strlen(new_ref) > 1024 - 7 - 7 - 1)
|
||||
return error("Tracking not set up: name too long: %s",
|
||||
new_ref);
|
||||
|
||||
memset(&tracking, 0, sizeof(tracking));
|
||||
tracking.spec.dst = (char *)orig_ref;
|
||||
if (for_each_remote(find_tracked_branch, &tracking))
|
||||
return 1;
|
||||
|
||||
if (!tracking.matches)
|
||||
switch (track) {
|
||||
case BRANCH_TRACK_ALWAYS:
|
||||
case BRANCH_TRACK_EXPLICIT:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (tracking.matches > 1)
|
||||
return error("Not tracking: ambiguous information for ref %s",
|
||||
orig_ref);
|
||||
|
||||
sprintf(key, "branch.%s.remote", new_ref);
|
||||
git_config_set(key, tracking.remote ? tracking.remote : ".");
|
||||
sprintf(key, "branch.%s.merge", new_ref);
|
||||
git_config_set(key, tracking.src ? tracking.src : orig_ref);
|
||||
free(tracking.src);
|
||||
printf("Branch %s set up to track %s branch %s.\n", new_ref,
|
||||
tracking.remote ? "remote" : "local", orig_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void create_branch(const char *head,
|
||||
const char *name, const char *start_name,
|
||||
int force, int reflog, enum branch_track track)
|
||||
{
|
||||
struct ref_lock *lock;
|
||||
struct commit *commit;
|
||||
unsigned char sha1[20];
|
||||
char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
|
||||
int forcing = 0;
|
||||
|
||||
snprintf(ref, sizeof ref, "refs/heads/%s", name);
|
||||
if (check_ref_format(ref))
|
||||
die("'%s' is not a valid branch name.", name);
|
||||
|
||||
if (resolve_ref(ref, sha1, 1, NULL)) {
|
||||
if (!force)
|
||||
die("A branch named '%s' already exists.", name);
|
||||
else if (!is_bare_repository() && !strcmp(head, name))
|
||||
die("Cannot force update the current branch.");
|
||||
forcing = 1;
|
||||
}
|
||||
|
||||
real_ref = NULL;
|
||||
if (get_sha1(start_name, sha1))
|
||||
die("Not a valid object name: '%s'.", start_name);
|
||||
|
||||
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
|
||||
case 0:
|
||||
/* Not branching from any existing branch */
|
||||
if (track == BRANCH_TRACK_EXPLICIT)
|
||||
die("Cannot setup tracking information; starting point is not a branch.");
|
||||
break;
|
||||
case 1:
|
||||
/* Unique completion -- good */
|
||||
break;
|
||||
default:
|
||||
die("Ambiguous object name: '%s'.", start_name);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((commit = lookup_commit_reference(sha1)) == NULL)
|
||||
die("Not a valid branch point: '%s'.", start_name);
|
||||
hashcpy(sha1, commit->object.sha1);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, NULL, 0);
|
||||
if (!lock)
|
||||
die("Failed to lock ref for update: %s.", strerror(errno));
|
||||
|
||||
if (reflog)
|
||||
log_all_ref_updates = 1;
|
||||
|
||||
if (forcing)
|
||||
snprintf(msg, sizeof msg, "branch: Reset from %s",
|
||||
start_name);
|
||||
else
|
||||
snprintf(msg, sizeof msg, "branch: Created from %s",
|
||||
start_name);
|
||||
|
||||
if (real_ref && track)
|
||||
setup_tracking(name, real_ref, track);
|
||||
|
||||
if (write_ref_sha1(lock, sha1, msg) < 0)
|
||||
die("Failed to write ref: %s.", strerror(errno));
|
||||
|
||||
free(real_ref);
|
||||
}
|
||||
|
||||
void remove_branch_state(void)
|
||||
{
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
unlink(git_path("SQUASH_MSG"));
|
||||
}
|
24
branch.h
Normal file
24
branch.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef BRANCH_H
|
||||
#define BRANCH_H
|
||||
|
||||
/* Functions for acting on the information about branches. */
|
||||
|
||||
/*
|
||||
* Creates a new branch, where head is the branch currently checked
|
||||
* out, name is the new branch name, start_name is the name of the
|
||||
* existing branch that the new branch should start from, force
|
||||
* enables overwriting an existing (non-head) branch, reflog creates a
|
||||
* reflog for the branch, and track causes the new branch to be
|
||||
* configured to merge the remote branch that start_name is a tracking
|
||||
* branch for (if any).
|
||||
*/
|
||||
void create_branch(const char *head, const char *name, const char *start_name,
|
||||
int force, int reflog, enum branch_track track);
|
||||
|
||||
/*
|
||||
* Remove information about the state of working on the current
|
||||
* branch. (E.g., MERGE_HEAD)
|
||||
*/
|
||||
void remove_branch_state(void);
|
||||
|
||||
#endif
|
711
builtin-apply.c
711
builtin-apply.c
@ -161,6 +161,84 @@ struct patch {
|
||||
struct patch *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* A line in a file, len-bytes long (includes the terminating LF,
|
||||
* except for an incomplete line at the end if the file ends with
|
||||
* one), and its contents hashes to 'hash'.
|
||||
*/
|
||||
struct line {
|
||||
size_t len;
|
||||
unsigned hash : 24;
|
||||
unsigned flag : 8;
|
||||
#define LINE_COMMON 1
|
||||
};
|
||||
|
||||
/*
|
||||
* This represents a "file", which is an array of "lines".
|
||||
*/
|
||||
struct image {
|
||||
char *buf;
|
||||
size_t len;
|
||||
size_t nr;
|
||||
size_t alloc;
|
||||
struct line *line_allocated;
|
||||
struct line *line;
|
||||
};
|
||||
|
||||
static uint32_t hash_line(const char *cp, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t h;
|
||||
for (i = 0, h = 0; i < len; i++) {
|
||||
if (!isspace(cp[i])) {
|
||||
h = h * 3 + (cp[i] & 0xff);
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
|
||||
{
|
||||
ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
|
||||
img->line_allocated[img->nr].len = len;
|
||||
img->line_allocated[img->nr].hash = hash_line(bol, len);
|
||||
img->line_allocated[img->nr].flag = flag;
|
||||
img->nr++;
|
||||
}
|
||||
|
||||
static void prepare_image(struct image *image, char *buf, size_t len,
|
||||
int prepare_linetable)
|
||||
{
|
||||
const char *cp, *ep;
|
||||
|
||||
memset(image, 0, sizeof(*image));
|
||||
image->buf = buf;
|
||||
image->len = len;
|
||||
|
||||
if (!prepare_linetable)
|
||||
return;
|
||||
|
||||
ep = image->buf + image->len;
|
||||
cp = image->buf;
|
||||
while (cp < ep) {
|
||||
const char *next;
|
||||
for (next = cp; next < ep && *next != '\n'; next++)
|
||||
;
|
||||
if (next < ep)
|
||||
next++;
|
||||
add_line_info(image, cp, next - cp, 0);
|
||||
cp = next;
|
||||
}
|
||||
image->line = image->line_allocated;
|
||||
}
|
||||
|
||||
static void clear_image(struct image *image)
|
||||
{
|
||||
free(image->buf);
|
||||
image->buf = NULL;
|
||||
image->len = 0;
|
||||
}
|
||||
|
||||
static void say_patch_name(FILE *output, const char *pre,
|
||||
struct patch *patch, const char *post)
|
||||
{
|
||||
@ -1437,227 +1515,338 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
|
||||
}
|
||||
}
|
||||
|
||||
static int find_offset(const char *buf, unsigned long size,
|
||||
const char *fragment, unsigned long fragsize,
|
||||
int line, int *lines)
|
||||
static void update_pre_post_images(struct image *preimage,
|
||||
struct image *postimage,
|
||||
char *buf,
|
||||
size_t len)
|
||||
{
|
||||
int i;
|
||||
unsigned long start, backwards, forwards;
|
||||
int i, ctx;
|
||||
char *new, *old, *fixed;
|
||||
struct image fixed_preimage;
|
||||
|
||||
if (fragsize > size)
|
||||
return -1;
|
||||
/*
|
||||
* Update the preimage with whitespace fixes. Note that we
|
||||
* are not losing preimage->buf -- apply_one_fragment() will
|
||||
* free "oldlines".
|
||||
*/
|
||||
prepare_image(&fixed_preimage, buf, len, 1);
|
||||
assert(fixed_preimage.nr == preimage->nr);
|
||||
for (i = 0; i < preimage->nr; i++)
|
||||
fixed_preimage.line[i].flag = preimage->line[i].flag;
|
||||
free(preimage->line_allocated);
|
||||
*preimage = fixed_preimage;
|
||||
|
||||
start = 0;
|
||||
if (line > 1) {
|
||||
unsigned long offset = 0;
|
||||
i = line-1;
|
||||
while (offset + fragsize <= size) {
|
||||
if (buf[offset++] == '\n') {
|
||||
start = offset;
|
||||
if (!--i)
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Adjust the common context lines in postimage, in place.
|
||||
* This is possible because whitespace fixing does not make
|
||||
* the string grow.
|
||||
*/
|
||||
new = old = postimage->buf;
|
||||
fixed = preimage->buf;
|
||||
for (i = ctx = 0; i < postimage->nr; i++) {
|
||||
size_t len = postimage->line[i].len;
|
||||
if (!(postimage->line[i].flag & LINE_COMMON)) {
|
||||
/* an added line -- no counterparts in preimage */
|
||||
memmove(new, old, len);
|
||||
old += len;
|
||||
new += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* a common context -- skip it in the original postimage */
|
||||
old += len;
|
||||
|
||||
/* and find the corresponding one in the fixed preimage */
|
||||
while (ctx < preimage->nr &&
|
||||
!(preimage->line[ctx].flag & LINE_COMMON)) {
|
||||
fixed += preimage->line[ctx].len;
|
||||
ctx++;
|
||||
}
|
||||
if (preimage->nr <= ctx)
|
||||
die("oops");
|
||||
|
||||
/* and copy it in, while fixing the line length */
|
||||
len = preimage->line[ctx].len;
|
||||
memcpy(new, fixed, len);
|
||||
new += len;
|
||||
fixed += len;
|
||||
postimage->line[i].len = len;
|
||||
ctx++;
|
||||
}
|
||||
|
||||
/* Exact line number? */
|
||||
if ((start + fragsize <= size) &&
|
||||
!memcmp(buf + start, fragment, fragsize))
|
||||
return start;
|
||||
/* Fix the length of the whole thing */
|
||||
postimage->len = new - postimage->buf;
|
||||
}
|
||||
|
||||
static int match_fragment(struct image *img,
|
||||
struct image *preimage,
|
||||
struct image *postimage,
|
||||
unsigned long try,
|
||||
int try_lno,
|
||||
unsigned ws_rule,
|
||||
int match_beginning, int match_end)
|
||||
{
|
||||
int i;
|
||||
char *fixed_buf, *buf, *orig, *target;
|
||||
|
||||
if (preimage->nr + try_lno > img->nr)
|
||||
return 0;
|
||||
|
||||
if (match_beginning && try_lno)
|
||||
return 0;
|
||||
|
||||
if (match_end && preimage->nr + try_lno != img->nr)
|
||||
return 0;
|
||||
|
||||
/* Quick hash check */
|
||||
for (i = 0; i < preimage->nr; i++)
|
||||
if (preimage->line[i].hash != img->line[try_lno + i].hash)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Do we have an exact match? If we were told to match
|
||||
* at the end, size must be exactly at try+fragsize,
|
||||
* otherwise try+fragsize must be still within the preimage,
|
||||
* and either case, the old piece should match the preimage
|
||||
* exactly.
|
||||
*/
|
||||
if ((match_end
|
||||
? (try + preimage->len == img->len)
|
||||
: (try + preimage->len <= img->len)) &&
|
||||
!memcmp(img->buf + try, preimage->buf, preimage->len))
|
||||
return 1;
|
||||
|
||||
if (ws_error_action != correct_ws_error)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The hunk does not apply byte-by-byte, but the hash says
|
||||
* it might with whitespace fuzz.
|
||||
*/
|
||||
fixed_buf = xmalloc(preimage->len + 1);
|
||||
buf = fixed_buf;
|
||||
orig = preimage->buf;
|
||||
target = img->buf + try;
|
||||
for (i = 0; i < preimage->nr; i++) {
|
||||
size_t fixlen; /* length after fixing the preimage */
|
||||
size_t oldlen = preimage->line[i].len;
|
||||
size_t tgtlen = img->line[try_lno + i].len;
|
||||
size_t tgtfixlen; /* length after fixing the target line */
|
||||
char tgtfixbuf[1024], *tgtfix;
|
||||
int match;
|
||||
|
||||
/* Try fixing the line in the preimage */
|
||||
fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
|
||||
|
||||
/* Try fixing the line in the target */
|
||||
if (sizeof(tgtfixbuf) < tgtlen)
|
||||
tgtfix = tgtfixbuf;
|
||||
else
|
||||
tgtfix = xmalloc(tgtlen);
|
||||
tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
|
||||
|
||||
/*
|
||||
* If they match, either the preimage was based on
|
||||
* a version before our tree fixed whitespace breakage,
|
||||
* or we are lacking a whitespace-fix patch the tree
|
||||
* the preimage was based on already had (i.e. target
|
||||
* has whitespace breakage, the preimage doesn't).
|
||||
* In either case, we are fixing the whitespace breakages
|
||||
* so we might as well take the fix together with their
|
||||
* real change.
|
||||
*/
|
||||
match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
|
||||
|
||||
if (tgtfix != tgtfixbuf)
|
||||
free(tgtfix);
|
||||
if (!match)
|
||||
goto unmatch_exit;
|
||||
|
||||
orig += oldlen;
|
||||
buf += fixlen;
|
||||
target += tgtlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Yes, the preimage is based on an older version that still
|
||||
* has whitespace breakages unfixed, and fixing them makes the
|
||||
* hunk match. Update the context lines in the postimage.
|
||||
*/
|
||||
update_pre_post_images(preimage, postimage,
|
||||
fixed_buf, buf - fixed_buf);
|
||||
return 1;
|
||||
|
||||
unmatch_exit:
|
||||
free(fixed_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_pos(struct image *img,
|
||||
struct image *preimage,
|
||||
struct image *postimage,
|
||||
int line,
|
||||
unsigned ws_rule,
|
||||
int match_beginning, int match_end)
|
||||
{
|
||||
int i;
|
||||
unsigned long backwards, forwards, try;
|
||||
int backwards_lno, forwards_lno, try_lno;
|
||||
|
||||
if (preimage->nr > img->nr)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* If match_begining or match_end is specified, there is no
|
||||
* point starting from a wrong line that will never match and
|
||||
* wander around and wait for a match at the specified end.
|
||||
*/
|
||||
if (match_beginning)
|
||||
line = 0;
|
||||
else if (match_end)
|
||||
line = img->nr - preimage->nr;
|
||||
|
||||
if (line > img->nr)
|
||||
line = img->nr;
|
||||
|
||||
try = 0;
|
||||
for (i = 0; i < line; i++)
|
||||
try += img->line[i].len;
|
||||
|
||||
/*
|
||||
* There's probably some smart way to do this, but I'll leave
|
||||
* that to the smart and beautiful people. I'm simple and stupid.
|
||||
*/
|
||||
backwards = start;
|
||||
forwards = start;
|
||||
for (i = 0; ; i++) {
|
||||
unsigned long try;
|
||||
int n;
|
||||
backwards = try;
|
||||
backwards_lno = line;
|
||||
forwards = try;
|
||||
forwards_lno = line;
|
||||
try_lno = line;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
if (match_fragment(img, preimage, postimage,
|
||||
try, try_lno, ws_rule,
|
||||
match_beginning, match_end))
|
||||
return try_lno;
|
||||
|
||||
again:
|
||||
if (backwards_lno == 0 && forwards_lno == img->nr)
|
||||
break;
|
||||
|
||||
/* "backward" */
|
||||
if (i & 1) {
|
||||
if (!backwards) {
|
||||
if (forwards + fragsize > size)
|
||||
break;
|
||||
continue;
|
||||
if (backwards_lno == 0) {
|
||||
i++;
|
||||
goto again;
|
||||
}
|
||||
do {
|
||||
--backwards;
|
||||
} while (backwards && buf[backwards-1] != '\n');
|
||||
backwards_lno--;
|
||||
backwards -= img->line[backwards_lno].len;
|
||||
try = backwards;
|
||||
try_lno = backwards_lno;
|
||||
} else {
|
||||
while (forwards + fragsize <= size) {
|
||||
if (buf[forwards++] == '\n')
|
||||
break;
|
||||
if (forwards_lno == img->nr) {
|
||||
i++;
|
||||
goto again;
|
||||
}
|
||||
forwards += img->line[forwards_lno].len;
|
||||
forwards_lno++;
|
||||
try = forwards;
|
||||
try_lno = forwards_lno;
|
||||
}
|
||||
|
||||
if (try + fragsize > size)
|
||||
continue;
|
||||
if (memcmp(buf + try, fragment, fragsize))
|
||||
continue;
|
||||
n = (i >> 1)+1;
|
||||
if (i & 1)
|
||||
n = -n;
|
||||
*lines = n;
|
||||
return try;
|
||||
}
|
||||
|
||||
/*
|
||||
* We should start searching forward and backward.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void remove_first_line(const char **rbuf, int *rsize)
|
||||
static void remove_first_line(struct image *img)
|
||||
{
|
||||
const char *buf = *rbuf;
|
||||
int size = *rsize;
|
||||
unsigned long offset;
|
||||
offset = 0;
|
||||
while (offset <= size) {
|
||||
if (buf[offset++] == '\n')
|
||||
break;
|
||||
}
|
||||
*rsize = size - offset;
|
||||
*rbuf = buf + offset;
|
||||
img->buf += img->line[0].len;
|
||||
img->len -= img->line[0].len;
|
||||
img->line++;
|
||||
img->nr--;
|
||||
}
|
||||
|
||||
static void remove_last_line(const char **rbuf, int *rsize)
|
||||
static void remove_last_line(struct image *img)
|
||||
{
|
||||
const char *buf = *rbuf;
|
||||
int size = *rsize;
|
||||
unsigned long offset;
|
||||
offset = size - 1;
|
||||
while (offset > 0) {
|
||||
if (buf[--offset] == '\n')
|
||||
break;
|
||||
}
|
||||
*rsize = offset + 1;
|
||||
img->len -= img->line[--img->nr].len;
|
||||
}
|
||||
|
||||
static int apply_line(char *output, const char *patch, int plen,
|
||||
unsigned ws_rule)
|
||||
static void update_image(struct image *img,
|
||||
int applied_pos,
|
||||
struct image *preimage,
|
||||
struct image *postimage)
|
||||
{
|
||||
/*
|
||||
* plen is number of bytes to be copied from patch,
|
||||
* starting at patch+1 (patch[0] is '+'). Typically
|
||||
* patch[plen] is '\n', unless this is the incomplete
|
||||
* last line.
|
||||
* remove the copy of preimage at offset in img
|
||||
* and replace it with postimage
|
||||
*/
|
||||
int i;
|
||||
int add_nl_to_tail = 0;
|
||||
int fixed = 0;
|
||||
int last_tab_in_indent = 0;
|
||||
int last_space_in_indent = 0;
|
||||
int need_fix_leading_space = 0;
|
||||
char *buf;
|
||||
int i, nr;
|
||||
size_t remove_count, insert_count, applied_at = 0;
|
||||
char *result;
|
||||
|
||||
if ((ws_error_action != correct_ws_error) || !whitespace_error ||
|
||||
*patch != '+') {
|
||||
memcpy(output, patch + 1, plen);
|
||||
return plen;
|
||||
}
|
||||
for (i = 0; i < applied_pos; i++)
|
||||
applied_at += img->line[i].len;
|
||||
|
||||
/*
|
||||
* Strip trailing whitespace
|
||||
*/
|
||||
if ((ws_rule & WS_TRAILING_SPACE) &&
|
||||
(1 < plen && isspace(patch[plen-1]))) {
|
||||
if (patch[plen] == '\n')
|
||||
add_nl_to_tail = 1;
|
||||
plen--;
|
||||
while (0 < plen && isspace(patch[plen]))
|
||||
plen--;
|
||||
fixed = 1;
|
||||
}
|
||||
remove_count = 0;
|
||||
for (i = 0; i < preimage->nr; i++)
|
||||
remove_count += img->line[applied_pos + i].len;
|
||||
insert_count = postimage->len;
|
||||
|
||||
/*
|
||||
* Check leading whitespaces (indent)
|
||||
*/
|
||||
for (i = 1; i < plen; i++) {
|
||||
char ch = patch[i];
|
||||
if (ch == '\t') {
|
||||
last_tab_in_indent = i;
|
||||
if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
|
||||
0 < last_space_in_indent)
|
||||
need_fix_leading_space = 1;
|
||||
} else if (ch == ' ') {
|
||||
last_space_in_indent = i;
|
||||
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
|
||||
8 <= i - last_tab_in_indent)
|
||||
need_fix_leading_space = 1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
buf = output;
|
||||
if (need_fix_leading_space) {
|
||||
int consecutive_spaces = 0;
|
||||
int last = last_tab_in_indent + 1;
|
||||
|
||||
if (ws_rule & WS_INDENT_WITH_NON_TAB) {
|
||||
/* have "last" point at one past the indent */
|
||||
if (last_tab_in_indent < last_space_in_indent)
|
||||
last = last_space_in_indent + 1;
|
||||
else
|
||||
last = last_tab_in_indent + 1;
|
||||
}
|
||||
/* Adjust the contents */
|
||||
result = xmalloc(img->len + insert_count - remove_count + 1);
|
||||
memcpy(result, img->buf, applied_at);
|
||||
memcpy(result + applied_at, postimage->buf, postimage->len);
|
||||
memcpy(result + applied_at + postimage->len,
|
||||
img->buf + (applied_at + remove_count),
|
||||
img->len - (applied_at + remove_count));
|
||||
free(img->buf);
|
||||
img->buf = result;
|
||||
img->len += insert_count - remove_count;
|
||||
result[img->len] = '\0';
|
||||
|
||||
/* Adjust the line table */
|
||||
nr = img->nr + postimage->nr - preimage->nr;
|
||||
if (preimage->nr < postimage->nr) {
|
||||
/*
|
||||
* between patch[1..last], strip the funny spaces,
|
||||
* updating them to tab as needed.
|
||||
* NOTE: this knows that we never call remove_first_line()
|
||||
* on anything other than pre/post image.
|
||||
*/
|
||||
for (i = 1; i < last; i++, plen--) {
|
||||
char ch = patch[i];
|
||||
if (ch != ' ') {
|
||||
consecutive_spaces = 0;
|
||||
*output++ = ch;
|
||||
} else {
|
||||
consecutive_spaces++;
|
||||
if (consecutive_spaces == 8) {
|
||||
*output++ = '\t';
|
||||
consecutive_spaces = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (0 < consecutive_spaces--)
|
||||
*output++ = ' ';
|
||||
fixed = 1;
|
||||
i = last;
|
||||
img->line = xrealloc(img->line, nr * sizeof(*img->line));
|
||||
img->line_allocated = img->line;
|
||||
}
|
||||
else
|
||||
i = 1;
|
||||
|
||||
memcpy(output, patch + i, plen);
|
||||
if (add_nl_to_tail)
|
||||
output[plen++] = '\n';
|
||||
if (fixed)
|
||||
applied_after_fixing_ws++;
|
||||
return output + plen - buf;
|
||||
if (preimage->nr != postimage->nr)
|
||||
memmove(img->line + applied_pos + postimage->nr,
|
||||
img->line + applied_pos + preimage->nr,
|
||||
(img->nr - (applied_pos + preimage->nr)) *
|
||||
sizeof(*img->line));
|
||||
memcpy(img->line + applied_pos,
|
||||
postimage->line,
|
||||
postimage->nr * sizeof(*img->line));
|
||||
img->nr = nr;
|
||||
}
|
||||
|
||||
static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
static int apply_one_fragment(struct image *img, struct fragment *frag,
|
||||
int inaccurate_eof, unsigned ws_rule)
|
||||
{
|
||||
int match_beginning, match_end;
|
||||
const char *patch = frag->patch;
|
||||
int offset, size = frag->size;
|
||||
char *old = xmalloc(size);
|
||||
char *new = xmalloc(size);
|
||||
const char *oldlines, *newlines;
|
||||
int oldsize = 0, newsize = 0;
|
||||
int size = frag->size;
|
||||
char *old, *new, *oldlines, *newlines;
|
||||
int new_blank_lines_at_end = 0;
|
||||
unsigned long leading, trailing;
|
||||
int pos, lines;
|
||||
int pos, applied_pos;
|
||||
struct image preimage;
|
||||
struct image postimage;
|
||||
|
||||
memset(&preimage, 0, sizeof(preimage));
|
||||
memset(&postimage, 0, sizeof(postimage));
|
||||
oldlines = xmalloc(size);
|
||||
newlines = xmalloc(size);
|
||||
|
||||
old = oldlines;
|
||||
new = newlines;
|
||||
while (size > 0) {
|
||||
char first;
|
||||
int len = linelen(patch, size);
|
||||
int plen;
|
||||
int plen, added;
|
||||
int added_blank_line = 0;
|
||||
|
||||
if (!len)
|
||||
@ -1670,7 +1859,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
* followed by "\ No newline", then we also remove the
|
||||
* last one (which is the newline, of course).
|
||||
*/
|
||||
plen = len-1;
|
||||
plen = len - 1;
|
||||
if (len < size && patch[len] == '\\')
|
||||
plen--;
|
||||
first = *patch;
|
||||
@ -1687,25 +1876,40 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
if (plen < 0)
|
||||
/* ... followed by '\No newline'; nothing */
|
||||
break;
|
||||
old[oldsize++] = '\n';
|
||||
new[newsize++] = '\n';
|
||||
*old++ = '\n';
|
||||
*new++ = '\n';
|
||||
add_line_info(&preimage, "\n", 1, LINE_COMMON);
|
||||
add_line_info(&postimage, "\n", 1, LINE_COMMON);
|
||||
break;
|
||||
case ' ':
|
||||
case '-':
|
||||
memcpy(old + oldsize, patch + 1, plen);
|
||||
oldsize += plen;
|
||||
memcpy(old, patch + 1, plen);
|
||||
add_line_info(&preimage, old, plen,
|
||||
(first == ' ' ? LINE_COMMON : 0));
|
||||
old += plen;
|
||||
if (first == '-')
|
||||
break;
|
||||
/* Fall-through for ' ' */
|
||||
case '+':
|
||||
if (first != '+' || !no_add) {
|
||||
int added = apply_line(new + newsize, patch,
|
||||
plen, ws_rule);
|
||||
newsize += added;
|
||||
if (first == '+' &&
|
||||
added == 1 && new[newsize-1] == '\n')
|
||||
added_blank_line = 1;
|
||||
/* --no-add does not add new lines */
|
||||
if (first == '+' && no_add)
|
||||
break;
|
||||
|
||||
if (first != '+' ||
|
||||
!whitespace_error ||
|
||||
ws_error_action != correct_ws_error) {
|
||||
memcpy(new, patch + 1, plen);
|
||||
added = plen;
|
||||
}
|
||||
else {
|
||||
added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
|
||||
}
|
||||
add_line_info(&postimage, new, added,
|
||||
(first == '+' ? 0 : LINE_COMMON));
|
||||
new += added;
|
||||
if (first == '+' &&
|
||||
added == 1 && new[-1] == '\n')
|
||||
added_blank_line = 1;
|
||||
break;
|
||||
case '@': case '\\':
|
||||
/* Ignore it, we already handled it */
|
||||
@ -1722,16 +1926,13 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
patch += len;
|
||||
size -= len;
|
||||
}
|
||||
|
||||
if (inaccurate_eof &&
|
||||
oldsize > 0 && old[oldsize - 1] == '\n' &&
|
||||
newsize > 0 && new[newsize - 1] == '\n') {
|
||||
oldsize--;
|
||||
newsize--;
|
||||
old > oldlines && old[-1] == '\n' &&
|
||||
new > newlines && new[-1] == '\n') {
|
||||
old--;
|
||||
new--;
|
||||
}
|
||||
|
||||
oldlines = old;
|
||||
newlines = new;
|
||||
leading = frag->leading;
|
||||
trailing = frag->trailing;
|
||||
|
||||
@ -1752,33 +1953,21 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
match_end = !trailing;
|
||||
}
|
||||
|
||||
lines = 0;
|
||||
pos = frag->newpos;
|
||||
pos = frag->newpos ? (frag->newpos - 1) : 0;
|
||||
preimage.buf = oldlines;
|
||||
preimage.len = old - oldlines;
|
||||
postimage.buf = newlines;
|
||||
postimage.len = new - newlines;
|
||||
preimage.line = preimage.line_allocated;
|
||||
postimage.line = postimage.line_allocated;
|
||||
|
||||
for (;;) {
|
||||
offset = find_offset(buf->buf, buf->len,
|
||||
oldlines, oldsize, pos, &lines);
|
||||
if (match_end && offset + oldsize != buf->len)
|
||||
offset = -1;
|
||||
if (match_beginning && offset)
|
||||
offset = -1;
|
||||
if (offset >= 0) {
|
||||
if (ws_error_action == correct_ws_error &&
|
||||
(buf->len - oldsize - offset == 0)) /* end of file? */
|
||||
newsize -= new_blank_lines_at_end;
|
||||
|
||||
/* Warn if it was necessary to reduce the number
|
||||
* of context lines.
|
||||
*/
|
||||
if ((leading != frag->leading) ||
|
||||
(trailing != frag->trailing))
|
||||
fprintf(stderr, "Context reduced to (%ld/%ld)"
|
||||
" to apply fragment at %d\n",
|
||||
leading, trailing, pos + lines);
|
||||
applied_pos = find_pos(img, &preimage, &postimage, pos,
|
||||
ws_rule, match_beginning, match_end);
|
||||
|
||||
strbuf_splice(buf, offset, oldsize, newlines, newsize);
|
||||
offset = 0;
|
||||
if (applied_pos >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Am I at my context limits? */
|
||||
if ((leading <= p_context) && (trailing <= p_context))
|
||||
@ -1787,33 +1976,64 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
|
||||
match_beginning = match_end = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reduce the number of context lines; reduce both
|
||||
* leading and trailing if they are equal otherwise
|
||||
* just reduce the larger context.
|
||||
*/
|
||||
if (leading >= trailing) {
|
||||
remove_first_line(&oldlines, &oldsize);
|
||||
remove_first_line(&newlines, &newsize);
|
||||
remove_first_line(&preimage);
|
||||
remove_first_line(&postimage);
|
||||
pos--;
|
||||
leading--;
|
||||
}
|
||||
if (trailing > leading) {
|
||||
remove_last_line(&oldlines, &oldsize);
|
||||
remove_last_line(&newlines, &newsize);
|
||||
remove_last_line(&preimage);
|
||||
remove_last_line(&postimage);
|
||||
trailing--;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset && apply_verbosely)
|
||||
error("while searching for:\n%.*s", oldsize, oldlines);
|
||||
if (applied_pos >= 0) {
|
||||
if (ws_error_action == correct_ws_error &&
|
||||
new_blank_lines_at_end &&
|
||||
postimage.nr + applied_pos == img->nr) {
|
||||
/*
|
||||
* If the patch application adds blank lines
|
||||
* at the end, and if the patch applies at the
|
||||
* end of the image, remove those added blank
|
||||
* lines.
|
||||
*/
|
||||
while (new_blank_lines_at_end--)
|
||||
remove_last_line(&postimage);
|
||||
}
|
||||
|
||||
free(old);
|
||||
free(new);
|
||||
return offset;
|
||||
/*
|
||||
* Warn if it was necessary to reduce the number
|
||||
* of context lines.
|
||||
*/
|
||||
if ((leading != frag->leading) ||
|
||||
(trailing != frag->trailing))
|
||||
fprintf(stderr, "Context reduced to (%ld/%ld)"
|
||||
" to apply fragment at %d\n",
|
||||
leading, trailing, applied_pos+1);
|
||||
update_image(img, applied_pos, &preimage, &postimage);
|
||||
} else {
|
||||
if (apply_verbosely)
|
||||
error("while searching for:\n%.*s",
|
||||
(int)(old - oldlines), oldlines);
|
||||
}
|
||||
|
||||
free(oldlines);
|
||||
free(newlines);
|
||||
free(preimage.line_allocated);
|
||||
free(postimage.line_allocated);
|
||||
|
||||
return (applied_pos < 0);
|
||||
}
|
||||
|
||||
static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
|
||||
static int apply_binary_fragment(struct image *img, struct patch *patch)
|
||||
{
|
||||
struct fragment *fragment = patch->fragments;
|
||||
unsigned long len;
|
||||
@ -1830,22 +2050,26 @@ static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
|
||||
}
|
||||
switch (fragment->binary_patch_method) {
|
||||
case BINARY_DELTA_DEFLATED:
|
||||
dst = patch_delta(buf->buf, buf->len, fragment->patch,
|
||||
dst = patch_delta(img->buf, img->len, fragment->patch,
|
||||
fragment->size, &len);
|
||||
if (!dst)
|
||||
return -1;
|
||||
/* XXX patch_delta NUL-terminates */
|
||||
strbuf_attach(buf, dst, len, len + 1);
|
||||
clear_image(img);
|
||||
img->buf = dst;
|
||||
img->len = len;
|
||||
return 0;
|
||||
case BINARY_LITERAL_DEFLATED:
|
||||
strbuf_reset(buf);
|
||||
strbuf_add(buf, fragment->patch, fragment->size);
|
||||
clear_image(img);
|
||||
img->len = fragment->size;
|
||||
img->buf = xmalloc(img->len+1);
|
||||
memcpy(img->buf, fragment->patch, img->len);
|
||||
img->buf[img->len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
static int apply_binary(struct image *img, struct patch *patch)
|
||||
{
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
unsigned char sha1[20];
|
||||
@ -1866,7 +2090,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
* See if the old one matches what the patch
|
||||
* applies to.
|
||||
*/
|
||||
hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
|
||||
hash_sha1_file(img->buf, img->len, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
|
||||
return error("the patch applies to '%s' (%s), "
|
||||
"which does not match the "
|
||||
@ -1875,14 +2099,14 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
}
|
||||
else {
|
||||
/* Otherwise, the old one must be empty. */
|
||||
if (buf->len)
|
||||
if (img->len)
|
||||
return error("the patch applies to an empty "
|
||||
"'%s' but it is not empty", name);
|
||||
}
|
||||
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1);
|
||||
if (is_null_sha1(sha1)) {
|
||||
strbuf_release(buf);
|
||||
clear_image(img);
|
||||
return 0; /* deletion patch */
|
||||
}
|
||||
|
||||
@ -1897,20 +2121,21 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
return error("the necessary postimage %s for "
|
||||
"'%s' cannot be read",
|
||||
patch->new_sha1_prefix, name);
|
||||
/* XXX read_sha1_file NUL-terminates */
|
||||
strbuf_attach(buf, result, size, size + 1);
|
||||
clear_image(img);
|
||||
img->buf = result;
|
||||
img->len = size;
|
||||
} else {
|
||||
/*
|
||||
* We have verified buf matches the preimage;
|
||||
* apply the patch data to it, which is stored
|
||||
* in the patch->fragments->{patch,size}.
|
||||
*/
|
||||
if (apply_binary_fragment(buf, patch))
|
||||
if (apply_binary_fragment(img, patch))
|
||||
return error("binary patch does not apply to '%s'",
|
||||
name);
|
||||
|
||||
/* verify that the result matches */
|
||||
hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
|
||||
hash_sha1_file(img->buf, img->len, blob_type, sha1);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
|
||||
return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
|
||||
name, patch->new_sha1_prefix, sha1_to_hex(sha1));
|
||||
@ -1919,7 +2144,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_fragments(struct strbuf *buf, struct patch *patch)
|
||||
static int apply_fragments(struct image *img, struct patch *patch)
|
||||
{
|
||||
struct fragment *frag = patch->fragments;
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
@ -1927,10 +2152,10 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)
|
||||
unsigned inaccurate_eof = patch->inaccurate_eof;
|
||||
|
||||
if (patch->is_binary)
|
||||
return apply_binary(buf, patch);
|
||||
return apply_binary(img, patch);
|
||||
|
||||
while (frag) {
|
||||
if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
|
||||
if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
|
||||
error("patch failed: %s:%ld", name, frag->oldpos);
|
||||
if (!apply_with_reject)
|
||||
return -1;
|
||||
@ -1966,6 +2191,9 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
|
||||
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
|
||||
{
|
||||
struct strbuf buf;
|
||||
struct image image;
|
||||
size_t len;
|
||||
char *img;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
if (cached) {
|
||||
@ -1988,9 +2216,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
|
||||
}
|
||||
}
|
||||
|
||||
if (apply_fragments(&buf, patch) < 0)
|
||||
img = strbuf_detach(&buf, &len);
|
||||
prepare_image(&image, img, len, !patch->is_binary);
|
||||
|
||||
if (apply_fragments(&image, patch) < 0)
|
||||
return -1; /* note with --reject this succeeds. */
|
||||
patch->result = strbuf_detach(&buf, &patch->resultsize);
|
||||
patch->result = image.buf;
|
||||
patch->resultsize = image.len;
|
||||
free(image.line_allocated);
|
||||
|
||||
if (0 < patch->is_delete && patch->resultsize)
|
||||
return error("removal patch leaves file contents");
|
||||
|
@ -123,8 +123,7 @@ static inline struct origin *origin_incref(struct origin *o)
|
||||
static void origin_decref(struct origin *o)
|
||||
{
|
||||
if (o && --o->refcnt <= 0) {
|
||||
if (o->file.ptr)
|
||||
free(o->file.ptr);
|
||||
free(o->file.ptr);
|
||||
free(o);
|
||||
}
|
||||
}
|
||||
|
158
builtin-branch.c
158
builtin-branch.c
@ -12,6 +12,7 @@
|
||||
#include "builtin.h"
|
||||
#include "remote.h"
|
||||
#include "parse-options.h"
|
||||
#include "branch.h"
|
||||
|
||||
static const char * const builtin_branch_usage[] = {
|
||||
"git-branch [options] [-r | -a]",
|
||||
@ -29,8 +30,6 @@ static const char * const builtin_branch_usage[] = {
|
||||
static const char *head;
|
||||
static unsigned char head_sha1[20];
|
||||
|
||||
static int branch_track = 1;
|
||||
|
||||
static int branch_use_color = -1;
|
||||
static char branch_colors[][COLOR_MAXLEN] = {
|
||||
"\033[m", /* reset */
|
||||
@ -75,10 +74,6 @@ static int git_branch_config(const char *var, const char *value)
|
||||
color_parse(value, var, branch_colors[slot]);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "branch.autosetupmerge")) {
|
||||
branch_track = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
return git_color_default_config(var, value);
|
||||
}
|
||||
|
||||
@ -126,8 +121,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name)
|
||||
free(name);
|
||||
free(name);
|
||||
|
||||
name = xstrdup(mkpath(fmt, argv[i]));
|
||||
if (!resolve_ref(name, sha1, 1, NULL)) {
|
||||
@ -172,8 +166,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
|
||||
}
|
||||
}
|
||||
|
||||
if (name)
|
||||
free(name);
|
||||
free(name);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
@ -359,141 +352,6 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
|
||||
free_ref_list(&ref_list);
|
||||
}
|
||||
|
||||
struct tracking {
|
||||
struct refspec spec;
|
||||
char *src;
|
||||
const char *remote;
|
||||
int matches;
|
||||
};
|
||||
|
||||
static int find_tracked_branch(struct remote *remote, void *priv)
|
||||
{
|
||||
struct tracking *tracking = priv;
|
||||
|
||||
if (!remote_find_tracking(remote, &tracking->spec)) {
|
||||
if (++tracking->matches == 1) {
|
||||
tracking->src = tracking->spec.src;
|
||||
tracking->remote = remote->name;
|
||||
} else {
|
||||
free(tracking->spec.src);
|
||||
if (tracking->src) {
|
||||
free(tracking->src);
|
||||
tracking->src = NULL;
|
||||
}
|
||||
}
|
||||
tracking->spec.src = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is called when new_ref is branched off of orig_ref, and tries
|
||||
* to infer the settings for branch.<new_ref>.{remote,merge} from the
|
||||
* config.
|
||||
*/
|
||||
static int setup_tracking(const char *new_ref, const char *orig_ref)
|
||||
{
|
||||
char key[1024];
|
||||
struct tracking tracking;
|
||||
|
||||
if (strlen(new_ref) > 1024 - 7 - 7 - 1)
|
||||
return error("Tracking not set up: name too long: %s",
|
||||
new_ref);
|
||||
|
||||
memset(&tracking, 0, sizeof(tracking));
|
||||
tracking.spec.dst = (char *)orig_ref;
|
||||
if (for_each_remote(find_tracked_branch, &tracking) ||
|
||||
!tracking.matches)
|
||||
return 1;
|
||||
|
||||
if (tracking.matches > 1)
|
||||
return error("Not tracking: ambiguous information for ref %s",
|
||||
orig_ref);
|
||||
|
||||
if (tracking.matches == 1) {
|
||||
sprintf(key, "branch.%s.remote", new_ref);
|
||||
git_config_set(key, tracking.remote ? tracking.remote : ".");
|
||||
sprintf(key, "branch.%s.merge", new_ref);
|
||||
git_config_set(key, tracking.src);
|
||||
free(tracking.src);
|
||||
printf("Branch %s set up to track remote branch %s.\n",
|
||||
new_ref, orig_ref);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_branch(const char *name, const char *start_name,
|
||||
int force, int reflog, int track)
|
||||
{
|
||||
struct ref_lock *lock;
|
||||
struct commit *commit;
|
||||
unsigned char sha1[20];
|
||||
char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
|
||||
int forcing = 0;
|
||||
|
||||
snprintf(ref, sizeof ref, "refs/heads/%s", name);
|
||||
if (check_ref_format(ref))
|
||||
die("'%s' is not a valid branch name.", name);
|
||||
|
||||
if (resolve_ref(ref, sha1, 1, NULL)) {
|
||||
if (!force)
|
||||
die("A branch named '%s' already exists.", name);
|
||||
else if (!is_bare_repository() && !strcmp(head, name))
|
||||
die("Cannot force update the current branch.");
|
||||
forcing = 1;
|
||||
}
|
||||
|
||||
real_ref = NULL;
|
||||
if (get_sha1(start_name, sha1))
|
||||
die("Not a valid object name: '%s'.", start_name);
|
||||
|
||||
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
|
||||
case 0:
|
||||
/* Not branching from any existing branch */
|
||||
real_ref = NULL;
|
||||
break;
|
||||
case 1:
|
||||
/* Unique completion -- good */
|
||||
break;
|
||||
default:
|
||||
die("Ambiguous object name: '%s'.", start_name);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((commit = lookup_commit_reference(sha1)) == NULL)
|
||||
die("Not a valid branch point: '%s'.", start_name);
|
||||
hashcpy(sha1, commit->object.sha1);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, NULL, 0);
|
||||
if (!lock)
|
||||
die("Failed to lock ref for update: %s.", strerror(errno));
|
||||
|
||||
if (reflog)
|
||||
log_all_ref_updates = 1;
|
||||
|
||||
if (forcing)
|
||||
snprintf(msg, sizeof msg, "branch: Reset from %s",
|
||||
start_name);
|
||||
else
|
||||
snprintf(msg, sizeof msg, "branch: Created from %s",
|
||||
start_name);
|
||||
|
||||
/* When branching off a remote branch, set up so that git-pull
|
||||
automatically merges from there. So far, this is only done for
|
||||
remotes registered via .git/config. */
|
||||
if (real_ref && track)
|
||||
setup_tracking(name, real_ref);
|
||||
|
||||
if (write_ref_sha1(lock, sha1, msg) < 0)
|
||||
die("Failed to write ref: %s.", strerror(errno));
|
||||
|
||||
if (real_ref)
|
||||
free(real_ref);
|
||||
}
|
||||
|
||||
static void rename_branch(const char *oldname, const char *newname, int force)
|
||||
{
|
||||
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
|
||||
@ -554,14 +412,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int delete = 0, rename = 0, force_create = 0;
|
||||
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
|
||||
int reflog = 0, track;
|
||||
int reflog = 0;
|
||||
enum branch_track track;
|
||||
int kinds = REF_LOCAL_BRANCH;
|
||||
struct commit_list *with_commit = NULL;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_GROUP("Generic options"),
|
||||
OPT__VERBOSE(&verbose),
|
||||
OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"),
|
||||
OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))",
|
||||
BRANCH_TRACK_EXPLICIT),
|
||||
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
|
||||
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
|
||||
REF_REMOTE_BRANCH),
|
||||
@ -592,7 +452,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
if (branch_use_color == -1)
|
||||
branch_use_color = git_use_color_default;
|
||||
|
||||
track = branch_track;
|
||||
track = git_branch_track;
|
||||
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
|
||||
if (!!delete + !!rename + !!force_create > 1)
|
||||
usage_with_options(builtin_branch_usage, options);
|
||||
@ -618,7 +478,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
else if (rename && (argc == 2))
|
||||
rename_branch(argv[0], argv[1], rename > 1);
|
||||
else if (argc <= 2)
|
||||
create_branch(argv[0], (argc == 2) ? argv[1] : head,
|
||||
create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
|
||||
force_create, reflog, track);
|
||||
else
|
||||
usage_with_options(builtin_branch_usage, options);
|
||||
|
573
builtin-checkout.c
Normal file
573
builtin-checkout.c
Normal file
@ -0,0 +1,573 @@
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "parse-options.h"
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
#include "tree.h"
|
||||
#include "tree-walk.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "dir.h"
|
||||
#include "run-command.h"
|
||||
#include "merge-recursive.h"
|
||||
#include "branch.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "remote.h"
|
||||
|
||||
static const char * const checkout_usage[] = {
|
||||
"git checkout [options] <branch>",
|
||||
"git checkout [options] [<branch>] -- <file>...",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int post_checkout_hook(struct commit *old, struct commit *new,
|
||||
int changed)
|
||||
{
|
||||
struct child_process proc;
|
||||
const char *name = git_path("hooks/post-checkout");
|
||||
const char *argv[5];
|
||||
|
||||
if (access(name, X_OK) < 0)
|
||||
return 0;
|
||||
|
||||
memset(&proc, 0, sizeof(proc));
|
||||
argv[0] = name;
|
||||
argv[1] = xstrdup(sha1_to_hex(old->object.sha1));
|
||||
argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
|
||||
argv[3] = changed ? "1" : "0";
|
||||
argv[4] = NULL;
|
||||
proc.argv = argv;
|
||||
proc.no_stdin = 1;
|
||||
proc.stdout_to_stderr = 1;
|
||||
return run_command(&proc);
|
||||
}
|
||||
|
||||
static int update_some(const unsigned char *sha1, const char *base, int baselen,
|
||||
const char *pathname, unsigned mode, int stage)
|
||||
{
|
||||
int len;
|
||||
struct cache_entry *ce;
|
||||
|
||||
if (S_ISGITLINK(mode))
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(mode))
|
||||
return READ_TREE_RECURSIVE;
|
||||
|
||||
len = baselen + strlen(pathname);
|
||||
ce = xcalloc(1, cache_entry_size(len));
|
||||
hashcpy(ce->sha1, sha1);
|
||||
memcpy(ce->name, base, baselen);
|
||||
memcpy(ce->name + baselen, pathname, len - baselen);
|
||||
ce->ce_flags = create_ce_flags(len, 0);
|
||||
ce->ce_mode = create_ce_mode(mode);
|
||||
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_tree_some(struct tree *tree, const char **pathspec)
|
||||
{
|
||||
int newfd;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
newfd = hold_locked_index(lock_file, 1);
|
||||
read_cache();
|
||||
|
||||
read_tree_recursive(tree, "", 0, 0, pathspec, update_some);
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
die("unable to write new index file");
|
||||
|
||||
/* update the index with the given tree's info
|
||||
* for all args, expanding wildcards, and exit
|
||||
* with any non-zero return code.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checkout_paths(const char **pathspec)
|
||||
{
|
||||
int pos;
|
||||
struct checkout state;
|
||||
static char *ps_matched;
|
||||
unsigned char rev[20];
|
||||
int flag;
|
||||
struct commit *head;
|
||||
|
||||
for (pos = 0; pathspec[pos]; pos++)
|
||||
;
|
||||
ps_matched = xcalloc(1, pos);
|
||||
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
struct cache_entry *ce = active_cache[pos];
|
||||
pathspec_match(pathspec, ps_matched, ce->name, 0);
|
||||
}
|
||||
|
||||
if (report_path_error(ps_matched, pathspec, 0))
|
||||
return 1;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.force = 1;
|
||||
state.refresh_cache = 1;
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
struct cache_entry *ce = active_cache[pos];
|
||||
if (pathspec_match(pathspec, NULL, ce->name, 0)) {
|
||||
checkout_entry(ce, &state, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
resolve_ref("HEAD", rev, 0, &flag);
|
||||
head = lookup_commit_reference_gently(rev, 1);
|
||||
|
||||
return post_checkout_hook(head, head, 0);
|
||||
}
|
||||
|
||||
static void show_local_changes(struct object *head)
|
||||
{
|
||||
struct rev_info rev;
|
||||
/* I think we want full paths, even if we're in a subdirectory. */
|
||||
init_revisions(&rev, NULL);
|
||||
rev.abbrev = 0;
|
||||
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
|
||||
add_pending_object(&rev, head, NULL);
|
||||
run_diff_index(&rev, 0);
|
||||
}
|
||||
|
||||
static void describe_detached_head(char *msg, struct commit *commit)
|
||||
{
|
||||
struct strbuf sb;
|
||||
strbuf_init(&sb, 0);
|
||||
parse_commit(commit);
|
||||
pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, "", "", 0, 0);
|
||||
fprintf(stderr, "%s %s... %s\n", msg,
|
||||
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
static int reset_to_new(struct tree *tree, int quiet)
|
||||
{
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc tree_desc;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.head_idx = -1;
|
||||
opts.update = 1;
|
||||
opts.reset = 1;
|
||||
opts.merge = 1;
|
||||
opts.fn = oneway_merge;
|
||||
opts.verbose_update = !quiet;
|
||||
parse_tree(tree);
|
||||
init_tree_desc(&tree_desc, tree->buffer, tree->size);
|
||||
if (unpack_trees(1, &tree_desc, &opts))
|
||||
return 128;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_clean_to_new(struct tree *tree, int quiet)
|
||||
{
|
||||
struct unpack_trees_options opts;
|
||||
struct tree_desc tree_desc;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.head_idx = -1;
|
||||
opts.skip_unmerged = 1;
|
||||
opts.reset = 1;
|
||||
opts.merge = 1;
|
||||
opts.fn = oneway_merge;
|
||||
opts.verbose_update = !quiet;
|
||||
parse_tree(tree);
|
||||
init_tree_desc(&tree_desc, tree->buffer, tree->size);
|
||||
if (unpack_trees(1, &tree_desc, &opts))
|
||||
exit(128);
|
||||
}
|
||||
|
||||
struct checkout_opts {
|
||||
int quiet;
|
||||
int merge;
|
||||
int force;
|
||||
|
||||
char *new_branch;
|
||||
int new_branch_log;
|
||||
enum branch_track track;
|
||||
};
|
||||
|
||||
struct branch_info {
|
||||
const char *name; /* The short name used */
|
||||
const char *path; /* The full name of a real branch */
|
||||
struct commit *commit; /* The named commit */
|
||||
};
|
||||
|
||||
static void setup_branch_path(struct branch_info *branch)
|
||||
{
|
||||
struct strbuf buf;
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_addstr(&buf, "refs/heads/");
|
||||
strbuf_addstr(&buf, branch->name);
|
||||
branch->path = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static int merge_working_tree(struct checkout_opts *opts,
|
||||
struct branch_info *old, struct branch_info *new)
|
||||
{
|
||||
int ret;
|
||||
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
|
||||
int newfd = hold_locked_index(lock_file, 1);
|
||||
read_cache();
|
||||
|
||||
if (opts->force) {
|
||||
ret = reset_to_new(new->commit->tree, opts->quiet);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
struct tree_desc trees[2];
|
||||
struct tree *tree;
|
||||
struct unpack_trees_options topts;
|
||||
memset(&topts, 0, sizeof(topts));
|
||||
topts.head_idx = -1;
|
||||
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
|
||||
if (unmerged_cache()) {
|
||||
error("you need to resolve your current index first");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 2-way merge to the new branch */
|
||||
topts.update = 1;
|
||||
topts.merge = 1;
|
||||
topts.gently = opts->merge;
|
||||
topts.verbose_update = !opts->quiet;
|
||||
topts.fn = twoway_merge;
|
||||
topts.dir = xcalloc(1, sizeof(*topts.dir));
|
||||
topts.dir->show_ignored = 1;
|
||||
topts.dir->exclude_per_dir = ".gitignore";
|
||||
tree = parse_tree_indirect(old->commit->object.sha1);
|
||||
init_tree_desc(&trees[0], tree->buffer, tree->size);
|
||||
tree = parse_tree_indirect(new->commit->object.sha1);
|
||||
init_tree_desc(&trees[1], tree->buffer, tree->size);
|
||||
|
||||
if (unpack_trees(2, trees, &topts)) {
|
||||
/*
|
||||
* Unpack couldn't do a trivial merge; either
|
||||
* give up or do a real merge, depending on
|
||||
* whether the merge flag was used.
|
||||
*/
|
||||
struct tree *result;
|
||||
struct tree *work;
|
||||
if (!opts->merge)
|
||||
return 1;
|
||||
parse_commit(old->commit);
|
||||
|
||||
/* Do more real merge */
|
||||
|
||||
/*
|
||||
* We update the index fully, then write the
|
||||
* tree from the index, then merge the new
|
||||
* branch with the current tree, with the old
|
||||
* branch as the base. Then we reset the index
|
||||
* (but not the working tree) to the new
|
||||
* branch, leaving the working tree as the
|
||||
* merged version, but skipping unmerged
|
||||
* entries in the index.
|
||||
*/
|
||||
|
||||
add_files_to_cache(0, NULL, NULL);
|
||||
work = write_tree_from_memory();
|
||||
|
||||
ret = reset_to_new(new->commit->tree, opts->quiet);
|
||||
if (ret)
|
||||
return ret;
|
||||
merge_trees(new->commit->tree, work, old->commit->tree,
|
||||
new->name, "local", &result);
|
||||
reset_clean_to_new(new->commit->tree, opts->quiet);
|
||||
}
|
||||
}
|
||||
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_locked_index(lock_file))
|
||||
die("unable to write new index file");
|
||||
|
||||
if (!opts->force)
|
||||
show_local_changes(&new->commit->object);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
|
||||
{
|
||||
/*
|
||||
* We have switched to a new branch; is it building on
|
||||
* top of another branch, and if so does that other branch
|
||||
* have changes we do not have yet?
|
||||
*/
|
||||
char *base;
|
||||
unsigned char sha1[20];
|
||||
struct commit *ours, *theirs;
|
||||
char symmetric[84];
|
||||
struct rev_info revs;
|
||||
const char *rev_argv[10];
|
||||
int rev_argc;
|
||||
int num_ours, num_theirs;
|
||||
const char *remote_msg;
|
||||
struct branch *branch = branch_get(new->name);
|
||||
|
||||
/*
|
||||
* Nothing to report unless we are marked to build on top of
|
||||
* somebody else.
|
||||
*/
|
||||
if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If what we used to build on no longer exists, there is
|
||||
* nothing to report.
|
||||
*/
|
||||
base = branch->merge[0]->dst;
|
||||
if (!resolve_ref(base, sha1, 1, NULL))
|
||||
return;
|
||||
|
||||
theirs = lookup_commit(sha1);
|
||||
ours = new->commit;
|
||||
if (!hashcmp(sha1, ours->object.sha1))
|
||||
return; /* we are the same */
|
||||
|
||||
/* Run "rev-list --left-right ours...theirs" internally... */
|
||||
rev_argc = 0;
|
||||
rev_argv[rev_argc++] = NULL;
|
||||
rev_argv[rev_argc++] = "--left-right";
|
||||
rev_argv[rev_argc++] = symmetric;
|
||||
rev_argv[rev_argc++] = "--";
|
||||
rev_argv[rev_argc] = NULL;
|
||||
|
||||
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
||||
strcpy(symmetric + 40, "...");
|
||||
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
||||
prepare_revision_walk(&revs);
|
||||
|
||||
/* ... and count the commits on each side. */
|
||||
num_ours = 0;
|
||||
num_theirs = 0;
|
||||
while (1) {
|
||||
struct commit *c = get_revision(&revs);
|
||||
if (!c)
|
||||
break;
|
||||
if (c->object.flags & SYMMETRIC_LEFT)
|
||||
num_ours++;
|
||||
else
|
||||
num_theirs++;
|
||||
}
|
||||
|
||||
if (!prefixcmp(base, "refs/remotes/")) {
|
||||
remote_msg = " remote";
|
||||
base += strlen("refs/remotes/");
|
||||
} else {
|
||||
remote_msg = "";
|
||||
}
|
||||
|
||||
if (!num_theirs)
|
||||
printf("Your branch is ahead of the tracked%s branch '%s' "
|
||||
"by %d commit%s.\n",
|
||||
remote_msg, base,
|
||||
num_ours, (num_ours == 1) ? "" : "s");
|
||||
else if (!num_ours)
|
||||
printf("Your branch is behind the tracked%s branch '%s' "
|
||||
"by %d commit%s,\n"
|
||||
"and can be fast-forwarded.\n",
|
||||
remote_msg, base,
|
||||
num_theirs, (num_theirs == 1) ? "" : "s");
|
||||
else
|
||||
printf("Your branch and the tracked%s branch '%s' "
|
||||
"have diverged,\nand respectively "
|
||||
"have %d and %d different commit(s) each.\n",
|
||||
remote_msg, base,
|
||||
num_ours, num_theirs);
|
||||
}
|
||||
|
||||
static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
struct branch_info *old,
|
||||
struct branch_info *new)
|
||||
{
|
||||
struct strbuf msg;
|
||||
const char *old_desc;
|
||||
if (opts->new_branch) {
|
||||
create_branch(old->name, opts->new_branch, new->name, 0,
|
||||
opts->new_branch_log, opts->track);
|
||||
new->name = opts->new_branch;
|
||||
setup_branch_path(new);
|
||||
}
|
||||
|
||||
strbuf_init(&msg, 0);
|
||||
old_desc = old->name;
|
||||
if (!old_desc)
|
||||
old_desc = sha1_to_hex(old->commit->object.sha1);
|
||||
strbuf_addf(&msg, "checkout: moving from %s to %s",
|
||||
old_desc, new->name);
|
||||
|
||||
if (new->path) {
|
||||
create_symref("HEAD", new->path, msg.buf);
|
||||
if (!opts->quiet) {
|
||||
if (old->path && !strcmp(new->path, old->path))
|
||||
fprintf(stderr, "Already on \"%s\"\n",
|
||||
new->name);
|
||||
else
|
||||
fprintf(stderr, "Switched to%s branch \"%s\"\n",
|
||||
opts->new_branch ? " a new" : "",
|
||||
new->name);
|
||||
}
|
||||
} else if (strcmp(new->name, "HEAD")) {
|
||||
update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
|
||||
REF_NODEREF, DIE_ON_ERR);
|
||||
if (!opts->quiet) {
|
||||
if (old->path)
|
||||
fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b <new_branch_name>\n", new->name);
|
||||
describe_detached_head("HEAD is now at", new->commit);
|
||||
}
|
||||
}
|
||||
remove_branch_state();
|
||||
strbuf_release(&msg);
|
||||
if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
|
||||
report_tracking(new, opts);
|
||||
}
|
||||
|
||||
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
|
||||
{
|
||||
int ret = 0;
|
||||
struct branch_info old;
|
||||
unsigned char rev[20];
|
||||
int flag;
|
||||
memset(&old, 0, sizeof(old));
|
||||
old.path = resolve_ref("HEAD", rev, 0, &flag);
|
||||
old.commit = lookup_commit_reference_gently(rev, 1);
|
||||
if (!(flag & REF_ISSYMREF))
|
||||
old.path = NULL;
|
||||
|
||||
if (old.path && !prefixcmp(old.path, "refs/heads/"))
|
||||
old.name = old.path + strlen("refs/heads/");
|
||||
|
||||
if (!new->name) {
|
||||
new->name = "HEAD";
|
||||
new->commit = old.commit;
|
||||
if (!new->commit)
|
||||
die("You are on a branch yet to be born");
|
||||
parse_commit(new->commit);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the new thing isn't a branch and isn't HEAD and we're
|
||||
* not starting a new branch, and we want messages, and we
|
||||
* weren't on a branch, and we're moving to a new commit,
|
||||
* describe the old commit.
|
||||
*/
|
||||
if (!new->path && strcmp(new->name, "HEAD") && !opts->new_branch &&
|
||||
!opts->quiet && !old.path && new->commit != old.commit)
|
||||
describe_detached_head("Previous HEAD position was", old.commit);
|
||||
|
||||
if (!old.commit) {
|
||||
if (!opts->quiet) {
|
||||
fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
|
||||
fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
|
||||
}
|
||||
opts->force = 1;
|
||||
}
|
||||
|
||||
ret = merge_working_tree(opts, &old, new);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
update_refs_for_switch(opts, &old, new);
|
||||
|
||||
return post_checkout_hook(old.commit, new->commit, 1);
|
||||
}
|
||||
|
||||
static int git_checkout_config(const char *var, const char *value)
|
||||
{
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int cmd_checkout(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct checkout_opts opts;
|
||||
unsigned char rev[20];
|
||||
const char *arg;
|
||||
struct branch_info new;
|
||||
struct tree *source_tree = NULL;
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&opts.quiet),
|
||||
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
|
||||
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
|
||||
OPT_SET_INT( 0 , "track", &opts.track, "track",
|
||||
BRANCH_TRACK_EXPLICIT),
|
||||
OPT_BOOLEAN('f', NULL, &opts.force, "force"),
|
||||
OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
memset(&new, 0, sizeof(new));
|
||||
|
||||
git_config(git_checkout_config);
|
||||
|
||||
opts.track = git_branch_track;
|
||||
|
||||
argc = parse_options(argc, argv, options, checkout_usage, 0);
|
||||
if (argc) {
|
||||
arg = argv[0];
|
||||
if (get_sha1(arg, rev))
|
||||
;
|
||||
else if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
|
||||
new.name = arg;
|
||||
setup_branch_path(&new);
|
||||
if (resolve_ref(new.path, rev, 1, NULL))
|
||||
new.commit = lookup_commit_reference(rev);
|
||||
else
|
||||
new.path = NULL;
|
||||
parse_commit(new.commit);
|
||||
source_tree = new.commit->tree;
|
||||
argv++;
|
||||
argc--;
|
||||
} else if ((source_tree = parse_tree_indirect(rev))) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc && !strcmp(argv[0], "--")) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (!opts.new_branch && (opts.track != git_branch_track))
|
||||
die("git checkout: --track and --no-track require -b");
|
||||
|
||||
if (opts.force && opts.merge)
|
||||
die("git checkout: -f and -m are incompatible");
|
||||
|
||||
if (argc) {
|
||||
const char **pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
if (!pathspec)
|
||||
die("invalid path specification");
|
||||
|
||||
/* Checkout paths */
|
||||
if (opts.new_branch || opts.force || opts.merge) {
|
||||
if (argc == 1) {
|
||||
die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
|
||||
} else {
|
||||
die("git checkout: updating paths is incompatible with switching branches/forcing");
|
||||
}
|
||||
}
|
||||
|
||||
if (source_tree)
|
||||
read_tree_some(source_tree, pathspec);
|
||||
else
|
||||
read_cache();
|
||||
return checkout_paths(pathspec);
|
||||
}
|
||||
|
||||
if (new.name && !new.commit) {
|
||||
die("Cannot switch branch to a non-commit.");
|
||||
}
|
||||
|
||||
return switch_branches(&opts, &new);
|
||||
}
|
@ -205,7 +205,8 @@ static void create_base_index(void)
|
||||
die("failed to unpack HEAD tree object");
|
||||
parse_tree(tree);
|
||||
init_tree_desc(&t, tree->buffer, tree->size);
|
||||
unpack_trees(1, &t, &opts);
|
||||
if (unpack_trees(1, &t, &opts))
|
||||
exit(128); /* We've already reported the error, finish dying */
|
||||
}
|
||||
|
||||
static char *prepare_index(int argc, const char **argv, const char *prefix)
|
||||
|
@ -46,19 +46,34 @@ static void add_to_known_names(const char *path,
|
||||
|
||||
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
|
||||
int might_be_tag = !prefixcmp(path, "refs/tags/");
|
||||
struct commit *commit;
|
||||
struct object *object;
|
||||
int prio;
|
||||
unsigned char peeled[20];
|
||||
int is_tag, prio;
|
||||
|
||||
if (!commit)
|
||||
if (!all && !might_be_tag)
|
||||
return 0;
|
||||
object = parse_object(sha1);
|
||||
|
||||
if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
|
||||
commit = lookup_commit_reference_gently(peeled, 1);
|
||||
if (!commit)
|
||||
return 0;
|
||||
is_tag = !!hashcmp(sha1, commit->object.sha1);
|
||||
} else {
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
object = parse_object(sha1);
|
||||
if (!commit || !object)
|
||||
return 0;
|
||||
is_tag = object->type == OBJ_TAG;
|
||||
}
|
||||
|
||||
/* If --all, then any refs are used.
|
||||
* If --tags, then any tags are used.
|
||||
* Otherwise only annotated tags are used.
|
||||
*/
|
||||
if (!prefixcmp(path, "refs/tags/")) {
|
||||
if (object->type == OBJ_TAG) {
|
||||
if (might_be_tag) {
|
||||
if (is_tag) {
|
||||
prio = 2;
|
||||
if (pattern && fnmatch(pattern, path + 10, 0))
|
||||
prio = 0;
|
||||
@ -159,6 +174,8 @@ static void describe(const char *arg, int last_one)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!max_candidates)
|
||||
die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
|
||||
if (debug)
|
||||
fprintf(stderr, "searching to describe %s\n", arg);
|
||||
|
||||
@ -255,6 +272,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"),
|
||||
OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"),
|
||||
OPT__ABBREV(&abbrev),
|
||||
OPT_SET_INT(0, "exact-match", &max_candidates,
|
||||
"only output exact matches", 0),
|
||||
OPT_INTEGER(0, "candidates", &max_candidates,
|
||||
"consider <n> most recent tags (default: 10)"),
|
||||
OPT_STRING(0, "match", &pattern, "pattern",
|
||||
@ -263,8 +282,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, describe_usage, 0);
|
||||
if (max_candidates < 1)
|
||||
max_candidates = 1;
|
||||
if (max_candidates < 0)
|
||||
max_candidates = 0;
|
||||
else if (max_candidates > MAX_TAGS)
|
||||
max_candidates = MAX_TAGS;
|
||||
|
||||
|
@ -44,12 +44,17 @@ static void stuff_change(struct diff_options *opt,
|
||||
tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
|
||||
tmp_c = old_name; old_name = new_name; new_name = tmp_c;
|
||||
}
|
||||
|
||||
if (opt->prefix &&
|
||||
(strncmp(old_name, opt->prefix, opt->prefix_length) ||
|
||||
strncmp(new_name, opt->prefix, opt->prefix_length)))
|
||||
return;
|
||||
|
||||
one = alloc_filespec(old_name);
|
||||
two = alloc_filespec(new_name);
|
||||
fill_filespec(one, old_sha1, old_mode);
|
||||
fill_filespec(two, new_sha1, new_mode);
|
||||
|
||||
/* NEEDSWORK: shouldn't this part of diffopt??? */
|
||||
diff_queue(&diff_queued_diff, one, two);
|
||||
}
|
||||
|
||||
@ -246,6 +251,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
if (diff_setup_done(&rev.diffopt) < 0)
|
||||
die("diff_setup_done failed");
|
||||
}
|
||||
if (rev.diffopt.prefix && nongit) {
|
||||
rev.diffopt.prefix = NULL;
|
||||
rev.diffopt.prefix_length = 0;
|
||||
}
|
||||
DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
|
||||
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
||||
|
||||
|
@ -123,7 +123,7 @@ static void show_filemodify(struct diff_queue_struct *q,
|
||||
printf("D %s\n", spec->path);
|
||||
else {
|
||||
struct object *object = lookup_object(spec->sha1);
|
||||
printf("M 0%06o :%d %s\n", spec->mode,
|
||||
printf("M %06o :%d %s\n", spec->mode,
|
||||
get_object_mark(object), spec->path);
|
||||
}
|
||||
}
|
||||
@ -196,8 +196,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
|
||||
? strlen(reencoded) : message
|
||||
? strlen(message) : 0),
|
||||
reencoded ? reencoded : message ? message : "");
|
||||
if (reencoded)
|
||||
free(reencoded);
|
||||
free(reencoded);
|
||||
|
||||
for (i = 0, p = commit->parents; p; p = p->next) {
|
||||
int mark = get_object_mark(&p->item->object);
|
||||
|
@ -538,8 +538,10 @@ static int get_pack(int xd[2], char **pack_lockfile)
|
||||
cmd.git_cmd = 1;
|
||||
if (start_command(&cmd))
|
||||
die("fetch-pack: unable to fork off %s", argv[0]);
|
||||
if (do_keep && pack_lockfile)
|
||||
if (do_keep && pack_lockfile) {
|
||||
*pack_lockfile = index_pack_lockfile(cmd.out);
|
||||
close(cmd.out);
|
||||
}
|
||||
|
||||
if (finish_command(&cmd))
|
||||
die("%s failed", argv[0]);
|
||||
|
@ -165,7 +165,7 @@ static int verify_format(const char *format)
|
||||
for (cp = format; *cp && (sp = find_next(cp)); ) {
|
||||
const char *ep = strchr(sp, ')');
|
||||
if (!ep)
|
||||
return error("malformatted format string %s", sp);
|
||||
return error("malformed format string %s", sp);
|
||||
/* sp points at "%(" and ep points at the closing ")" */
|
||||
parse_atom(sp + 2, ep);
|
||||
cp = ep + 1;
|
||||
|
@ -80,8 +80,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
|
||||
|
||||
walker_free(walker);
|
||||
|
||||
if (rewritten_url)
|
||||
free(rewritten_url);
|
||||
free(rewritten_url);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -29,27 +29,6 @@ static void safe_create_dir(const char *dir, int share)
|
||||
die("Could not make %s writable by group\n", dir);
|
||||
}
|
||||
|
||||
static int copy_file(const char *dst, const char *src, int mode)
|
||||
{
|
||||
int fdi, fdo, status;
|
||||
|
||||
mode = (mode & 0111) ? 0777 : 0666;
|
||||
if ((fdi = open(src, O_RDONLY)) < 0)
|
||||
return fdi;
|
||||
if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
|
||||
close(fdi);
|
||||
return fdo;
|
||||
}
|
||||
status = copy_fd(fdi, fdo);
|
||||
if (close(fdo) != 0)
|
||||
return error("%s: write error: %s", dst, strerror(errno));
|
||||
|
||||
if (!status && adjust_shared_perm(dst))
|
||||
return -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void copy_templates_1(char *path, int baselen,
|
||||
char *template, int template_baselen,
|
||||
DIR *dir)
|
||||
|
347
builtin-log.c
347
builtin-log.c
@ -15,6 +15,8 @@
|
||||
#include "reflog-walk.h"
|
||||
#include "patch-ids.h"
|
||||
#include "refs.h"
|
||||
#include "run-command.h"
|
||||
#include "shortlog.h"
|
||||
|
||||
static int default_show_root = 1;
|
||||
static const char *fmt_patch_subject_prefix = "PATCH";
|
||||
@ -428,24 +430,47 @@ static int istitlechar(char c)
|
||||
(c >= '0' && c <= '9') || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
static char *extra_headers = NULL;
|
||||
static int extra_headers_size = 0;
|
||||
static const char *fmt_patch_suffix = ".patch";
|
||||
static int numbered = 0;
|
||||
static int auto_number = 0;
|
||||
|
||||
static char **extra_hdr;
|
||||
static int extra_hdr_nr;
|
||||
static int extra_hdr_alloc;
|
||||
|
||||
static char **extra_to;
|
||||
static int extra_to_nr;
|
||||
static int extra_to_alloc;
|
||||
|
||||
static char **extra_cc;
|
||||
static int extra_cc_nr;
|
||||
static int extra_cc_alloc;
|
||||
|
||||
static void add_header(const char *value)
|
||||
{
|
||||
int len = strlen(value);
|
||||
while (value[len - 1] == '\n')
|
||||
len--;
|
||||
if (!strncasecmp(value, "to: ", 4)) {
|
||||
ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
|
||||
extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
|
||||
return;
|
||||
}
|
||||
if (!strncasecmp(value, "cc: ", 4)) {
|
||||
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
|
||||
extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
|
||||
return;
|
||||
}
|
||||
ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
|
||||
extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
|
||||
}
|
||||
|
||||
static int git_format_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "format.headers")) {
|
||||
int len;
|
||||
|
||||
if (!value)
|
||||
die("format.headers without value");
|
||||
len = strlen(value);
|
||||
extra_headers_size += len + 1;
|
||||
extra_headers = xrealloc(extra_headers, extra_headers_size);
|
||||
extra_headers[extra_headers_size - len - 1] = 0;
|
||||
strcat(extra_headers, value);
|
||||
add_header(value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "format.suffix")) {
|
||||
@ -470,74 +495,81 @@ static int git_format_config(const char *var, const char *value)
|
||||
}
|
||||
|
||||
|
||||
static FILE *realstdout = NULL;
|
||||
static const char *output_directory = NULL;
|
||||
|
||||
static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
|
||||
int numbered_files)
|
||||
static const char *get_oneline_for_filename(struct commit *commit,
|
||||
int keep_subject)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
static char filename[PATH_MAX];
|
||||
char *sol;
|
||||
int len = 0;
|
||||
int suffix_len = strlen(fmt_patch_suffix) + 1;
|
||||
|
||||
sol = strstr(commit->buffer, "\n\n");
|
||||
if (!sol)
|
||||
filename[0] = '\0';
|
||||
else {
|
||||
int j, space = 0;
|
||||
|
||||
sol += 2;
|
||||
/* strip [PATCH] or [PATCH blabla] */
|
||||
if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
|
||||
char *eos = strchr(sol + 6, ']');
|
||||
if (eos) {
|
||||
while (isspace(*eos))
|
||||
eos++;
|
||||
sol = eos;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0;
|
||||
j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
|
||||
len < sizeof(filename) - suffix_len &&
|
||||
sol[j] && sol[j] != '\n';
|
||||
j++) {
|
||||
if (istitlechar(sol[j])) {
|
||||
if (space) {
|
||||
filename[len++] = '-';
|
||||
space = 0;
|
||||
}
|
||||
filename[len++] = sol[j];
|
||||
if (sol[j] == '.')
|
||||
while (sol[j + 1] == '.')
|
||||
j++;
|
||||
} else
|
||||
space = 1;
|
||||
}
|
||||
while (filename[len - 1] == '.'
|
||||
|| filename[len - 1] == '-')
|
||||
len--;
|
||||
filename[len] = '\0';
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
static FILE *realstdout = NULL;
|
||||
static const char *output_directory = NULL;
|
||||
|
||||
static int reopen_stdout(const char *oneline, int nr, int total)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
int len = 0;
|
||||
int suffix_len = strlen(fmt_patch_suffix) + 1;
|
||||
|
||||
if (output_directory) {
|
||||
if (strlen(output_directory) >=
|
||||
len = snprintf(filename, sizeof(filename), "%s",
|
||||
output_directory);
|
||||
if (len >=
|
||||
sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
|
||||
return error("name of output directory is too long");
|
||||
strlcpy(filename, output_directory, sizeof(filename) - suffix_len);
|
||||
len = strlen(filename);
|
||||
if (filename[len - 1] != '/')
|
||||
filename[len++] = '/';
|
||||
}
|
||||
|
||||
if (numbered_files) {
|
||||
sprintf(filename + len, "%d", nr);
|
||||
len = strlen(filename);
|
||||
|
||||
} else {
|
||||
sprintf(filename + len, "%04d", nr);
|
||||
len = strlen(filename);
|
||||
|
||||
sol = strstr(commit->buffer, "\n\n");
|
||||
if (sol) {
|
||||
int j, space = 1;
|
||||
|
||||
sol += 2;
|
||||
/* strip [PATCH] or [PATCH blabla] */
|
||||
if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
|
||||
char *eos = strchr(sol + 6, ']');
|
||||
if (eos) {
|
||||
while (isspace(*eos))
|
||||
eos++;
|
||||
sol = eos;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0;
|
||||
j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
|
||||
len < sizeof(filename) - suffix_len &&
|
||||
sol[j] && sol[j] != '\n';
|
||||
j++) {
|
||||
if (istitlechar(sol[j])) {
|
||||
if (space) {
|
||||
filename[len++] = '-';
|
||||
space = 0;
|
||||
}
|
||||
filename[len++] = sol[j];
|
||||
if (sol[j] == '.')
|
||||
while (sol[j + 1] == '.')
|
||||
j++;
|
||||
} else
|
||||
space = 1;
|
||||
}
|
||||
while (filename[len - 1] == '.'
|
||||
|| filename[len - 1] == '-')
|
||||
len--;
|
||||
filename[len] = 0;
|
||||
}
|
||||
if (len + suffix_len >= sizeof(filename))
|
||||
return error("Patch pathname too long");
|
||||
if (!oneline)
|
||||
len += sprintf(filename + len, "%d", nr);
|
||||
else {
|
||||
len += sprintf(filename + len, "%04d-", nr);
|
||||
len += snprintf(filename + len, sizeof(filename) - len - 1
|
||||
- suffix_len, "%s", oneline);
|
||||
strcpy(filename + len, fmt_patch_suffix);
|
||||
}
|
||||
|
||||
@ -594,16 +626,89 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
|
||||
o2->flags = flags2;
|
||||
}
|
||||
|
||||
static void gen_message_id(char *dest, unsigned int length, char *base)
|
||||
static void gen_message_id(struct rev_info *info, char *base)
|
||||
{
|
||||
const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
|
||||
const char *email_start = strrchr(committer, '<');
|
||||
const char *email_end = strrchr(committer, '>');
|
||||
if(!email_start || !email_end || email_start > email_end - 1)
|
||||
struct strbuf buf;
|
||||
if (!email_start || !email_end || email_start > email_end - 1)
|
||||
die("Could not extract email from committer identity.");
|
||||
snprintf(dest, length, "%s.%lu.git.%.*s", base,
|
||||
(unsigned long) time(NULL),
|
||||
(int)(email_end - email_start - 1), email_start + 1);
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
|
||||
(unsigned long) time(NULL),
|
||||
(int)(email_end - email_start - 1), email_start + 1);
|
||||
info->message_id = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static void make_cover_letter(struct rev_info *rev, int use_stdout,
|
||||
int numbered, int numbered_files,
|
||||
struct commit *origin,
|
||||
int nr, struct commit **list, struct commit *head)
|
||||
{
|
||||
const char *committer;
|
||||
const char *origin_sha1, *head_sha1;
|
||||
const char *argv[7];
|
||||
const char *subject_start = NULL;
|
||||
const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
|
||||
const char *msg;
|
||||
const char *extra_headers = rev->extra_headers;
|
||||
struct shortlog log;
|
||||
struct strbuf sb;
|
||||
int i;
|
||||
const char *encoding = "utf-8";
|
||||
|
||||
if (rev->commit_format != CMIT_FMT_EMAIL)
|
||||
die("Cover letter needs email format");
|
||||
|
||||
if (!use_stdout && reopen_stdout(numbered_files ?
|
||||
NULL : "cover-letter", 0, rev->total))
|
||||
return;
|
||||
|
||||
head_sha1 = sha1_to_hex(head->object.sha1);
|
||||
|
||||
log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers);
|
||||
|
||||
committer = git_committer_info(0);
|
||||
|
||||
msg = body;
|
||||
strbuf_init(&sb, 0);
|
||||
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
|
||||
encoding);
|
||||
pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
|
||||
encoding, 0);
|
||||
pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
|
||||
printf("%s\n", sb.buf);
|
||||
|
||||
strbuf_release(&sb);
|
||||
|
||||
shortlog_init(&log);
|
||||
for (i = 0; i < nr; i++)
|
||||
shortlog_add_commit(&log, list[i]);
|
||||
|
||||
shortlog_output(&log);
|
||||
|
||||
/*
|
||||
* We can only do diffstat with a unique reference point
|
||||
*/
|
||||
if (!origin)
|
||||
return;
|
||||
|
||||
origin_sha1 = sha1_to_hex(origin->object.sha1);
|
||||
|
||||
argv[0] = "diff";
|
||||
argv[1] = "--stat";
|
||||
argv[2] = "--summary";
|
||||
argv[3] = head_sha1;
|
||||
argv[4] = "--not";
|
||||
argv[5] = origin_sha1;
|
||||
argv[6] = "--";
|
||||
argv[7] = NULL;
|
||||
fflush(stdout);
|
||||
run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
|
||||
fflush(stdout);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const char *clean_message_id(const char *msg_id)
|
||||
@ -641,11 +746,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
int subject_prefix = 0;
|
||||
int ignore_if_in_upstream = 0;
|
||||
int thread = 0;
|
||||
int cover_letter = 0;
|
||||
int boundary_count = 0;
|
||||
struct commit *origin = NULL, *head = NULL;
|
||||
const char *in_reply_to = NULL;
|
||||
struct patch_ids ids;
|
||||
char *add_signoff = NULL;
|
||||
char message_id[1024];
|
||||
char ref_message_id[1024];
|
||||
struct strbuf buf;
|
||||
|
||||
git_config(git_format_config);
|
||||
init_revisions(&rev, prefix);
|
||||
@ -658,7 +765,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
|
||||
|
||||
rev.subject_prefix = fmt_patch_subject_prefix;
|
||||
rev.extra_headers = extra_headers;
|
||||
|
||||
/*
|
||||
* Parse the arguments before setup_revisions(), or something
|
||||
@ -686,6 +792,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
die("Need a number for --start-number");
|
||||
start_number = strtol(argv[i], NULL, 10);
|
||||
}
|
||||
else if (!prefixcmp(argv[i], "--cc=")) {
|
||||
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
|
||||
extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-k") ||
|
||||
!strcmp(argv[i], "--keep-subject")) {
|
||||
keep_subject = 1;
|
||||
@ -742,11 +852,44 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
rev.subject_prefix = argv[i] + 17;
|
||||
} else if (!prefixcmp(argv[i], "--suffix="))
|
||||
fmt_patch_suffix = argv[i] + 9;
|
||||
else if (!strcmp(argv[i], "--cover-letter"))
|
||||
cover_letter = 1;
|
||||
else
|
||||
argv[j++] = argv[i];
|
||||
}
|
||||
argc = j;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
|
||||
for (i = 0; i < extra_hdr_nr; i++) {
|
||||
strbuf_addstr(&buf, extra_hdr[i]);
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
if (extra_to_nr)
|
||||
strbuf_addstr(&buf, "To: ");
|
||||
for (i = 0; i < extra_to_nr; i++) {
|
||||
if (i)
|
||||
strbuf_addstr(&buf, " ");
|
||||
strbuf_addstr(&buf, extra_to[i]);
|
||||
if (i + 1 < extra_to_nr)
|
||||
strbuf_addch(&buf, ',');
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
if (extra_cc_nr)
|
||||
strbuf_addstr(&buf, "Cc: ");
|
||||
for (i = 0; i < extra_cc_nr; i++) {
|
||||
if (i)
|
||||
strbuf_addstr(&buf, " ");
|
||||
strbuf_addstr(&buf, extra_cc[i]);
|
||||
if (i + 1 < extra_cc_nr)
|
||||
strbuf_addch(&buf, ',');
|
||||
strbuf_addch(&buf, '\n');
|
||||
}
|
||||
|
||||
rev.extra_headers = strbuf_detach(&buf, 0);
|
||||
|
||||
if (start_number < 0)
|
||||
start_number = 1;
|
||||
if (numbered && keep_subject)
|
||||
@ -793,6 +936,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
* get_revision() to do the usual traversal.
|
||||
*/
|
||||
}
|
||||
if (cover_letter) {
|
||||
/* remember the range */
|
||||
int i;
|
||||
for (i = 0; i < rev.pending.nr; i++) {
|
||||
struct object *o = rev.pending.objects[i].item;
|
||||
if (!(o->flags & UNINTERESTING))
|
||||
head = (struct commit *)o;
|
||||
}
|
||||
/* We can't generate a cover letter without any patches */
|
||||
if (!head)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ignore_if_in_upstream)
|
||||
get_patch_ids(&rev, &ids, prefix);
|
||||
@ -802,7 +957,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (prepare_revision_walk(&rev))
|
||||
die("revision walk setup failed");
|
||||
rev.boundary = 1;
|
||||
while ((commit = get_revision(&rev)) != NULL) {
|
||||
if (commit->object.flags & BOUNDARY) {
|
||||
boundary_count++;
|
||||
origin = (boundary_count == 1) ? commit : NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore merges */
|
||||
if (commit->parents && commit->parents->next)
|
||||
continue;
|
||||
@ -820,29 +982,42 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
numbered = 1;
|
||||
if (numbered)
|
||||
rev.total = total + start_number - 1;
|
||||
rev.add_signoff = add_signoff;
|
||||
if (in_reply_to)
|
||||
rev.ref_message_id = clean_message_id(in_reply_to);
|
||||
if (cover_letter) {
|
||||
if (thread)
|
||||
gen_message_id(&rev, "cover");
|
||||
make_cover_letter(&rev, use_stdout, numbered, numbered_files,
|
||||
origin, nr, list, head);
|
||||
total++;
|
||||
start_number--;
|
||||
}
|
||||
rev.add_signoff = add_signoff;
|
||||
while (0 <= --nr) {
|
||||
int shown;
|
||||
commit = list[nr];
|
||||
rev.nr = total - nr + (start_number - 1);
|
||||
/* Make the second and subsequent mails replies to the first */
|
||||
if (thread) {
|
||||
if (nr == (total - 2)) {
|
||||
strncpy(ref_message_id, message_id,
|
||||
sizeof(ref_message_id));
|
||||
ref_message_id[sizeof(ref_message_id)-1]='\0';
|
||||
rev.ref_message_id = ref_message_id;
|
||||
/* Have we already had a message ID? */
|
||||
if (rev.message_id) {
|
||||
/*
|
||||
* If we've got the ID to be a reply
|
||||
* to, discard the current ID;
|
||||
* otherwise, make everything a reply
|
||||
* to that.
|
||||
*/
|
||||
if (rev.ref_message_id)
|
||||
free(rev.message_id);
|
||||
else
|
||||
rev.ref_message_id = rev.message_id;
|
||||
}
|
||||
gen_message_id(message_id, sizeof(message_id),
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
rev.message_id = message_id;
|
||||
gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
|
||||
}
|
||||
if (!use_stdout)
|
||||
if (reopen_stdout(commit, rev.nr, keep_subject,
|
||||
numbered_files))
|
||||
die("Failed to create output files");
|
||||
if (!use_stdout && reopen_stdout(numbered_files ? NULL :
|
||||
get_oneline_for_filename(commit, keep_subject),
|
||||
rev.nr, rev.total))
|
||||
die("Failed to create output files");
|
||||
shown = log_tree_commit(&rev, commit);
|
||||
free(commit->buffer);
|
||||
commit->buffer = NULL;
|
||||
|
@ -46,7 +46,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
|
||||
&xpp, XDL_MERGE_ZEALOUS, &result);
|
||||
&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
free(mmfs[i].ptr);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "cache-tree.h"
|
||||
#include "commit.h"
|
||||
#include "blob.h"
|
||||
#include "builtin.h"
|
||||
#include "tree-walk.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
@ -17,6 +18,7 @@
|
||||
#include "xdiff-interface.h"
|
||||
#include "interpolate.h"
|
||||
#include "attr.h"
|
||||
#include "merge-recursive.h"
|
||||
|
||||
static int subtree_merge;
|
||||
|
||||
@ -221,22 +223,11 @@ static int git_merge_trees(int index_only,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int unmerged_index(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tree *git_write_tree(void)
|
||||
struct tree *write_tree_from_memory(void)
|
||||
{
|
||||
struct tree *result = NULL;
|
||||
|
||||
if (unmerged_index()) {
|
||||
if (unmerged_cache()) {
|
||||
int i;
|
||||
output(0, "There are unmerged index entries:");
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
@ -1496,12 +1487,12 @@ static int process_entry(const char *path, struct stage_data *entry,
|
||||
return clean_merge;
|
||||
}
|
||||
|
||||
static int merge_trees(struct tree *head,
|
||||
struct tree *merge,
|
||||
struct tree *common,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct tree **result)
|
||||
int merge_trees(struct tree *head,
|
||||
struct tree *merge,
|
||||
struct tree *common,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct tree **result)
|
||||
{
|
||||
int code, clean;
|
||||
|
||||
@ -1523,7 +1514,7 @@ static int merge_trees(struct tree *head,
|
||||
sha1_to_hex(head->object.sha1),
|
||||
sha1_to_hex(merge->object.sha1));
|
||||
|
||||
if (unmerged_index()) {
|
||||
if (unmerged_cache()) {
|
||||
struct path_list *entries, *re_head, *re_merge;
|
||||
int i;
|
||||
path_list_clear(¤t_file_set, 1);
|
||||
@ -1553,7 +1544,7 @@ static int merge_trees(struct tree *head,
|
||||
clean = 1;
|
||||
|
||||
if (index_only)
|
||||
*result = git_write_tree();
|
||||
*result = write_tree_from_memory();
|
||||
|
||||
return clean;
|
||||
}
|
||||
@ -1573,12 +1564,12 @@ static struct commit_list *reverse_commit_list(struct commit_list *list)
|
||||
* Merge the commits h1 and h2, return the resulting virtual
|
||||
* commit object and a flag indicating the cleanness of the merge.
|
||||
*/
|
||||
static int merge(struct commit *h1,
|
||||
struct commit *h2,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct commit_list *ca,
|
||||
struct commit **result)
|
||||
int merge_recursive(struct commit *h1,
|
||||
struct commit *h2,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct commit_list *ca,
|
||||
struct commit **result)
|
||||
{
|
||||
struct commit_list *iter;
|
||||
struct commit *merged_common_ancestors;
|
||||
@ -1623,11 +1614,11 @@ static int merge(struct commit *h1,
|
||||
* "conflicts" were already resolved.
|
||||
*/
|
||||
discard_cache();
|
||||
merge(merged_common_ancestors, iter->item,
|
||||
"Temporary merge branch 1",
|
||||
"Temporary merge branch 2",
|
||||
NULL,
|
||||
&merged_common_ancestors);
|
||||
merge_recursive(merged_common_ancestors, iter->item,
|
||||
"Temporary merge branch 1",
|
||||
"Temporary merge branch 2",
|
||||
NULL,
|
||||
&merged_common_ancestors);
|
||||
call_depth--;
|
||||
|
||||
if (!merged_common_ancestors)
|
||||
@ -1698,7 +1689,7 @@ static int merge_config(const char *var, const char *value)
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
static const char *bases[20];
|
||||
static unsigned bases_count = 0;
|
||||
@ -1752,7 +1743,7 @@ int main(int argc, char *argv[])
|
||||
struct commit *ancestor = get_ref(bases[i]);
|
||||
ca = commit_list_insert(ancestor, &ca);
|
||||
}
|
||||
clean = merge(h1, h2, branch1, branch2, ca, &result);
|
||||
clean = merge_recursive(h1, h2, branch1, branch2, ca, &result);
|
||||
|
||||
if (active_cache_changed &&
|
||||
(write_cache(index_fd, active_cache, active_nr) ||
|
@ -16,6 +16,7 @@
|
||||
#include "progress.h"
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
#include "thread-utils.h"
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
@ -1428,8 +1429,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
|
||||
* accounting lock. Compiler will optimize the strangeness
|
||||
* away when THREADED_DELTA_SEARCH is not defined.
|
||||
*/
|
||||
if (trg_entry->delta_data)
|
||||
free(trg_entry->delta_data);
|
||||
free(trg_entry->delta_data);
|
||||
cache_lock();
|
||||
if (trg_entry->delta_data) {
|
||||
delta_cache_size -= trg_entry->delta_size;
|
||||
@ -1852,11 +1852,11 @@ static int git_pack_config(const char *k, const char *v)
|
||||
}
|
||||
if (!strcmp(k, "pack.threads")) {
|
||||
delta_search_threads = git_config_int(k, v);
|
||||
if (delta_search_threads < 1)
|
||||
if (delta_search_threads < 0)
|
||||
die("invalid number of threads specified (%d)",
|
||||
delta_search_threads);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
if (delta_search_threads != 1)
|
||||
warning("no threads support, ignoring %s", k);
|
||||
#endif
|
||||
return 0;
|
||||
@ -2122,10 +2122,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
if (!prefixcmp(arg, "--threads=")) {
|
||||
char *end;
|
||||
delta_search_threads = strtoul(arg+10, &end, 0);
|
||||
if (!arg[10] || *end || delta_search_threads < 1)
|
||||
if (!arg[10] || *end || delta_search_threads < 0)
|
||||
usage(pack_usage);
|
||||
#ifndef THREADED_DELTA_SEARCH
|
||||
if (delta_search_threads > 1)
|
||||
if (delta_search_threads != 1)
|
||||
warning("no threads support, "
|
||||
"ignoring %s", arg);
|
||||
#endif
|
||||
@ -2235,6 +2235,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
||||
if (!pack_to_stdout && thin)
|
||||
die("--thin cannot be used to build an indexable pack.");
|
||||
|
||||
#ifdef THREADED_DELTA_SEARCH
|
||||
if (!delta_search_threads) /* --threads=0 means autodetect */
|
||||
delta_search_threads = online_cpus();
|
||||
#endif
|
||||
|
||||
prepare_packed_git();
|
||||
|
||||
if (progress)
|
||||
|
@ -44,15 +44,6 @@ static void set_refspecs(const char **refs, int nr)
|
||||
strcat(tag, refs[i]);
|
||||
ref = tag;
|
||||
}
|
||||
if (!strcmp("HEAD", ref)) {
|
||||
unsigned char sha1_dummy[20];
|
||||
ref = resolve_ref(ref, sha1_dummy, 1, NULL);
|
||||
if (!ref)
|
||||
die("HEAD cannot be resolved.");
|
||||
if (prefixcmp(ref, "refs/heads/"))
|
||||
die("HEAD cannot be resolved to branch.");
|
||||
ref = xstrdup(ref + 11);
|
||||
}
|
||||
add_refspec(ref);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ static int read_cache_unmerged(void)
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce)) {
|
||||
remove_index_entry(ce);
|
||||
if (last && !strcmp(ce->name, last->name))
|
||||
continue;
|
||||
cache_tree_invalidate_path(active_cache_tree, ce->name);
|
||||
@ -268,7 +269,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
parse_tree(tree);
|
||||
init_tree_desc(t+i, tree->buffer, tree->size);
|
||||
}
|
||||
unpack_trees(nr_trees, t, &opts);
|
||||
if (unpack_trees(nr_trees, t, &opts))
|
||||
return 128;
|
||||
|
||||
/*
|
||||
* When reading only one tree (either the most basic form,
|
||||
|
@ -267,23 +267,6 @@ static int diff_two(const char *file1, const char *label1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_file(const char *src, const char *dest)
|
||||
{
|
||||
FILE *in, *out;
|
||||
char buffer[32768];
|
||||
int count;
|
||||
|
||||
if (!(in = fopen(src, "r")))
|
||||
return error("Could not open %s", src);
|
||||
if (!(out = fopen(dest, "w")))
|
||||
return error("Could not open %s", dest);
|
||||
while ((count = fread(buffer, 1, sizeof(buffer), in)))
|
||||
fwrite(buffer, 1, count, out);
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_plain_rerere(struct path_list *rr, int fd)
|
||||
{
|
||||
struct path_list conflict = { NULL, 0, 0, 1 };
|
||||
@ -343,7 +326,7 @@ static int do_plain_rerere(struct path_list *rr, int fd)
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "Recorded resolution for '%s'.\n", path);
|
||||
copy_file(path, rr_path(name, "postimage"));
|
||||
copy_file(rr_path(name, "postimage"), path, 0666);
|
||||
tail_optimization:
|
||||
if (i < rr->nr - 1)
|
||||
memmove(rr->items + i,
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "tree.h"
|
||||
#include "branch.h"
|
||||
|
||||
static const char builtin_reset_usage[] =
|
||||
"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
|
||||
@ -44,18 +45,6 @@ static inline int is_merge(void)
|
||||
return !access(git_path("MERGE_HEAD"), F_OK);
|
||||
}
|
||||
|
||||
static int unmerged_files(void)
|
||||
{
|
||||
int i;
|
||||
read_cache();
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
|
||||
{
|
||||
int i = 0;
|
||||
@ -250,7 +239,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
* at all, but requires them in a good order. Other resets reset
|
||||
* the index file to the tree object we are switching to. */
|
||||
if (reset_type == SOFT) {
|
||||
if (is_merge() || unmerged_files())
|
||||
if (is_merge() || read_cache() < 0 || unmerged_cache())
|
||||
die("Cannot do a soft reset in the middle of a merge.");
|
||||
}
|
||||
else if (reset_index_file(sha1, (reset_type == HARD)))
|
||||
@ -282,10 +271,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
break;
|
||||
}
|
||||
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
unlink(git_path("SQUASH_MSG"));
|
||||
remove_branch_state();
|
||||
|
||||
free(reflog_action);
|
||||
|
||||
|
@ -315,7 +315,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
|
||||
s = strchr(sb.buf, ' ');
|
||||
if (!s || *sb.buf == ' ') {
|
||||
o->type = OPTION_GROUP;
|
||||
o->help = xstrdup(skipspaces(s));
|
||||
o->help = xstrdup(skipspaces(sb.buf));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -397,8 +397,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
else
|
||||
return execl_git_cmd("commit", "-n", "-F", defmsg, NULL);
|
||||
}
|
||||
if (reencoded_message)
|
||||
free(reencoded_message);
|
||||
free(reencoded_message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ static int pack_objects(int fd, struct ref *refs)
|
||||
refs = refs->next;
|
||||
}
|
||||
|
||||
close(po.in);
|
||||
if (finish_command(&po))
|
||||
return error("pack-objects died with strange error");
|
||||
return 0;
|
||||
@ -403,12 +404,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
|
||||
if (!remote_tail)
|
||||
remote_tail = &remote_refs;
|
||||
if (match_refs(local_refs, remote_refs, &remote_tail,
|
||||
nr_refspec, refspec, flags))
|
||||
nr_refspec, refspec, flags)) {
|
||||
close(out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!remote_refs) {
|
||||
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
|
||||
"Perhaps you should specify a branch such as 'master'.\n");
|
||||
close(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -495,12 +499,11 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
|
||||
|
||||
packet_flush(out);
|
||||
if (new_refs && !args.dry_run) {
|
||||
if (pack_objects(out, remote_refs) < 0) {
|
||||
close(out);
|
||||
if (pack_objects(out, remote_refs) < 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
close(out);
|
||||
else
|
||||
close(out);
|
||||
|
||||
if (expect_status_report)
|
||||
ret = receive_status(in, remote_refs);
|
||||
@ -648,7 +651,7 @@ int send_pack(struct send_pack_args *my_args,
|
||||
conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
|
||||
ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
/* do_send_pack always closes fd[1] */
|
||||
ret |= finish_connect(conn);
|
||||
return !!ret;
|
||||
}
|
||||
|
@ -6,13 +6,11 @@
|
||||
#include "revision.h"
|
||||
#include "utf8.h"
|
||||
#include "mailmap.h"
|
||||
#include "shortlog.h"
|
||||
|
||||
static const char shortlog_usage[] =
|
||||
"git-shortlog [-n] [-s] [-e] [<commit-id>... ]";
|
||||
|
||||
static char *common_repo_prefix;
|
||||
static int email;
|
||||
|
||||
static int compare_by_number(const void *a1, const void *a2)
|
||||
{
|
||||
const struct path_list_item *i1 = a1, *i2 = a2;
|
||||
@ -26,13 +24,11 @@ static int compare_by_number(const void *a1, const void *a2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct path_list mailmap = {NULL, 0, 0, 0};
|
||||
|
||||
static void insert_one_record(struct path_list *list,
|
||||
static void insert_one_record(struct shortlog *log,
|
||||
const char *author,
|
||||
const char *oneline)
|
||||
{
|
||||
const char *dot3 = common_repo_prefix;
|
||||
const char *dot3 = log->common_repo_prefix;
|
||||
char *buffer, *p;
|
||||
struct path_list_item *item;
|
||||
struct path_list *onelines;
|
||||
@ -47,7 +43,7 @@ static void insert_one_record(struct path_list *list,
|
||||
eoemail = strchr(boemail, '>');
|
||||
if (!eoemail)
|
||||
return;
|
||||
if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) {
|
||||
if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
|
||||
while (author < boemail && isspace(*author))
|
||||
author++;
|
||||
for (len = 0;
|
||||
@ -61,14 +57,14 @@ static void insert_one_record(struct path_list *list,
|
||||
else
|
||||
len = strlen(namebuf);
|
||||
|
||||
if (email) {
|
||||
if (log->email) {
|
||||
size_t room = sizeof(namebuf) - len - 1;
|
||||
int maillen = eoemail - boemail + 1;
|
||||
snprintf(namebuf + len, room, " %.*s", maillen, boemail);
|
||||
}
|
||||
|
||||
buffer = xstrdup(namebuf);
|
||||
item = path_list_insert(buffer, list);
|
||||
item = path_list_insert(buffer, &log->list);
|
||||
if (item->util == NULL)
|
||||
item->util = xcalloc(1, sizeof(struct path_list));
|
||||
else
|
||||
@ -114,7 +110,7 @@ static void insert_one_record(struct path_list *list,
|
||||
onelines->items[onelines->nr++].path = buffer;
|
||||
}
|
||||
|
||||
static void read_from_stdin(struct path_list *list)
|
||||
static void read_from_stdin(struct shortlog *log)
|
||||
{
|
||||
char author[1024], oneline[1024];
|
||||
|
||||
@ -128,39 +124,43 @@ static void read_from_stdin(struct path_list *list)
|
||||
while (fgets(oneline, sizeof(oneline), stdin) &&
|
||||
oneline[0] == '\n')
|
||||
; /* discard blanks */
|
||||
insert_one_record(list, author + 8, oneline);
|
||||
insert_one_record(log, author + 8, oneline);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_from_rev(struct rev_info *rev, struct path_list *list)
|
||||
void shortlog_add_commit(struct shortlog *log, struct commit *commit)
|
||||
{
|
||||
const char *author = NULL, *buffer;
|
||||
|
||||
buffer = commit->buffer;
|
||||
while (*buffer && *buffer != '\n') {
|
||||
const char *eol = strchr(buffer, '\n');
|
||||
|
||||
if (eol == NULL)
|
||||
eol = buffer + strlen(buffer);
|
||||
else
|
||||
eol++;
|
||||
|
||||
if (!prefixcmp(buffer, "author "))
|
||||
author = buffer + 7;
|
||||
buffer = eol;
|
||||
}
|
||||
if (!author)
|
||||
die("Missing author: %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
if (*buffer)
|
||||
buffer++;
|
||||
insert_one_record(log, author, !*buffer ? "<none>" : buffer);
|
||||
}
|
||||
|
||||
static void get_from_rev(struct rev_info *rev, struct shortlog *log)
|
||||
{
|
||||
struct commit *commit;
|
||||
|
||||
if (prepare_revision_walk(rev))
|
||||
die("revision walk setup failed");
|
||||
while ((commit = get_revision(rev)) != NULL) {
|
||||
const char *author = NULL, *buffer;
|
||||
|
||||
buffer = commit->buffer;
|
||||
while (*buffer && *buffer != '\n') {
|
||||
const char *eol = strchr(buffer, '\n');
|
||||
|
||||
if (eol == NULL)
|
||||
eol = buffer + strlen(buffer);
|
||||
else
|
||||
eol++;
|
||||
|
||||
if (!prefixcmp(buffer, "author "))
|
||||
author = buffer + 7;
|
||||
buffer = eol;
|
||||
}
|
||||
if (!author)
|
||||
die("Missing author: %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
if (*buffer)
|
||||
buffer++;
|
||||
insert_one_record(list, author, !*buffer ? "<none>" : buffer);
|
||||
}
|
||||
while ((commit = get_revision(rev)) != NULL)
|
||||
shortlog_add_commit(log, commit);
|
||||
}
|
||||
|
||||
static int parse_uint(char const **arg, int comma)
|
||||
@ -212,29 +212,38 @@ static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
|
||||
die(wrap_arg_usage);
|
||||
}
|
||||
|
||||
void shortlog_init(struct shortlog *log)
|
||||
{
|
||||
memset(log, 0, sizeof(*log));
|
||||
|
||||
read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
|
||||
|
||||
log->list.strdup_paths = 1;
|
||||
log->wrap = DEFAULT_WRAPLEN;
|
||||
log->in1 = DEFAULT_INDENT1;
|
||||
log->in2 = DEFAULT_INDENT2;
|
||||
}
|
||||
|
||||
int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct shortlog log;
|
||||
struct rev_info rev;
|
||||
struct path_list list = { NULL, 0, 0, 1 };
|
||||
int i, j, sort_by_number = 0, summary = 0;
|
||||
int wrap_lines = 0;
|
||||
int wrap = DEFAULT_WRAPLEN;
|
||||
int in1 = DEFAULT_INDENT1;
|
||||
int in2 = DEFAULT_INDENT2;
|
||||
|
||||
shortlog_init(&log);
|
||||
|
||||
/* since -n is a shadowed rev argument, parse our args first */
|
||||
while (argc > 1) {
|
||||
if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
|
||||
sort_by_number = 1;
|
||||
log.sort_by_number = 1;
|
||||
else if (!strcmp(argv[1], "-s") ||
|
||||
!strcmp(argv[1], "--summary"))
|
||||
summary = 1;
|
||||
log.summary = 1;
|
||||
else if (!strcmp(argv[1], "-e") ||
|
||||
!strcmp(argv[1], "--email"))
|
||||
email = 1;
|
||||
log.email = 1;
|
||||
else if (!prefixcmp(argv[1], "-w")) {
|
||||
wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &in1, &in2, &wrap);
|
||||
log.wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
|
||||
}
|
||||
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
|
||||
usage(shortlog_usage);
|
||||
@ -248,34 +257,38 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
if (argc > 1)
|
||||
die ("unrecognized argument: %s", argv[1]);
|
||||
|
||||
read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
|
||||
|
||||
/* assume HEAD if from a tty */
|
||||
if (!rev.pending.nr && isatty(0))
|
||||
add_head_to_pending(&rev);
|
||||
if (rev.pending.nr == 0) {
|
||||
read_from_stdin(&list);
|
||||
read_from_stdin(&log);
|
||||
}
|
||||
else
|
||||
get_from_rev(&rev, &list);
|
||||
get_from_rev(&rev, &log);
|
||||
|
||||
if (sort_by_number)
|
||||
qsort(list.items, list.nr, sizeof(struct path_list_item),
|
||||
shortlog_output(&log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shortlog_output(struct shortlog *log)
|
||||
{
|
||||
int i, j;
|
||||
if (log->sort_by_number)
|
||||
qsort(log->list.items, log->list.nr, sizeof(struct path_list_item),
|
||||
compare_by_number);
|
||||
for (i = 0; i < log->list.nr; i++) {
|
||||
struct path_list *onelines = log->list.items[i].util;
|
||||
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
struct path_list *onelines = list.items[i].util;
|
||||
|
||||
if (summary) {
|
||||
printf("%6d\t%s\n", onelines->nr, list.items[i].path);
|
||||
if (log->summary) {
|
||||
printf("%6d\t%s\n", onelines->nr, log->list.items[i].path);
|
||||
} else {
|
||||
printf("%s (%d):\n", list.items[i].path, onelines->nr);
|
||||
printf("%s (%d):\n", log->list.items[i].path, onelines->nr);
|
||||
for (j = onelines->nr - 1; j >= 0; j--) {
|
||||
const char *msg = onelines->items[j].path;
|
||||
|
||||
if (wrap_lines) {
|
||||
int col = print_wrapped_text(msg, in1, in2, wrap);
|
||||
if (col != wrap)
|
||||
if (log->wrap_lines) {
|
||||
int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
|
||||
if (col != log->wrap)
|
||||
putchar('\n');
|
||||
}
|
||||
else
|
||||
@ -287,13 +300,11 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
onelines->strdup_paths = 1;
|
||||
path_list_clear(onelines, 1);
|
||||
free(onelines);
|
||||
list.items[i].util = NULL;
|
||||
log->list.items[i].util = NULL;
|
||||
}
|
||||
|
||||
list.strdup_paths = 1;
|
||||
path_list_clear(&list, 1);
|
||||
mailmap.strdup_paths = 1;
|
||||
path_list_clear(&mailmap, 1);
|
||||
|
||||
return 0;
|
||||
log->list.strdup_paths = 1;
|
||||
path_list_clear(&log->list, 1);
|
||||
log->mailmap.strdup_paths = 1;
|
||||
path_list_clear(&log->mailmap, 1);
|
||||
}
|
||||
|
@ -226,12 +226,13 @@ static int do_sign(struct strbuf *buffer)
|
||||
|
||||
if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
|
||||
close(gpg.in);
|
||||
close(gpg.out);
|
||||
finish_command(&gpg);
|
||||
return error("gpg did not accept the tag data");
|
||||
}
|
||||
close(gpg.in);
|
||||
gpg.close_in = 0;
|
||||
len = strbuf_read(buffer, gpg.out, 1024);
|
||||
close(gpg.out);
|
||||
|
||||
if (finish_command(&gpg) || !len || len < 0)
|
||||
return error("gpg failed to sign the tag");
|
||||
|
@ -45,14 +45,12 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
|
||||
memset(&gpg, 0, sizeof(gpg));
|
||||
gpg.argv = args_gpg;
|
||||
gpg.in = -1;
|
||||
gpg.out = 1;
|
||||
args_gpg[2] = path;
|
||||
if (start_command(&gpg))
|
||||
return error("could not run gpg.");
|
||||
|
||||
write_in_full(gpg.in, buf, len);
|
||||
close(gpg.in);
|
||||
gpg.close_in = 0;
|
||||
ret = finish_command(&gpg);
|
||||
|
||||
unlink(path);
|
||||
|
@ -18,6 +18,7 @@ extern int cmd_blame(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_branch(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_bundle(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_checkout(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
|
||||
@ -56,6 +57,7 @@ extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mv(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
|
||||
|
6
bundle.c
6
bundle.c
@ -333,10 +333,12 @@ int create_bundle(struct bundle_header *header, const char *path,
|
||||
write_or_die(rls.in, sha1_to_hex(object->sha1), 40);
|
||||
write_or_die(rls.in, "\n", 1);
|
||||
}
|
||||
close(rls.in);
|
||||
if (finish_command(&rls))
|
||||
return error ("pack-objects died");
|
||||
|
||||
return bundle_to_stdout ? close(bundle_fd) : commit_lock_file(&lock);
|
||||
if (!bundle_to_stdout)
|
||||
commit_lock_file(&lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unbundle(struct bundle_header *header, int bundle_fd)
|
||||
|
52
cache.h
52
cache.h
@ -110,7 +110,6 @@ struct ondisk_cache_entry {
|
||||
};
|
||||
|
||||
struct cache_entry {
|
||||
struct cache_entry *next;
|
||||
unsigned int ce_ctime;
|
||||
unsigned int ce_mtime;
|
||||
unsigned int ce_dev;
|
||||
@ -121,6 +120,7 @@ struct cache_entry {
|
||||
unsigned int ce_size;
|
||||
unsigned int ce_flags;
|
||||
unsigned char sha1[20];
|
||||
struct cache_entry *next;
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
|
||||
@ -133,7 +133,39 @@ struct cache_entry {
|
||||
#define CE_UPDATE (0x10000)
|
||||
#define CE_REMOVE (0x20000)
|
||||
#define CE_UPTODATE (0x40000)
|
||||
#define CE_UNHASHED (0x80000)
|
||||
|
||||
#define CE_HASHED (0x100000)
|
||||
#define CE_UNHASHED (0x200000)
|
||||
|
||||
/*
|
||||
* Copy the sha1 and stat state of a cache entry from one to
|
||||
* another. But we never change the name, or the hash state!
|
||||
*/
|
||||
#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
|
||||
static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
|
||||
{
|
||||
unsigned int state = dst->ce_flags & CE_STATE_MASK;
|
||||
|
||||
/* Don't copy hash chain and name */
|
||||
memcpy(dst, src, offsetof(struct cache_entry, next));
|
||||
|
||||
/* Restore the hash state */
|
||||
dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't actually *remove* it, we can just mark it invalid so that
|
||||
* we won't find it in lookups.
|
||||
*
|
||||
* Not only would we have to search the lists (simple enough), but
|
||||
* we'd also have to rehash other hash buckets in case this makes the
|
||||
* hash bucket empty (common). So it's much better to just mark
|
||||
* it.
|
||||
*/
|
||||
static inline void remove_index_entry(struct cache_entry *ce)
|
||||
{
|
||||
ce->ce_flags |= CE_UNHASHED;
|
||||
}
|
||||
|
||||
static inline unsigned create_ce_flags(size_t len, unsigned stage)
|
||||
{
|
||||
@ -220,6 +252,7 @@ extern struct index_state the_index;
|
||||
#define read_cache_from(path) read_index_from(&the_index, (path))
|
||||
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
|
||||
#define discard_cache() discard_index(&the_index)
|
||||
#define unmerged_cache() unmerged_index(&the_index)
|
||||
#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
|
||||
#define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
|
||||
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
|
||||
@ -314,6 +347,7 @@ extern int read_index(struct index_state *);
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int write_index(struct index_state *, int newfd);
|
||||
extern int discard_index(struct index_state *);
|
||||
extern int unmerged_index(struct index_state *);
|
||||
extern int verify_path(const char *path);
|
||||
extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
|
||||
extern int index_name_pos(struct index_state *, const char *name, int namelen);
|
||||
@ -391,6 +425,15 @@ enum safe_crlf {
|
||||
|
||||
extern enum safe_crlf safe_crlf;
|
||||
|
||||
enum branch_track {
|
||||
BRANCH_TRACK_NEVER = 0,
|
||||
BRANCH_TRACK_REMOTE,
|
||||
BRANCH_TRACK_ALWAYS,
|
||||
BRANCH_TRACK_EXPLICIT,
|
||||
};
|
||||
|
||||
extern enum branch_track git_branch_track;
|
||||
|
||||
#define GIT_REPO_VERSION 0
|
||||
extern int repository_format_version;
|
||||
extern int check_repository_format(void);
|
||||
@ -666,6 +709,7 @@ extern const char *git_log_output_encoding;
|
||||
/* IO helper functions */
|
||||
extern void maybe_flush_or_die(FILE *, const char *);
|
||||
extern int copy_fd(int ifd, int ofd);
|
||||
extern int copy_file(const char *dst, const char *src, int mode);
|
||||
extern int read_in_full(int fd, void *buf, size_t count);
|
||||
extern int write_in_full(int fd, const void *buf, size_t count);
|
||||
extern void write_or_die(int fd, const void *buf, size_t count);
|
||||
@ -719,6 +763,7 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i
|
||||
#define WS_TRAILING_SPACE 01
|
||||
#define WS_SPACE_BEFORE_TAB 02
|
||||
#define WS_INDENT_WITH_NON_TAB 04
|
||||
#define WS_CR_AT_EOL 010
|
||||
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
|
||||
extern unsigned whitespace_rule_cfg;
|
||||
extern unsigned whitespace_rule(const char *);
|
||||
@ -727,10 +772,13 @@ extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
|
||||
FILE *stream, const char *set,
|
||||
const char *reset, const char *ws);
|
||||
extern char *whitespace_error_string(unsigned ws);
|
||||
extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
|
||||
|
||||
/* ls-files */
|
||||
int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
|
||||
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
|
||||
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
|
15
commit.h
15
commit.h
@ -71,6 +71,21 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
|
||||
int abbrev, const char *subject,
|
||||
const char *after_subject, enum date_mode,
|
||||
int non_ascii_present);
|
||||
void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding);
|
||||
void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int plain_non_ascii);
|
||||
void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent);
|
||||
|
||||
|
||||
/** Removes the first commit from a list sorted by date, and adds all
|
||||
* of its parents.
|
||||
|
8
config.c
8
config.c
@ -471,6 +471,14 @@ int git_default_config(const char *var, const char *value)
|
||||
whitespace_rule_cfg = parse_whitespace_rule(value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "branch.autosetupmerge")) {
|
||||
if (value && !strcasecmp(value, "always")) {
|
||||
git_branch_track = BRANCH_TRACK_ALWAYS;
|
||||
return 0;
|
||||
}
|
||||
git_branch_track = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
|
@ -68,8 +68,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
|
||||
|
||||
name_len = strlen(name);
|
||||
if (len != name_len + 41) {
|
||||
if (server_capabilities)
|
||||
free(server_capabilities);
|
||||
free(server_capabilities);
|
||||
server_capabilities = xstrdup(name + name_len + 1);
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,10 @@ __git_ps1 ()
|
||||
fi
|
||||
if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
|
||||
then
|
||||
b="$(cut -c1-7 $g/HEAD)..."
|
||||
if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
|
||||
then
|
||||
b="$(cut -c1-7 $g/HEAD)..."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -645,6 +648,7 @@ _git_format_patch ()
|
||||
--in-reply-to=
|
||||
--full-index --binary
|
||||
--not --all
|
||||
--cover-letter
|
||||
"
|
||||
return
|
||||
;;
|
||||
|
@ -728,7 +728,7 @@ Return the list of files that haven't been handled."
|
||||
(defun git-run-ls-files-with-excludes (status files default-state &rest options)
|
||||
"Run git-ls-files on FILES with appropriate --exclude-from options."
|
||||
(let ((exclude-files (git-get-exclude-files)))
|
||||
(apply #'git-run-ls-files status files default-state "--directory"
|
||||
(apply #'git-run-ls-files status files default-state "--directory" "--no-empty-directory"
|
||||
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
|
||||
(append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
|
||||
|
||||
|
@ -210,11 +210,14 @@ then
|
||||
git read-tree $v --reset -u $new
|
||||
else
|
||||
git update-index --refresh >/dev/null
|
||||
merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
|
||||
case "$merge" in
|
||||
'')
|
||||
echo >&2 "$merge_error"
|
||||
git read-tree $v -m -u --exclude-per-directory=.gitignore $old $new || (
|
||||
case "$merge,$v" in
|
||||
,*)
|
||||
exit 1 ;;
|
||||
1,)
|
||||
;; # quiet
|
||||
*)
|
||||
echo >&2 "Falling back to 3-way merge..." ;;
|
||||
esac
|
||||
|
||||
# Match the index to the working tree, and do a three-way.
|
@ -464,71 +464,47 @@ class P4Submit(Command):
|
||||
def __init__(self):
|
||||
Command.__init__(self)
|
||||
self.options = [
|
||||
optparse.make_option("--continue", action="store_false", dest="firstTime"),
|
||||
optparse.make_option("--verbose", dest="verbose", action="store_true"),
|
||||
optparse.make_option("--origin", dest="origin"),
|
||||
optparse.make_option("--reset", action="store_true", dest="reset"),
|
||||
optparse.make_option("--log-substitutions", dest="substFile"),
|
||||
optparse.make_option("--direct", dest="directSubmit", action="store_true"),
|
||||
optparse.make_option("-M", dest="detectRename", action="store_true"),
|
||||
]
|
||||
self.description = "Submit changes from git to the perforce depot."
|
||||
self.usage += " [name of git branch to submit into perforce depot]"
|
||||
self.firstTime = True
|
||||
self.reset = False
|
||||
self.interactive = True
|
||||
self.substFile = ""
|
||||
self.firstTime = True
|
||||
self.origin = ""
|
||||
self.directSubmit = False
|
||||
self.detectRename = False
|
||||
self.verbose = False
|
||||
self.isWindows = (platform.system() == "Windows")
|
||||
|
||||
self.logSubstitutions = {}
|
||||
self.logSubstitutions["<enter description here>"] = "%log%"
|
||||
self.logSubstitutions["\tDetails:"] = "\tDetails: %log%"
|
||||
|
||||
def check(self):
|
||||
if len(p4CmdList("opened ...")) > 0:
|
||||
die("You have files opened with perforce! Close them before starting the sync.")
|
||||
|
||||
def start(self):
|
||||
if len(self.config) > 0 and not self.reset:
|
||||
die("Cannot start sync. Previous sync config found at %s\n"
|
||||
"If you want to start submitting again from scratch "
|
||||
"maybe you want to call git-p4 submit --reset" % self.configFile)
|
||||
|
||||
commits = []
|
||||
if self.directSubmit:
|
||||
commits.append("0")
|
||||
else:
|
||||
for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
|
||||
commits.append(line.strip())
|
||||
commits.reverse()
|
||||
|
||||
self.config["commits"] = commits
|
||||
|
||||
# replaces everything between 'Description:' and the next P4 submit template field with the
|
||||
# commit message
|
||||
def prepareLogMessage(self, template, message):
|
||||
result = ""
|
||||
|
||||
inDescriptionSection = False
|
||||
|
||||
for line in template.split("\n"):
|
||||
if line.startswith("#"):
|
||||
result += line + "\n"
|
||||
continue
|
||||
|
||||
substituted = False
|
||||
for key in self.logSubstitutions.keys():
|
||||
if line.find(key) != -1:
|
||||
value = self.logSubstitutions[key]
|
||||
value = value.replace("%log%", message)
|
||||
if value != "@remove@":
|
||||
result += line.replace(key, value) + "\n"
|
||||
substituted = True
|
||||
break
|
||||
if inDescriptionSection:
|
||||
if line.startswith("Files:"):
|
||||
inDescriptionSection = False
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
if line.startswith("Description:"):
|
||||
inDescriptionSection = True
|
||||
line += "\n"
|
||||
for messageLine in message.split("\n"):
|
||||
line += "\t" + messageLine + "\n"
|
||||
|
||||
if not substituted:
|
||||
result += line + "\n"
|
||||
result += line + "\n"
|
||||
|
||||
return result
|
||||
|
||||
@ -557,13 +533,9 @@ class P4Submit(Command):
|
||||
return template
|
||||
|
||||
def applyCommit(self, id):
|
||||
if self.directSubmit:
|
||||
print "Applying local change in working directory/index"
|
||||
diff = self.diffStatus
|
||||
else:
|
||||
print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
|
||||
diffOpts = ("", "-M")[self.detectRename]
|
||||
diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
|
||||
print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
|
||||
diffOpts = ("", "-M")[self.detectRename]
|
||||
diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
|
||||
filesToAdd = set()
|
||||
filesToDelete = set()
|
||||
editedFiles = set()
|
||||
@ -598,10 +570,7 @@ class P4Submit(Command):
|
||||
else:
|
||||
die("unknown modifier %s for %s" % (modifier, path))
|
||||
|
||||
if self.directSubmit:
|
||||
diffcmd = "cat \"%s\"" % self.diffFile
|
||||
else:
|
||||
diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
|
||||
diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
|
||||
patchcmd = diffcmd + " | git apply "
|
||||
tryPatchCmd = patchcmd + "--check -"
|
||||
applyPatchCmd = patchcmd + "--check --apply -"
|
||||
@ -649,13 +618,10 @@ class P4Submit(Command):
|
||||
mode = filesToChangeExecBit[f]
|
||||
setP4ExecBit(f, mode)
|
||||
|
||||
logMessage = ""
|
||||
if not self.directSubmit:
|
||||
logMessage = extractLogMessageFromGitCommit(id)
|
||||
logMessage = logMessage.replace("\n", "\n\t")
|
||||
if self.isWindows:
|
||||
logMessage = logMessage.replace("\n", "\r\n")
|
||||
logMessage = logMessage.strip()
|
||||
logMessage = extractLogMessageFromGitCommit(id)
|
||||
if self.isWindows:
|
||||
logMessage = logMessage.replace("\n", "\r\n")
|
||||
logMessage = logMessage.strip()
|
||||
|
||||
template = self.prepareSubmitTemplate()
|
||||
|
||||
@ -694,12 +660,6 @@ class P4Submit(Command):
|
||||
if self.isWindows:
|
||||
submitTemplate = submitTemplate.replace("\r\n", "\n")
|
||||
|
||||
if self.directSubmit:
|
||||
print "Submitting to git first"
|
||||
os.chdir(self.oldWorkingDirectory)
|
||||
write_pipe("git commit -a -F -", submitTemplate)
|
||||
os.chdir(self.clientPath)
|
||||
|
||||
write_pipe("p4 submit -i", submitTemplate)
|
||||
else:
|
||||
fileName = "submit.txt"
|
||||
@ -741,65 +701,33 @@ class P4Submit(Command):
|
||||
print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
|
||||
self.oldWorkingDirectory = os.getcwd()
|
||||
|
||||
if self.directSubmit:
|
||||
self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD")
|
||||
if len(self.diffStatus) == 0:
|
||||
print "No changes in working directory to submit."
|
||||
return True
|
||||
patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD")
|
||||
self.diffFile = self.gitdir + "/p4-git-diff"
|
||||
f = open(self.diffFile, "wb")
|
||||
f.write(patch)
|
||||
f.close();
|
||||
|
||||
os.chdir(self.clientPath)
|
||||
print "Syncronizing p4 checkout..."
|
||||
system("p4 sync ...")
|
||||
|
||||
if self.reset:
|
||||
self.firstTime = True
|
||||
|
||||
if len(self.substFile) > 0:
|
||||
for line in open(self.substFile, "r").readlines():
|
||||
tokens = line.strip().split("=")
|
||||
self.logSubstitutions[tokens[0]] = tokens[1]
|
||||
|
||||
self.check()
|
||||
self.configFile = self.gitdir + "/p4-git-sync.cfg"
|
||||
self.config = shelve.open(self.configFile, writeback=True)
|
||||
|
||||
if self.firstTime:
|
||||
self.start()
|
||||
|
||||
commits = self.config.get("commits", [])
|
||||
commits = []
|
||||
for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
|
||||
commits.append(line.strip())
|
||||
commits.reverse()
|
||||
|
||||
while len(commits) > 0:
|
||||
self.firstTime = False
|
||||
commit = commits[0]
|
||||
commits = commits[1:]
|
||||
self.config["commits"] = commits
|
||||
self.applyCommit(commit)
|
||||
if not self.interactive:
|
||||
break
|
||||
|
||||
self.config.close()
|
||||
|
||||
if self.directSubmit:
|
||||
os.remove(self.diffFile)
|
||||
|
||||
if len(commits) == 0:
|
||||
if self.firstTime:
|
||||
print "No changes found to apply between %s and current HEAD" % self.origin
|
||||
else:
|
||||
print "All changes applied!"
|
||||
os.chdir(self.oldWorkingDirectory)
|
||||
print "All changes applied!"
|
||||
os.chdir(self.oldWorkingDirectory)
|
||||
|
||||
sync = P4Sync()
|
||||
sync.run([])
|
||||
sync = P4Sync()
|
||||
sync.run([])
|
||||
|
||||
rebase = P4Rebase()
|
||||
rebase.rebase()
|
||||
os.remove(self.configFile)
|
||||
rebase = P4Rebase()
|
||||
rebase.rebase()
|
||||
|
||||
return True
|
||||
|
||||
@ -817,7 +745,9 @@ class P4Sync(Command):
|
||||
help="Import into refs/heads/ , not refs/remotes"),
|
||||
optparse.make_option("--max-changes", dest="maxChanges"),
|
||||
optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
|
||||
help="Keep entire BRANCH/DIR/SUBDIR prefix during import")
|
||||
help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
|
||||
optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
|
||||
help="Only sync files that are included in the Perforce Client Spec")
|
||||
]
|
||||
self.description = """Imports from Perforce into a git repository.\n
|
||||
example:
|
||||
@ -843,18 +773,27 @@ class P4Sync(Command):
|
||||
self.keepRepoPath = False
|
||||
self.depotPaths = None
|
||||
self.p4BranchesInGit = []
|
||||
self.cloneExclude = []
|
||||
self.useClientSpec = False
|
||||
self.clientSpecDirs = []
|
||||
|
||||
if gitConfig("git-p4.syncFromOrigin") == "false":
|
||||
self.syncWithOrigin = False
|
||||
|
||||
def extractFilesFromCommit(self, commit):
|
||||
self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
|
||||
for path in self.cloneExclude]
|
||||
files = []
|
||||
fnum = 0
|
||||
while commit.has_key("depotFile%s" % fnum):
|
||||
path = commit["depotFile%s" % fnum]
|
||||
|
||||
found = [p for p in self.depotPaths
|
||||
if path.startswith (p)]
|
||||
if [p for p in self.cloneExclude
|
||||
if path.startswith (p)]:
|
||||
found = False
|
||||
else:
|
||||
found = [p for p in self.depotPaths
|
||||
if path.startswith (p)]
|
||||
if not found:
|
||||
fnum = fnum + 1
|
||||
continue
|
||||
@ -911,11 +850,21 @@ class P4Sync(Command):
|
||||
|
||||
## Should move this out, doesn't use SELF.
|
||||
def readP4Files(self, files):
|
||||
for f in files:
|
||||
for val in self.clientSpecDirs:
|
||||
if f['path'].startswith(val[0]):
|
||||
if val[1] > 0:
|
||||
f['include'] = True
|
||||
else:
|
||||
f['include'] = False
|
||||
break
|
||||
|
||||
files = [f for f in files
|
||||
if f['action'] != 'delete']
|
||||
if f['action'] != 'delete' and
|
||||
(f.has_key('include') == False or f['include'] == True)]
|
||||
|
||||
if not files:
|
||||
return
|
||||
return []
|
||||
|
||||
filedata = p4CmdList('-x - print',
|
||||
stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
|
||||
@ -950,6 +899,7 @@ class P4Sync(Command):
|
||||
for f in files:
|
||||
assert not f.has_key('data')
|
||||
f['data'] = contents[f['path']]
|
||||
return files
|
||||
|
||||
def commit(self, details, files, branch, branchPrefixes, parent = ""):
|
||||
epoch = details["time"]
|
||||
@ -966,11 +916,7 @@ class P4Sync(Command):
|
||||
new_files.append (f)
|
||||
else:
|
||||
sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
|
||||
files = new_files
|
||||
self.readP4Files(files)
|
||||
|
||||
|
||||
|
||||
files = self.readP4Files(new_files)
|
||||
|
||||
self.gitStream.write("commit %s\n" % branch)
|
||||
# gitStream.write("mark :%s\n" % details["change"])
|
||||
@ -1385,6 +1331,26 @@ class P4Sync(Command):
|
||||
print self.gitError.read()
|
||||
|
||||
|
||||
def getClientSpec(self):
|
||||
specList = p4CmdList( "client -o" )
|
||||
temp = {}
|
||||
for entry in specList:
|
||||
for k,v in entry.iteritems():
|
||||
if k.startswith("View"):
|
||||
if v.startswith('"'):
|
||||
start = 1
|
||||
else:
|
||||
start = 0
|
||||
index = v.find("...")
|
||||
v = v[start:index]
|
||||
if v.startswith("-"):
|
||||
v = v[1:]
|
||||
temp[v] = -len(v)
|
||||
else:
|
||||
temp[v] = len(v)
|
||||
self.clientSpecDirs = temp.items()
|
||||
self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
|
||||
|
||||
def run(self, args):
|
||||
self.depotPaths = []
|
||||
self.changeRange = ""
|
||||
@ -1417,6 +1383,9 @@ class P4Sync(Command):
|
||||
if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
|
||||
system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
|
||||
|
||||
if self.useClientSpec or gitConfig("p4.useclientspec") == "true":
|
||||
self.getClientSpec()
|
||||
|
||||
# TODO: should always look at previous commits,
|
||||
# merge with previous imports, if possible.
|
||||
if args == []:
|
||||
@ -1634,13 +1603,23 @@ class P4Clone(P4Sync):
|
||||
P4Sync.__init__(self)
|
||||
self.description = "Creates a new git repository and imports from Perforce into it"
|
||||
self.usage = "usage: %prog [options] //depot/path[@revRange]"
|
||||
self.options.append(
|
||||
self.options += [
|
||||
optparse.make_option("--destination", dest="cloneDestination",
|
||||
action='store', default=None,
|
||||
help="where to leave result of the clone"))
|
||||
help="where to leave result of the clone"),
|
||||
optparse.make_option("-/", dest="cloneExclude",
|
||||
action="append", type="string",
|
||||
help="exclude depot path")
|
||||
]
|
||||
self.cloneDestination = None
|
||||
self.needsGit = False
|
||||
|
||||
# This is required for the "append" cloneExclude action
|
||||
def ensure_value(self, attr, value):
|
||||
if not hasattr(self, attr) or getattr(self, attr) is None:
|
||||
setattr(self, attr, value)
|
||||
return getattr(self, attr)
|
||||
|
||||
def defaultDestination(self, args):
|
||||
## TODO: use common prefix of args?
|
||||
depotPath = args[0]
|
||||
@ -1664,6 +1643,7 @@ class P4Clone(P4Sync):
|
||||
self.cloneDestination = depotPaths[-1]
|
||||
depotPaths = depotPaths[:-1]
|
||||
|
||||
self.cloneExclude = ["/"+p for p in self.cloneExclude]
|
||||
for p in depotPaths:
|
||||
if not p.startswith("//"):
|
||||
return False
|
||||
|
21
copy.c
21
copy.c
@ -34,3 +34,24 @@ int copy_fd(int ifd, int ofd)
|
||||
close(ifd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int copy_file(const char *dst, const char *src, int mode)
|
||||
{
|
||||
int fdi, fdo, status;
|
||||
|
||||
mode = (mode & 0111) ? 0777 : 0666;
|
||||
if ((fdi = open(src, O_RDONLY)) < 0)
|
||||
return fdi;
|
||||
if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
|
||||
close(fdi);
|
||||
return fdo;
|
||||
}
|
||||
status = copy_fd(fdi, fdo);
|
||||
if (close(fdo) != 0)
|
||||
return error("%s: write error: %s", dst, strerror(errno));
|
||||
|
||||
if (!status && adjust_shared_perm(dst))
|
||||
return -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
6
date.c
6
date.c
@ -213,9 +213,9 @@ static const struct {
|
||||
{ "EAST", +10, 0, }, /* Eastern Australian Standard */
|
||||
{ "EADT", +10, 1, }, /* Eastern Australian Daylight */
|
||||
{ "GST", +10, 0, }, /* Guam Standard, USSR Zone 9 */
|
||||
{ "NZT", +11, 0, }, /* New Zealand */
|
||||
{ "NZST", +11, 0, }, /* New Zealand Standard */
|
||||
{ "NZDT", +11, 1, }, /* New Zealand Daylight */
|
||||
{ "NZT", +12, 0, }, /* New Zealand */
|
||||
{ "NZST", +12, 0, }, /* New Zealand Standard */
|
||||
{ "NZDT", +12, 1, }, /* New Zealand Daylight */
|
||||
{ "IDLE", +12, 0, }, /* International Date Line East */
|
||||
};
|
||||
|
||||
|
@ -737,7 +737,8 @@ int run_diff_index(struct rev_info *revs, int cached)
|
||||
opts.unpack_data = revs;
|
||||
|
||||
init_tree_desc(&t, tree->buffer, tree->size);
|
||||
unpack_trees(1, &t, &opts);
|
||||
if (unpack_trees(1, &t, &opts))
|
||||
exit(128);
|
||||
|
||||
diffcore_std(&revs->diffopt);
|
||||
diff_flush(&revs->diffopt);
|
||||
@ -789,6 +790,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
|
||||
opts.unpack_data = &revs;
|
||||
|
||||
init_tree_desc(&t, tree->buffer, tree->size);
|
||||
unpack_trees(1, &t, &opts);
|
||||
if (unpack_trees(1, &t, &opts))
|
||||
exit(128);
|
||||
return 0;
|
||||
}
|
||||
|
222
diff.c
222
diff.c
@ -118,8 +118,7 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v
|
||||
pp->next = funcname_pattern_list;
|
||||
funcname_pattern_list = pp;
|
||||
}
|
||||
if (pp->pattern)
|
||||
free(pp->pattern);
|
||||
free(pp->pattern);
|
||||
pp->pattern = xstrdup(value);
|
||||
return 0;
|
||||
}
|
||||
@ -272,8 +271,8 @@ static void print_line_count(int count)
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_file(int prefix, const char *data, int size,
|
||||
const char *set, const char *reset)
|
||||
static void copy_file_with_prefix(int prefix, const char *data, int size,
|
||||
const char *set, const char *reset)
|
||||
{
|
||||
int ch, nl_just_seen = 1;
|
||||
while (0 < size--) {
|
||||
@ -331,9 +330,9 @@ static void emit_rewrite_diff(const char *name_a,
|
||||
print_line_count(lc_b);
|
||||
printf(" @@%s\n", reset);
|
||||
if (lc_a)
|
||||
copy_file('-', one->data, one->size, old, reset);
|
||||
copy_file_with_prefix('-', one->data, one->size, old, reset);
|
||||
if (lc_b)
|
||||
copy_file('+', two->data, two->size, new, reset);
|
||||
copy_file_with_prefix('+', two->data, two->size, new, reset);
|
||||
}
|
||||
|
||||
static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
|
||||
@ -492,10 +491,8 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
|
||||
ecbdata->diff_words->plus.text.size)
|
||||
diff_words_show(ecbdata->diff_words);
|
||||
|
||||
if (ecbdata->diff_words->minus.text.ptr)
|
||||
free (ecbdata->diff_words->minus.text.ptr);
|
||||
if (ecbdata->diff_words->plus.text.ptr)
|
||||
free (ecbdata->diff_words->plus.text.ptr);
|
||||
free (ecbdata->diff_words->minus.text.ptr);
|
||||
free (ecbdata->diff_words->plus.text.ptr);
|
||||
free(ecbdata->diff_words);
|
||||
ecbdata->diff_words = NULL;
|
||||
}
|
||||
@ -982,6 +979,90 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
|
||||
}
|
||||
}
|
||||
|
||||
struct diffstat_dir {
|
||||
struct diffstat_file **files;
|
||||
int nr, percent, cumulative;
|
||||
};
|
||||
|
||||
static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
|
||||
{
|
||||
unsigned long this_dir = 0;
|
||||
unsigned int sources = 0;
|
||||
|
||||
while (dir->nr) {
|
||||
struct diffstat_file *f = *dir->files;
|
||||
int namelen = strlen(f->name);
|
||||
unsigned long this;
|
||||
char *slash;
|
||||
|
||||
if (namelen < baselen)
|
||||
break;
|
||||
if (memcmp(f->name, base, baselen))
|
||||
break;
|
||||
slash = strchr(f->name + baselen, '/');
|
||||
if (slash) {
|
||||
int newbaselen = slash + 1 - f->name;
|
||||
this = gather_dirstat(dir, changed, f->name, newbaselen);
|
||||
sources++;
|
||||
} else {
|
||||
if (f->is_unmerged || f->is_binary)
|
||||
this = 0;
|
||||
else
|
||||
this = f->added + f->deleted;
|
||||
dir->files++;
|
||||
dir->nr--;
|
||||
sources += 2;
|
||||
}
|
||||
this_dir += this;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't report dirstat's for
|
||||
* - the top level
|
||||
* - or cases where everything came from a single directory
|
||||
* under this directory (sources == 1).
|
||||
*/
|
||||
if (baselen && sources != 1) {
|
||||
int permille = this_dir * 1000 / changed;
|
||||
if (permille) {
|
||||
int percent = permille / 10;
|
||||
if (percent >= dir->percent) {
|
||||
printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
|
||||
if (!dir->cumulative)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this_dir;
|
||||
}
|
||||
|
||||
static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
|
||||
{
|
||||
int i;
|
||||
unsigned long changed;
|
||||
struct diffstat_dir dir;
|
||||
|
||||
/* Calculate total changes */
|
||||
changed = 0;
|
||||
for (i = 0; i < data->nr; i++) {
|
||||
if (data->files[i]->is_binary || data->files[i]->is_unmerged)
|
||||
continue;
|
||||
changed += data->files[i]->added;
|
||||
changed += data->files[i]->deleted;
|
||||
}
|
||||
|
||||
/* This can happen even with many files, if everything was renames */
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
/* Show all directories with more than x% of the changes */
|
||||
dir.files = data->files;
|
||||
dir.nr = data->nr;
|
||||
dir.percent = options->dirstat_percent;
|
||||
dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
|
||||
gather_dirstat(&dir, changed, "", 0);
|
||||
}
|
||||
|
||||
static void free_diffstat_info(struct diffstat_t *diffstat)
|
||||
{
|
||||
int i;
|
||||
@ -1399,6 +1480,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
|
||||
}
|
||||
|
||||
static void builtin_checkdiff(const char *name_a, const char *name_b,
|
||||
const char *attr_path,
|
||||
struct diff_filespec *one,
|
||||
struct diff_filespec *two, struct diff_options *o)
|
||||
{
|
||||
@ -1413,7 +1495,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
|
||||
data.filename = name_b ? name_b : name_a;
|
||||
data.lineno = 0;
|
||||
data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
|
||||
data.ws_rule = whitespace_rule(data.filename);
|
||||
data.ws_rule = whitespace_rule(attr_path);
|
||||
|
||||
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
|
||||
die("unable to read files to diff");
|
||||
@ -1838,6 +1920,9 @@ static const char *external_diff_attr(const char *name)
|
||||
{
|
||||
struct git_attr_check attr_diff_check;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
setup_diff_attr_check(&attr_diff_check);
|
||||
if (!git_checkattr(name, 1, &attr_diff_check)) {
|
||||
const char *value = attr_diff_check.value;
|
||||
@ -1857,6 +1942,7 @@ static const char *external_diff_attr(const char *name)
|
||||
static void run_diff_cmd(const char *pgm,
|
||||
const char *name,
|
||||
const char *other,
|
||||
const char *attr_path,
|
||||
struct diff_filespec *one,
|
||||
struct diff_filespec *two,
|
||||
const char *xfrm_msg,
|
||||
@ -1866,7 +1952,7 @@ static void run_diff_cmd(const char *pgm,
|
||||
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
|
||||
pgm = NULL;
|
||||
else {
|
||||
const char *cmd = external_diff_attr(name);
|
||||
const char *cmd = external_diff_attr(attr_path);
|
||||
if (cmd)
|
||||
pgm = cmd;
|
||||
}
|
||||
@ -1907,6 +1993,15 @@ static int similarity_index(struct diff_filepair *p)
|
||||
return p->score * 100 / MAX_SCORE;
|
||||
}
|
||||
|
||||
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
|
||||
{
|
||||
/* Strip the prefix but do not molest /dev/null and absolute paths */
|
||||
if (*namep && **namep != '/')
|
||||
*namep += prefix_length;
|
||||
if (*otherp && **otherp != '/')
|
||||
*otherp += prefix_length;
|
||||
}
|
||||
|
||||
static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
{
|
||||
const char *pgm = external_diff();
|
||||
@ -1916,16 +2011,21 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
struct diff_filespec *two = p->two;
|
||||
const char *name;
|
||||
const char *other;
|
||||
const char *attr_path;
|
||||
int complete_rewrite = 0;
|
||||
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p)) {
|
||||
run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
name = p->one->path;
|
||||
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
|
||||
attr_path = name;
|
||||
if (o->prefix_length)
|
||||
strip_prefix(o->prefix_length, &name, &other);
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p)) {
|
||||
run_diff_cmd(pgm, name, NULL, attr_path,
|
||||
NULL, NULL, NULL, o, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
diff_fill_sha1_info(one);
|
||||
diff_fill_sha1_info(two);
|
||||
|
||||
@ -1988,15 +2088,17 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
* needs to be split into deletion and creation.
|
||||
*/
|
||||
struct diff_filespec *null = alloc_filespec(two->path);
|
||||
run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
|
||||
run_diff_cmd(NULL, name, other, attr_path,
|
||||
one, null, xfrm_msg, o, 0);
|
||||
free(null);
|
||||
null = alloc_filespec(one->path);
|
||||
run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
|
||||
run_diff_cmd(NULL, name, other, attr_path,
|
||||
null, two, xfrm_msg, o, 0);
|
||||
free(null);
|
||||
}
|
||||
else
|
||||
run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
|
||||
complete_rewrite);
|
||||
run_diff_cmd(pgm, name, other, attr_path,
|
||||
one, two, xfrm_msg, o, complete_rewrite);
|
||||
|
||||
strbuf_release(&msg);
|
||||
}
|
||||
@ -2017,6 +2119,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
|
||||
name = p->one->path;
|
||||
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
|
||||
|
||||
if (o->prefix_length)
|
||||
strip_prefix(o->prefix_length, &name, &other);
|
||||
|
||||
diff_fill_sha1_info(p->one);
|
||||
diff_fill_sha1_info(p->two);
|
||||
|
||||
@ -2029,6 +2134,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
|
||||
{
|
||||
const char *name;
|
||||
const char *other;
|
||||
const char *attr_path;
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p)) {
|
||||
/* unmerged */
|
||||
@ -2037,11 +2143,15 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
|
||||
|
||||
name = p->one->path;
|
||||
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
|
||||
attr_path = other ? other : name;
|
||||
|
||||
if (o->prefix_length)
|
||||
strip_prefix(o->prefix_length, &name, &other);
|
||||
|
||||
diff_fill_sha1_info(p->one);
|
||||
diff_fill_sha1_info(p->two);
|
||||
|
||||
builtin_checkdiff(name, other, p->one, p->two, o);
|
||||
builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
|
||||
}
|
||||
|
||||
void diff_setup(struct diff_options *options)
|
||||
@ -2050,6 +2160,7 @@ void diff_setup(struct diff_options *options)
|
||||
options->line_termination = '\n';
|
||||
options->break_opt = -1;
|
||||
options->rename_limit = -1;
|
||||
options->dirstat_percent = 3;
|
||||
options->context = 3;
|
||||
options->msg_sep = "";
|
||||
|
||||
@ -2083,6 +2194,13 @@ int diff_setup_done(struct diff_options *options)
|
||||
if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
|
||||
options->detect_rename = DIFF_DETECT_COPY;
|
||||
|
||||
if (!DIFF_OPT_TST(options, RELATIVE_NAME))
|
||||
options->prefix = NULL;
|
||||
if (options->prefix)
|
||||
options->prefix_length = strlen(options->prefix);
|
||||
else
|
||||
options->prefix_length = 0;
|
||||
|
||||
if (options->output_format & (DIFF_FORMAT_NAME |
|
||||
DIFF_FORMAT_NAME_STATUS |
|
||||
DIFF_FORMAT_CHECKDIFF |
|
||||
@ -2091,6 +2209,7 @@ int diff_setup_done(struct diff_options *options)
|
||||
DIFF_FORMAT_NUMSTAT |
|
||||
DIFF_FORMAT_DIFFSTAT |
|
||||
DIFF_FORMAT_SHORTSTAT |
|
||||
DIFF_FORMAT_DIRSTAT |
|
||||
DIFF_FORMAT_SUMMARY |
|
||||
DIFF_FORMAT_PATCH);
|
||||
|
||||
@ -2102,6 +2221,7 @@ int diff_setup_done(struct diff_options *options)
|
||||
DIFF_FORMAT_NUMSTAT |
|
||||
DIFF_FORMAT_DIFFSTAT |
|
||||
DIFF_FORMAT_SHORTSTAT |
|
||||
DIFF_FORMAT_DIRSTAT |
|
||||
DIFF_FORMAT_SUMMARY |
|
||||
DIFF_FORMAT_CHECKDIFF))
|
||||
DIFF_OPT_SET(options, RECURSIVE);
|
||||
@ -2212,6 +2332,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
||||
options->output_format |= DIFF_FORMAT_NUMSTAT;
|
||||
else if (!strcmp(arg, "--shortstat"))
|
||||
options->output_format |= DIFF_FORMAT_SHORTSTAT;
|
||||
else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent))
|
||||
options->output_format |= DIFF_FORMAT_DIRSTAT;
|
||||
else if (!strcmp(arg, "--cumulative"))
|
||||
options->output_format |= DIFF_FORMAT_CUMULATIVE;
|
||||
else if (!strcmp(arg, "--check"))
|
||||
options->output_format |= DIFF_FORMAT_CHECKDIFF;
|
||||
else if (!strcmp(arg, "--summary"))
|
||||
@ -2271,6 +2395,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
||||
}
|
||||
else if (!strcmp(arg, "--no-renames"))
|
||||
options->detect_rename = 0;
|
||||
else if (!strcmp(arg, "--relative"))
|
||||
DIFF_OPT_SET(options, RELATIVE_NAME);
|
||||
else if (!prefixcmp(arg, "--relative=")) {
|
||||
DIFF_OPT_SET(options, RELATIVE_NAME);
|
||||
options->prefix = arg + 11;
|
||||
}
|
||||
|
||||
/* xdiff options */
|
||||
else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
|
||||
@ -2482,12 +2612,20 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
|
||||
printf("%c%c", p->status, inter_name_termination);
|
||||
}
|
||||
|
||||
if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
|
||||
write_name_quoted(p->one->path, stdout, inter_name_termination);
|
||||
write_name_quoted(p->two->path, stdout, line_termination);
|
||||
if (p->status == DIFF_STATUS_COPIED ||
|
||||
p->status == DIFF_STATUS_RENAMED) {
|
||||
const char *name_a, *name_b;
|
||||
name_a = p->one->path;
|
||||
name_b = p->two->path;
|
||||
strip_prefix(opt->prefix_length, &name_a, &name_b);
|
||||
write_name_quoted(name_a, stdout, inter_name_termination);
|
||||
write_name_quoted(name_b, stdout, line_termination);
|
||||
} else {
|
||||
const char *path = p->one->mode ? p->one->path : p->two->path;
|
||||
write_name_quoted(path, stdout, line_termination);
|
||||
const char *name_a, *name_b;
|
||||
name_a = p->one->mode ? p->one->path : p->two->path;
|
||||
name_b = NULL;
|
||||
strip_prefix(opt->prefix_length, &name_a, &name_b);
|
||||
write_name_quoted(name_a, stdout, line_termination);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2684,8 +2822,13 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
|
||||
diff_flush_checkdiff(p, opt);
|
||||
else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
|
||||
diff_flush_raw(p, opt);
|
||||
else if (fmt & DIFF_FORMAT_NAME)
|
||||
write_name_quoted(p->two->path, stdout, opt->line_termination);
|
||||
else if (fmt & DIFF_FORMAT_NAME) {
|
||||
const char *name_a, *name_b;
|
||||
name_a = p->two->path;
|
||||
name_b = NULL;
|
||||
strip_prefix(opt->prefix_length, &name_a, &name_b);
|
||||
write_name_quoted(name_a, stdout, opt->line_termination);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
|
||||
@ -2930,7 +3073,7 @@ void diff_flush(struct diff_options *options)
|
||||
separator++;
|
||||
}
|
||||
|
||||
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
|
||||
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
|
||||
struct diffstat_t diffstat;
|
||||
|
||||
memset(&diffstat, 0, sizeof(struct diffstat_t));
|
||||
@ -2940,6 +3083,8 @@ void diff_flush(struct diff_options *options)
|
||||
if (check_pair_status(p))
|
||||
diff_flush_stat(p, options, &diffstat);
|
||||
}
|
||||
if (output_format & DIFF_FORMAT_DIRSTAT)
|
||||
show_dirstat(&diffstat, options);
|
||||
if (output_format & DIFF_FORMAT_NUMSTAT)
|
||||
show_numstat(&diffstat, options);
|
||||
if (output_format & DIFF_FORMAT_DIFFSTAT)
|
||||
@ -3171,6 +3316,11 @@ void diff_addremove(struct diff_options *options,
|
||||
|
||||
if (!path) path = "";
|
||||
sprintf(concatpath, "%s%s", base, path);
|
||||
|
||||
if (options->prefix &&
|
||||
strncmp(concatpath, options->prefix, options->prefix_length))
|
||||
return;
|
||||
|
||||
one = alloc_filespec(concatpath);
|
||||
two = alloc_filespec(concatpath);
|
||||
|
||||
@ -3200,6 +3350,11 @@ void diff_change(struct diff_options *options,
|
||||
}
|
||||
if (!path) path = "";
|
||||
sprintf(concatpath, "%s%s", base, path);
|
||||
|
||||
if (options->prefix &&
|
||||
strncmp(concatpath, options->prefix, options->prefix_length))
|
||||
return;
|
||||
|
||||
one = alloc_filespec(concatpath);
|
||||
two = alloc_filespec(concatpath);
|
||||
fill_filespec(one, old_sha1, old_mode);
|
||||
@ -3214,6 +3369,11 @@ void diff_unmerge(struct diff_options *options,
|
||||
unsigned mode, const unsigned char *sha1)
|
||||
{
|
||||
struct diff_filespec *one, *two;
|
||||
|
||||
if (options->prefix &&
|
||||
strncmp(path, options->prefix, options->prefix_length))
|
||||
return;
|
||||
|
||||
one = alloc_filespec(path);
|
||||
two = alloc_filespec(path);
|
||||
fill_filespec(one, sha1, mode);
|
||||
|
6
diff.h
6
diff.h
@ -30,6 +30,8 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
|
||||
#define DIFF_FORMAT_SUMMARY 0x0008
|
||||
#define DIFF_FORMAT_PATCH 0x0010
|
||||
#define DIFF_FORMAT_SHORTSTAT 0x0020
|
||||
#define DIFF_FORMAT_DIRSTAT 0x0040
|
||||
#define DIFF_FORMAT_CUMULATIVE 0x0080
|
||||
|
||||
/* These override all above */
|
||||
#define DIFF_FORMAT_NAME 0x0100
|
||||
@ -60,6 +62,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
|
||||
#define DIFF_OPT_EXIT_WITH_STATUS (1 << 14)
|
||||
#define DIFF_OPT_REVERSE_DIFF (1 << 15)
|
||||
#define DIFF_OPT_CHECK_FAILED (1 << 16)
|
||||
#define DIFF_OPT_RELATIVE_NAME (1 << 17)
|
||||
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
|
||||
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
|
||||
#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag)
|
||||
@ -80,8 +83,11 @@ struct diff_options {
|
||||
int pickaxe_opts;
|
||||
int rename_score;
|
||||
int rename_limit;
|
||||
int dirstat_percent;
|
||||
int setup;
|
||||
int abbrev;
|
||||
const char *prefix;
|
||||
int prefix_length;
|
||||
const char *msg_sep;
|
||||
const char *stat_sep;
|
||||
long xdl_opts;
|
||||
|
3
dir.c
3
dir.c
@ -704,8 +704,7 @@ static struct path_simplify *create_simplify(const char **pathspec)
|
||||
|
||||
static void free_simplify(struct path_simplify *simplify)
|
||||
{
|
||||
if (simplify)
|
||||
free(simplify);
|
||||
free(simplify);
|
||||
}
|
||||
|
||||
int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
|
||||
|
@ -37,6 +37,7 @@ const char *excludes_file;
|
||||
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
|
||||
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
|
||||
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
|
||||
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
|
||||
|
||||
/* This is set by setup_git_dir_gently() and/or git_default_config() */
|
||||
char *git_work_tree_cfg;
|
||||
|
@ -67,16 +67,18 @@ bisect_start() {
|
||||
die "Bad HEAD - I need a HEAD"
|
||||
case "$head" in
|
||||
refs/heads/bisect)
|
||||
if [ -s "$GIT_DIR/head-name" ]; then
|
||||
branch=`cat "$GIT_DIR/head-name"`
|
||||
if [ -s "$GIT_DIR/BISECT_START" ]; then
|
||||
branch=`cat "$GIT_DIR/BISECT_START"`
|
||||
else
|
||||
branch=master
|
||||
fi
|
||||
git checkout $branch || exit
|
||||
;;
|
||||
refs/heads/*|$_x40)
|
||||
# This error message should only be triggered by cogito usage,
|
||||
# and cogito users should understand it relates to cg-seek.
|
||||
[ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
|
||||
echo "${head#refs/heads/}" >"$GIT_DIR/head-name"
|
||||
echo "${head#refs/heads/}" >"$GIT_DIR/BISECT_START"
|
||||
;;
|
||||
*)
|
||||
die "Bad HEAD - strange symbolic ref"
|
||||
@ -353,8 +355,8 @@ bisect_reset() {
|
||||
return
|
||||
}
|
||||
case "$#" in
|
||||
0) if [ -s "$GIT_DIR/head-name" ]; then
|
||||
branch=`cat "$GIT_DIR/head-name"`
|
||||
0) if [ -s "$GIT_DIR/BISECT_START" ]; then
|
||||
branch=`cat "$GIT_DIR/BISECT_START"`
|
||||
else
|
||||
branch=master
|
||||
fi ;;
|
||||
@ -365,7 +367,9 @@ bisect_reset() {
|
||||
usage ;;
|
||||
esac
|
||||
if git checkout "$branch"; then
|
||||
# Cleanup head-name if it got left by an old version of git-bisect
|
||||
rm -f "$GIT_DIR/head-name"
|
||||
rm -f "$GIT_DIR/BISECT_START"
|
||||
bisect_clean_state
|
||||
fi
|
||||
}
|
||||
|
@ -92,8 +92,12 @@ ifndef V
|
||||
REMOVE_F1 = && echo ' ' REMOVE `basename "$$dst"` && $(RM_RF) "$$dst"
|
||||
endif
|
||||
|
||||
TCL_PATH ?= tclsh
|
||||
TCLTK_PATH ?= wish
|
||||
ifeq (./,$(dir $(TCLTK_PATH)))
|
||||
TCL_PATH ?= $(subst wish,tclsh,$(TCLTK_PATH))
|
||||
else
|
||||
TCL_PATH ?= $(dir $(TCLTK_PATH))$(notdir $(subst wish,tclsh,$(TCLTK_PATH)))
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),Darwin)
|
||||
TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app
|
||||
@ -127,7 +131,17 @@ GITGUI_MACOSXAPP :=
|
||||
|
||||
ifeq ($(uname_O),Cygwin)
|
||||
GITGUI_SCRIPT := `cygpath --windows --absolute "$(GITGUI_SCRIPT)"`
|
||||
gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)")
|
||||
|
||||
# Is this a Cygwin Tcl/Tk binary? If so it knows how to do
|
||||
# POSIX path translation just like cygpath does and we must
|
||||
# keep libdir in POSIX format so Cygwin packages of git-gui
|
||||
# work no matter where the user installs them.
|
||||
#
|
||||
ifeq ($(shell echo 'puts [file normalize /]' | '$(TCL_PATH_SQ)'),$(shell cygpath --mixed --absolute /))
|
||||
gg_libdir_sed_in := $(gg_libdir)
|
||||
else
|
||||
gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)")
|
||||
endif
|
||||
else
|
||||
ifeq ($(exedir),$(gg_libdir))
|
||||
GITGUI_RELATIVE := 1
|
||||
|
@ -663,7 +663,7 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
|
||||
}
|
||||
|
||||
set _real_git_version $_git_version
|
||||
regsub -- {-dirty$} $_git_version {} _git_version
|
||||
regsub -- {[\-\.]dirty$} $_git_version {} _git_version
|
||||
regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
|
||||
regsub {\.rc[0-9]+$} $_git_version {} _git_version
|
||||
regsub {\.GIT$} $_git_version {} _git_version
|
||||
|
@ -11,6 +11,7 @@ field w_quit ; # Quit button
|
||||
field o_cons ; # Console object (if active)
|
||||
field w_types ; # List of type buttons in clone
|
||||
field w_recentlist ; # Listbox containing recent repositories
|
||||
field w_localpath ; # Entry widget bound to local_path
|
||||
|
||||
field done 0 ; # Finished picking the repository?
|
||||
field local_path {} ; # Where this repository is locally
|
||||
@ -385,6 +386,7 @@ method _do_new {} {
|
||||
button $w_body.where.b \
|
||||
-text [mc "Browse"] \
|
||||
-command [cb _new_local_path]
|
||||
set w_localpath $w_body.where.t
|
||||
|
||||
pack $w_body.where.b -side right
|
||||
pack $w_body.where.l -side left
|
||||
@ -416,6 +418,7 @@ method _new_local_path {} {
|
||||
return
|
||||
}
|
||||
set local_path $p
|
||||
$w_localpath icursor end
|
||||
}
|
||||
|
||||
method _do_new2 {} {
|
||||
@ -481,6 +484,7 @@ method _do_clone {} {
|
||||
-text [mc "Browse"] \
|
||||
-command [cb _new_local_path]
|
||||
grid $args.where_l $args.where_t $args.where_b -sticky ew
|
||||
set w_localpath $args.where_t
|
||||
|
||||
label $args.type_l -text [mc "Clone Type:"]
|
||||
frame $args.type_f
|
||||
|
@ -1,6 +1,14 @@
|
||||
# git-gui branch (create/delete) support
|
||||
# Copyright (C) 2006, 2007 Shawn Pearce
|
||||
|
||||
proc _error_parent {} {
|
||||
set p [grab current .]
|
||||
if {$p eq {}} {
|
||||
return .
|
||||
}
|
||||
return $p
|
||||
}
|
||||
|
||||
proc error_popup {msg} {
|
||||
set title [appname]
|
||||
if {[reponame] ne {}} {
|
||||
@ -11,8 +19,8 @@ proc error_popup {msg} {
|
||||
-type ok \
|
||||
-title [append "$title: " [mc "error"]] \
|
||||
-message $msg]
|
||||
if {[winfo ismapped .]} {
|
||||
lappend cmd -parent .
|
||||
if {[winfo ismapped [_error_parent]]} {
|
||||
lappend cmd -parent [_error_parent]
|
||||
}
|
||||
eval $cmd
|
||||
}
|
||||
@ -27,13 +35,13 @@ proc warn_popup {msg} {
|
||||
-type ok \
|
||||
-title [append "$title: " [mc "warning"]] \
|
||||
-message $msg]
|
||||
if {[winfo ismapped .]} {
|
||||
lappend cmd -parent .
|
||||
if {[winfo ismapped [_error_parent]]} {
|
||||
lappend cmd -parent [_error_parent]
|
||||
}
|
||||
eval $cmd
|
||||
}
|
||||
|
||||
proc info_popup {msg {parent .}} {
|
||||
proc info_popup {msg} {
|
||||
set title [appname]
|
||||
if {[reponame] ne {}} {
|
||||
append title " ([reponame])"
|
||||
@ -56,8 +64,8 @@ proc ask_popup {msg} {
|
||||
-type yesno \
|
||||
-title $title \
|
||||
-message $msg]
|
||||
if {[winfo ismapped .]} {
|
||||
lappend cmd -parent .
|
||||
if {[winfo ismapped [_error_parent]]} {
|
||||
lappend cmd -parent [_error_parent]
|
||||
}
|
||||
eval $cmd
|
||||
}
|
||||
|
@ -174,6 +174,7 @@ fi
|
||||
|
||||
merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
|
||||
test true = "$rebase" &&
|
||||
exec git-rebase --onto $merge_head ${oldremoteref:-$merge_head}
|
||||
exec git-rebase $strategy_args --onto $merge_head \
|
||||
${oldremoteref:-$merge_head}
|
||||
exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \
|
||||
"$merge_name" HEAD $merge_head
|
||||
|
@ -268,6 +268,10 @@ do_next () {
|
||||
warn
|
||||
warn " git commit --amend"
|
||||
warn
|
||||
warn "Once you are satisfied with your changes, run"
|
||||
warn
|
||||
warn " git rebase --continue"
|
||||
warn
|
||||
exit 0
|
||||
;;
|
||||
squash|s)
|
||||
|
@ -3643,6 +3643,7 @@ sub _auth_providers () {
|
||||
SVN::Client::get_ssl_client_cert_file_provider(),
|
||||
SVN::Client::get_ssl_client_cert_prompt_provider(
|
||||
\&Git::SVN::Prompt::ssl_client_cert, 2),
|
||||
SVN::Client::get_ssl_client_cert_pw_file_provider(),
|
||||
SVN::Client::get_ssl_client_cert_pw_prompt_provider(
|
||||
\&Git::SVN::Prompt::ssl_client_cert_pw, 2),
|
||||
SVN::Client::get_username_provider(),
|
||||
|
20
git.c
20
git.c
@ -87,19 +87,6 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
|
||||
return handled;
|
||||
}
|
||||
|
||||
static const char *alias_command;
|
||||
static char *alias_string;
|
||||
|
||||
static int git_alias_config(const char *var, const char *value)
|
||||
{
|
||||
if (!prefixcmp(var, "alias.") && !strcmp(var + 6, alias_command)) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
alias_string = xstrdup(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int split_cmdline(char *cmdline, const char ***argv)
|
||||
{
|
||||
int src, dst, count = 0, size = 16;
|
||||
@ -159,11 +146,13 @@ static int handle_alias(int *argcp, const char ***argv)
|
||||
const char *subdir;
|
||||
int count, option_count;
|
||||
const char** new_argv;
|
||||
const char *alias_command;
|
||||
char *alias_string;
|
||||
|
||||
subdir = setup_git_directory_gently(&nongit);
|
||||
|
||||
alias_command = (*argv)[0];
|
||||
git_config(git_alias_config);
|
||||
alias_string = alias_lookup(alias_command);
|
||||
if (alias_string) {
|
||||
if (alias_string[0] == '!') {
|
||||
if (*argcp > 1) {
|
||||
@ -289,6 +278,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "branch", cmd_branch, RUN_SETUP },
|
||||
{ "bundle", cmd_bundle },
|
||||
{ "cat-file", cmd_cat_file, RUN_SETUP },
|
||||
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "checkout-index", cmd_checkout_index,
|
||||
RUN_SETUP | NEED_WORK_TREE},
|
||||
{ "check-ref-format", cmd_check_ref_format },
|
||||
@ -332,6 +322,8 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "merge-base", cmd_merge_base, RUN_SETUP },
|
||||
{ "merge-file", cmd_merge_file },
|
||||
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
|
||||
{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "name-rev", cmd_name_rev, RUN_SETUP },
|
||||
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
|
||||
|
@ -848,32 +848,73 @@ sub project_in_list {
|
||||
## ----------------------------------------------------------------------
|
||||
## HTML aware string manipulation
|
||||
|
||||
# Try to chop given string on a word boundary between position
|
||||
# $len and $len+$add_len. If there is no word boundary there,
|
||||
# chop at $len+$add_len. Do not chop if chopped part plus ellipsis
|
||||
# (marking chopped part) would be longer than given string.
|
||||
sub chop_str {
|
||||
my $str = shift;
|
||||
my $len = shift;
|
||||
my $add_len = shift || 10;
|
||||
my $where = shift || 'right'; # 'left' | 'center' | 'right'
|
||||
|
||||
# allow only $len chars, but don't cut a word if it would fit in $add_len
|
||||
# if it doesn't fit, cut it if it's still longer than the dots we would add
|
||||
$str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/;
|
||||
my $body = $1;
|
||||
my $tail = $2;
|
||||
if (length($tail) > 4) {
|
||||
$tail = " ...";
|
||||
$body =~ s/&[^;]*$//; # remove chopped character entities
|
||||
# remove chopped character entities entirely
|
||||
|
||||
# when chopping in the middle, distribute $len into left and right part
|
||||
# return early if chopping wouldn't make string shorter
|
||||
if ($where eq 'center') {
|
||||
return $str if ($len + 5 >= length($str)); # filler is length 5
|
||||
$len = int($len/2);
|
||||
} else {
|
||||
return $str if ($len + 4 >= length($str)); # filler is length 4
|
||||
}
|
||||
|
||||
# regexps: ending and beginning with word part up to $add_len
|
||||
my $endre = qr/.{$len}\w{0,$add_len}/;
|
||||
my $begre = qr/\w{0,$add_len}.{$len}/;
|
||||
|
||||
if ($where eq 'left') {
|
||||
$str =~ m/^(.*?)($begre)$/;
|
||||
my ($lead, $body) = ($1, $2);
|
||||
if (length($lead) > 4) {
|
||||
$body =~ s/^[^;]*;// if ($lead =~ m/&[^;]*$/);
|
||||
$lead = " ...";
|
||||
}
|
||||
return "$lead$body";
|
||||
|
||||
} elsif ($where eq 'center') {
|
||||
$str =~ m/^($endre)(.*)$/;
|
||||
my ($left, $str) = ($1, $2);
|
||||
$str =~ m/^(.*?)($begre)$/;
|
||||
my ($mid, $right) = ($1, $2);
|
||||
if (length($mid) > 5) {
|
||||
$left =~ s/&[^;]*$//;
|
||||
$right =~ s/^[^;]*;// if ($mid =~ m/&[^;]*$/);
|
||||
$mid = " ... ";
|
||||
}
|
||||
return "$left$mid$right";
|
||||
|
||||
} else {
|
||||
$str =~ m/^($endre)(.*)$/;
|
||||
my $body = $1;
|
||||
my $tail = $2;
|
||||
if (length($tail) > 4) {
|
||||
$body =~ s/&[^;]*$//;
|
||||
$tail = "... ";
|
||||
}
|
||||
return "$body$tail";
|
||||
}
|
||||
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 ($str) = @_;
|
||||
|
||||
my $chopped = chop_str($str, $len, $add_len);
|
||||
my $chopped = chop_str(@_);
|
||||
if ($chopped eq $str) {
|
||||
return esc_html($chopped);
|
||||
} else {
|
||||
@ -3791,11 +3832,11 @@ sub git_search_grep_body {
|
||||
foreach my $line (@$comment) {
|
||||
if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
|
||||
my ($lead, $match, $trail) = ($1, $2, $3);
|
||||
$match = chop_str($match, 70, 5); # in case match is very long
|
||||
my $contextlen = (80 - len($match))/2; # is left for the remainder
|
||||
$contextlen = 30 if ($contextlen > 30); # but not too much
|
||||
$lead = chop_str($lead, $contextlen, 10);
|
||||
$trail = chop_str($trail, $contextlen, 10);
|
||||
$match = chop_str($match, 70, 5, 'center');
|
||||
my $contextlen = int((80 - length($match))/2);
|
||||
$contextlen = 30 if ($contextlen > 30);
|
||||
$lead = chop_str($lead, $contextlen, 10, 'left');
|
||||
$trail = chop_str($trail, $contextlen, 10, 'right');
|
||||
|
||||
$lead = esc_html($lead);
|
||||
$match = esc_html($match);
|
||||
|
@ -41,6 +41,7 @@ int main(int argc, char **argv)
|
||||
const char *prefix = NULL;
|
||||
int prefix_length = -1;
|
||||
int no_more_flags = 0;
|
||||
int hashstdin = 0;
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
@ -65,13 +66,20 @@ int main(int argc, char **argv)
|
||||
else if (!strcmp(argv[i], "--help"))
|
||||
usage(hash_object_usage);
|
||||
else if (!strcmp(argv[i], "--stdin")) {
|
||||
hash_stdin(type, write_object);
|
||||
if (hashstdin)
|
||||
die("Multiple --stdin arguments are not supported");
|
||||
hashstdin = 1;
|
||||
}
|
||||
else
|
||||
usage(hash_object_usage);
|
||||
}
|
||||
else {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (hashstdin) {
|
||||
hash_stdin(type, write_object);
|
||||
hashstdin = 0;
|
||||
}
|
||||
if (0 <= prefix_length)
|
||||
arg = prefix_filename(prefix, prefix_length,
|
||||
arg);
|
||||
@ -79,5 +87,7 @@ int main(int argc, char **argv)
|
||||
no_more_flags = 1;
|
||||
}
|
||||
}
|
||||
if (hashstdin)
|
||||
hash_stdin(type, write_object);
|
||||
return 0;
|
||||
}
|
||||
|
146
help.c
146
help.c
@ -7,40 +7,49 @@
|
||||
#include "builtin.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "common-cmds.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char *help_default_format;
|
||||
enum help_format {
|
||||
HELP_FORMAT_MAN,
|
||||
HELP_FORMAT_INFO,
|
||||
HELP_FORMAT_WEB,
|
||||
};
|
||||
|
||||
static enum help_format {
|
||||
man_format,
|
||||
info_format,
|
||||
web_format,
|
||||
} help_format = man_format;
|
||||
static int show_all = 0;
|
||||
static enum help_format help_format = HELP_FORMAT_MAN;
|
||||
static struct option builtin_help_options[] = {
|
||||
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
|
||||
OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
|
||||
OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
|
||||
HELP_FORMAT_WEB),
|
||||
OPT_SET_INT('i', "info", &help_format, "show info page",
|
||||
HELP_FORMAT_INFO),
|
||||
};
|
||||
|
||||
static void parse_help_format(const char *format)
|
||||
static const char * const builtin_help_usage[] = {
|
||||
"git-help [--all] [--man|--web|--info] [command]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static enum help_format parse_help_format(const char *format)
|
||||
{
|
||||
if (!format) {
|
||||
help_format = man_format;
|
||||
return;
|
||||
}
|
||||
if (!strcmp(format, "man")) {
|
||||
help_format = man_format;
|
||||
return;
|
||||
}
|
||||
if (!strcmp(format, "info")) {
|
||||
help_format = info_format;
|
||||
return;
|
||||
}
|
||||
if (!strcmp(format, "web") || !strcmp(format, "html")) {
|
||||
help_format = web_format;
|
||||
return;
|
||||
}
|
||||
if (!strcmp(format, "man"))
|
||||
return HELP_FORMAT_MAN;
|
||||
if (!strcmp(format, "info"))
|
||||
return HELP_FORMAT_INFO;
|
||||
if (!strcmp(format, "web") || !strcmp(format, "html"))
|
||||
return HELP_FORMAT_WEB;
|
||||
die("unrecognized help format '%s'", format);
|
||||
}
|
||||
|
||||
static int git_help_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "help.format"))
|
||||
return git_config_string(&help_default_format, var, value);
|
||||
if (!strcmp(var, "help.format")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
help_format = parse_help_format(value);
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
@ -201,7 +210,7 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds,
|
||||
return longest;
|
||||
}
|
||||
|
||||
static void list_commands(void)
|
||||
static unsigned int load_command_list(void)
|
||||
{
|
||||
unsigned int longest = 0;
|
||||
unsigned int len;
|
||||
@ -241,6 +250,14 @@ static void list_commands(void)
|
||||
uniq(&other_cmds);
|
||||
exclude_cmds(&other_cmds, &main_cmds);
|
||||
|
||||
return longest;
|
||||
}
|
||||
|
||||
static void list_commands(void)
|
||||
{
|
||||
unsigned int longest = load_command_list();
|
||||
const char *exec_path = git_exec_path();
|
||||
|
||||
if (main_cmds.cnt) {
|
||||
printf("available git commands in '%s'\n", exec_path);
|
||||
printf("----------------------------");
|
||||
@ -275,6 +292,22 @@ void list_common_cmds_help(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int is_in_cmdlist(struct cmdnames *c, const char *s)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < c->cnt; i++)
|
||||
if (!strcmp(s, c->names[i]->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_git_command(const char *s)
|
||||
{
|
||||
load_command_list();
|
||||
return is_in_cmdlist(&main_cmds, s) ||
|
||||
is_in_cmdlist(&other_cmds, s);
|
||||
}
|
||||
|
||||
static const char *cmd_to_page(const char *git_cmd)
|
||||
{
|
||||
if (!git_cmd)
|
||||
@ -362,50 +395,43 @@ 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 = argv[1];
|
||||
int nongit;
|
||||
const char *alias;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("usage: %s\n\n", git_usage_string);
|
||||
list_common_cmds_help();
|
||||
exit(0);
|
||||
}
|
||||
setup_git_directory_gently(&nongit);
|
||||
git_config(git_help_config);
|
||||
|
||||
if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
|
||||
argc = parse_options(argc, argv, builtin_help_options,
|
||||
builtin_help_usage, 0);
|
||||
|
||||
if (show_all) {
|
||||
printf("usage: %s\n\n", git_usage_string);
|
||||
list_commands();
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if (!strcmp(help_cmd, "--web") || !strcmp(help_cmd, "-w")) {
|
||||
show_html_page(argc > 2 ? argv[2] : NULL);
|
||||
if (!argv[0]) {
|
||||
printf("usage: %s\n\n", git_usage_string);
|
||||
list_common_cmds_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if (!strcmp(help_cmd, "--info") || !strcmp(help_cmd, "-i")) {
|
||||
show_info_page(argc > 2 ? argv[2] : NULL);
|
||||
alias = alias_lookup(argv[0]);
|
||||
if (alias && !is_git_command(argv[0])) {
|
||||
printf("`git %s' is aliased to `%s'\n", argv[0], alias);
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if (!strcmp(help_cmd, "--man") || !strcmp(help_cmd, "-m")) {
|
||||
show_man_page(argc > 2 ? argv[2] : NULL);
|
||||
}
|
||||
|
||||
else {
|
||||
int nongit;
|
||||
|
||||
setup_git_directory_gently(&nongit);
|
||||
git_config(git_help_config);
|
||||
if (help_default_format)
|
||||
parse_help_format(help_default_format);
|
||||
|
||||
switch (help_format) {
|
||||
case man_format:
|
||||
show_man_page(help_cmd);
|
||||
break;
|
||||
case info_format:
|
||||
show_info_page(help_cmd);
|
||||
break;
|
||||
case web_format:
|
||||
show_html_page(help_cmd);
|
||||
break;
|
||||
}
|
||||
switch (help_format) {
|
||||
case HELP_FORMAT_MAN:
|
||||
show_man_page(argv[0]);
|
||||
break;
|
||||
case HELP_FORMAT_INFO:
|
||||
show_info_page(argv[0]);
|
||||
break;
|
||||
case HELP_FORMAT_WEB:
|
||||
show_html_page(argv[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
18
http-push.c
18
http-push.c
@ -664,8 +664,7 @@ static void release_request(struct transfer_request *request)
|
||||
close(request->local_fileno);
|
||||
if (request->local_stream)
|
||||
fclose(request->local_stream);
|
||||
if (request->url != NULL)
|
||||
free(request->url);
|
||||
free(request->url);
|
||||
free(request);
|
||||
}
|
||||
|
||||
@ -1283,10 +1282,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
|
||||
strbuf_release(&in_buffer);
|
||||
|
||||
if (lock->token == NULL || lock->timeout <= 0) {
|
||||
if (lock->token != NULL)
|
||||
free(lock->token);
|
||||
if (lock->owner != NULL)
|
||||
free(lock->owner);
|
||||
free(lock->token);
|
||||
free(lock->owner);
|
||||
free(url);
|
||||
free(lock);
|
||||
lock = NULL;
|
||||
@ -1344,8 +1341,7 @@ static int unlock_remote(struct remote_lock *lock)
|
||||
prev->next = prev->next->next;
|
||||
}
|
||||
|
||||
if (lock->owner != NULL)
|
||||
free(lock->owner);
|
||||
free(lock->owner);
|
||||
free(lock->url);
|
||||
free(lock->token);
|
||||
free(lock);
|
||||
@ -2035,8 +2031,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
|
||||
}
|
||||
free(url);
|
||||
|
||||
if (*symref != NULL)
|
||||
free(*symref);
|
||||
free(*symref);
|
||||
*symref = NULL;
|
||||
hashclr(sha1);
|
||||
|
||||
@ -2435,8 +2430,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (rewritten_url)
|
||||
free(rewritten_url);
|
||||
free(rewritten_url);
|
||||
if (info_ref_lock)
|
||||
unlock_remote(info_ref_lock);
|
||||
free(remote);
|
||||
|
@ -472,7 +472,7 @@ v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
|
||||
if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
|
||||
free( cmd->cmd );
|
||||
free( cmd );
|
||||
if (cb && cb->data)
|
||||
if (cb)
|
||||
free( cb->data );
|
||||
return NULL;
|
||||
}
|
||||
@ -858,8 +858,7 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
|
||||
normal:
|
||||
if (cmdp->cb.done)
|
||||
cmdp->cb.done( ctx, cmdp, resp );
|
||||
if (cmdp->cb.data)
|
||||
free( cmdp->cb.data );
|
||||
free( cmdp->cb.data );
|
||||
free( cmdp->cmd );
|
||||
free( cmdp );
|
||||
if (!tcmd || tcmd == cmdp)
|
||||
|
@ -11,8 +11,7 @@ void interp_set_entry(struct interp *table, int slot, const char *value)
|
||||
char *oldval = table[slot].value;
|
||||
char *newval = NULL;
|
||||
|
||||
if (oldval)
|
||||
free(oldval);
|
||||
free(oldval);
|
||||
|
||||
if (value)
|
||||
newval = xstrdup(value);
|
||||
|
126
log-tree.c
126
log-tree.c
@ -137,6 +137,72 @@ static int has_non_ascii(const char *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
const char **subject_p, const char **extra_headers_p)
|
||||
{
|
||||
const char *subject = NULL;
|
||||
const char *extra_headers = opt->extra_headers;
|
||||
if (opt->total > 0) {
|
||||
static char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s %0*d/%d] ",
|
||||
opt->subject_prefix,
|
||||
digits_in_number(opt->total),
|
||||
opt->nr, opt->total);
|
||||
subject = buffer;
|
||||
} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
|
||||
static char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s] ",
|
||||
opt->subject_prefix);
|
||||
subject = buffer;
|
||||
} else {
|
||||
subject = "Subject: ";
|
||||
}
|
||||
|
||||
printf("From %s Mon Sep 17 00:00:00 2001\n", name);
|
||||
if (opt->message_id)
|
||||
printf("Message-Id: <%s>\n", opt->message_id);
|
||||
if (opt->ref_message_id)
|
||||
printf("In-Reply-To: <%s>\nReferences: <%s>\n",
|
||||
opt->ref_message_id, opt->ref_message_id);
|
||||
if (opt->mime_boundary) {
|
||||
static char subject_buffer[1024];
|
||||
static char buffer[1024];
|
||||
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
|
||||
"%s"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: multipart/mixed;"
|
||||
" boundary=\"%s%s\"\n"
|
||||
"\n"
|
||||
"This is a multi-part message in MIME "
|
||||
"format.\n"
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/plain; "
|
||||
"charset=UTF-8; format=fixed\n"
|
||||
"Content-Transfer-Encoding: 8bit\n\n",
|
||||
extra_headers ? extra_headers : "",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
mime_boundary_leader, opt->mime_boundary);
|
||||
extra_headers = subject_buffer;
|
||||
|
||||
snprintf(buffer, sizeof(buffer) - 1,
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/x-patch;"
|
||||
" name=\"%s.diff\"\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Content-Disposition: %s;"
|
||||
" filename=\"%s.diff\"\n\n",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
name,
|
||||
opt->no_inline ? "attachment" : "inline",
|
||||
name);
|
||||
opt->diffopt.stat_sep = buffer;
|
||||
}
|
||||
*subject_p = subject;
|
||||
*extra_headers_p = extra_headers;
|
||||
}
|
||||
|
||||
void show_log(struct rev_info *opt, const char *sep)
|
||||
{
|
||||
struct strbuf msgbuf;
|
||||
@ -188,64 +254,8 @@ void show_log(struct rev_info *opt, const char *sep)
|
||||
*/
|
||||
|
||||
if (opt->commit_format == CMIT_FMT_EMAIL) {
|
||||
char *sha1 = sha1_to_hex(commit->object.sha1);
|
||||
if (opt->total > 0) {
|
||||
static char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s %0*d/%d] ",
|
||||
opt->subject_prefix,
|
||||
digits_in_number(opt->total),
|
||||
opt->nr, opt->total);
|
||||
subject = buffer;
|
||||
} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
|
||||
static char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Subject: [%s] ",
|
||||
opt->subject_prefix);
|
||||
subject = buffer;
|
||||
} else {
|
||||
subject = "Subject: ";
|
||||
}
|
||||
|
||||
printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
|
||||
if (opt->message_id)
|
||||
printf("Message-Id: <%s>\n", opt->message_id);
|
||||
if (opt->ref_message_id)
|
||||
printf("In-Reply-To: <%s>\nReferences: <%s>\n",
|
||||
opt->ref_message_id, opt->ref_message_id);
|
||||
if (opt->mime_boundary) {
|
||||
static char subject_buffer[1024];
|
||||
static char buffer[1024];
|
||||
snprintf(subject_buffer, sizeof(subject_buffer) - 1,
|
||||
"%s"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: multipart/mixed;"
|
||||
" boundary=\"%s%s\"\n"
|
||||
"\n"
|
||||
"This is a multi-part message in MIME "
|
||||
"format.\n"
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/plain; "
|
||||
"charset=UTF-8; format=fixed\n"
|
||||
"Content-Transfer-Encoding: 8bit\n\n",
|
||||
extra_headers ? extra_headers : "",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
mime_boundary_leader, opt->mime_boundary);
|
||||
extra_headers = subject_buffer;
|
||||
|
||||
snprintf(buffer, sizeof(buffer) - 1,
|
||||
"--%s%s\n"
|
||||
"Content-Type: text/x-patch;"
|
||||
" name=\"%s.diff\"\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Content-Disposition: %s;"
|
||||
" filename=\"%s.diff\"\n\n",
|
||||
mime_boundary_leader, opt->mime_boundary,
|
||||
sha1,
|
||||
opt->no_inline ? "attachment" : "inline",
|
||||
sha1);
|
||||
opt->diffopt.stat_sep = buffer;
|
||||
}
|
||||
log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
|
||||
&subject, &extra_headers);
|
||||
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
|
||||
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
|
||||
if (opt->commit_format != CMIT_FMT_ONELINE)
|
||||
|
@ -13,5 +13,7 @@ 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);
|
||||
void log_write_email_headers(struct rev_info *opt, const char *name,
|
||||
const char **subject_p, const char **extra_headers_p);
|
||||
|
||||
#endif
|
||||
|
20
merge-recursive.h
Normal file
20
merge-recursive.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef MERGE_RECURSIVE_H
|
||||
#define MERGE_RECURSIVE_H
|
||||
|
||||
int merge_recursive(struct commit *h1,
|
||||
struct commit *h2,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct commit_list *ancestors,
|
||||
struct commit **result);
|
||||
|
||||
int merge_trees(struct tree *head,
|
||||
struct tree *merge,
|
||||
struct tree *common,
|
||||
const char *branch1,
|
||||
const char *branch2,
|
||||
struct tree **result);
|
||||
|
||||
struct tree *write_tree_from_memory(void);
|
||||
|
||||
#endif
|
35
pretty.c
35
pretty.c
@ -30,8 +30,7 @@ enum cmit_fmt get_commit_format(const char *arg)
|
||||
if (*arg == '=')
|
||||
arg++;
|
||||
if (!prefixcmp(arg, "format:")) {
|
||||
if (user_format)
|
||||
free(user_format);
|
||||
free(user_format);
|
||||
user_format = xstrdup(arg + 7);
|
||||
return CMIT_FMT_USERFORMAT;
|
||||
}
|
||||
@ -110,9 +109,9 @@ needquote:
|
||||
strbuf_addstr(sb, "?=");
|
||||
}
|
||||
|
||||
static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding)
|
||||
void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
|
||||
const char *line, enum date_mode dmode,
|
||||
const char *encoding)
|
||||
{
|
||||
char *date;
|
||||
int namelen;
|
||||
@ -621,23 +620,23 @@ static void pp_header(enum cmit_fmt fmt,
|
||||
*/
|
||||
if (!memcmp(line, "author ", 7)) {
|
||||
strbuf_grow(sb, linelen + 80);
|
||||
add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
|
||||
pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
|
||||
}
|
||||
if (!memcmp(line, "committer ", 10) &&
|
||||
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
|
||||
strbuf_grow(sb, linelen + 80);
|
||||
add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
|
||||
pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int plain_non_ascii)
|
||||
void pp_title_line(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *subject,
|
||||
const char *after_subject,
|
||||
const char *encoding,
|
||||
int plain_non_ascii)
|
||||
{
|
||||
struct strbuf title;
|
||||
|
||||
@ -686,10 +685,10 @@ static void pp_title_line(enum cmit_fmt fmt,
|
||||
strbuf_release(&title);
|
||||
}
|
||||
|
||||
static void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent)
|
||||
void pp_remainder(enum cmit_fmt fmt,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent)
|
||||
{
|
||||
int first = 1;
|
||||
for (;;) {
|
||||
|
40
read-cache.c
40
read-cache.c
@ -37,8 +37,13 @@ static unsigned int hash_name(const char *name, int namelen)
|
||||
static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
|
||||
{
|
||||
void **pos;
|
||||
unsigned int hash = hash_name(ce->name, ce_namelen(ce));
|
||||
unsigned int hash;
|
||||
|
||||
if (ce->ce_flags & CE_HASHED)
|
||||
return;
|
||||
ce->ce_flags |= CE_HASHED;
|
||||
ce->next = NULL;
|
||||
hash = hash_name(ce->name, ce_namelen(ce));
|
||||
pos = insert_hash(hash, ce, &istate->name_hash);
|
||||
if (pos) {
|
||||
ce->next = *pos;
|
||||
@ -59,33 +64,18 @@ static void lazy_init_name_hash(struct index_state *istate)
|
||||
|
||||
static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
|
||||
{
|
||||
ce->ce_flags &= ~CE_UNHASHED;
|
||||
istate->cache[nr] = ce;
|
||||
if (istate->name_hash_initialized)
|
||||
hash_index_entry(istate, ce);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't actually *remove* it, we can just mark it invalid so that
|
||||
* we won't find it in lookups.
|
||||
*
|
||||
* Not only would we have to search the lists (simple enough), but
|
||||
* we'd also have to rehash other hash buckets in case this makes the
|
||||
* hash bucket empty (common). So it's much better to just mark
|
||||
* it.
|
||||
*/
|
||||
static void remove_hash_entry(struct index_state *istate, struct cache_entry *ce)
|
||||
{
|
||||
ce->ce_flags |= CE_UNHASHED;
|
||||
}
|
||||
|
||||
static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
|
||||
{
|
||||
struct cache_entry *old = istate->cache[nr];
|
||||
|
||||
if (ce != old) {
|
||||
remove_hash_entry(istate, old);
|
||||
set_index_entry(istate, nr, ce);
|
||||
}
|
||||
remove_index_entry(old);
|
||||
set_index_entry(istate, nr, ce);
|
||||
istate->cache_changed = 1;
|
||||
}
|
||||
|
||||
@ -413,7 +403,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
|
||||
{
|
||||
struct cache_entry *ce = istate->cache[pos];
|
||||
|
||||
remove_hash_entry(istate, ce);
|
||||
remove_index_entry(ce);
|
||||
istate->cache_changed = 1;
|
||||
istate->cache_nr--;
|
||||
if (pos >= istate->cache_nr)
|
||||
@ -1176,6 +1166,16 @@ int discard_index(struct index_state *istate)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unmerged_index(struct index_state *istate)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
if (ce_stage(istate->cache[i]))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WRITE_BUFFER_SIZE 8192
|
||||
static unsigned char write_buffer[WRITE_BUFFER_SIZE];
|
||||
static unsigned long write_buffer_len;
|
||||
|
@ -132,6 +132,7 @@ static int run_hook(const char *hook_name)
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(proc.in);
|
||||
return hook_status(finish_command(&proc), hook_name);
|
||||
}
|
||||
|
||||
@ -414,6 +415,7 @@ static const char *unpack(void)
|
||||
if (start_command(&ip))
|
||||
return "index-pack fork failed";
|
||||
pack_lockfile = index_pack_lockfile(ip.out);
|
||||
close(ip.out);
|
||||
status = finish_command(&ip);
|
||||
if (!status) {
|
||||
reprepare_packed_git();
|
||||
|
27
refs.c
27
refs.c
@ -157,6 +157,7 @@ static struct cached_refs {
|
||||
struct ref_list *loose;
|
||||
struct ref_list *packed;
|
||||
} cached_refs;
|
||||
static struct ref_list *current_ref;
|
||||
|
||||
static void free_ref_list(struct ref_list *list)
|
||||
{
|
||||
@ -476,6 +477,7 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
|
||||
error("%s does not point to a valid object!", entry->name);
|
||||
return 0;
|
||||
}
|
||||
current_ref = entry;
|
||||
return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
|
||||
}
|
||||
|
||||
@ -485,6 +487,16 @@ int peel_ref(const char *ref, unsigned char *sha1)
|
||||
unsigned char base[20];
|
||||
struct object *o;
|
||||
|
||||
if (current_ref && (current_ref->name == ref
|
||||
|| !strcmp(current_ref->name, ref))) {
|
||||
if (current_ref->flag & REF_KNOWS_PEELED) {
|
||||
hashcpy(sha1, current_ref->peeled);
|
||||
return 0;
|
||||
}
|
||||
hashcpy(base, current_ref->sha1);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
if (!resolve_ref(ref, base, 1, &flag))
|
||||
return -1;
|
||||
|
||||
@ -504,9 +516,9 @@ int peel_ref(const char *ref, unsigned char *sha1)
|
||||
}
|
||||
}
|
||||
|
||||
/* fallback - callers should not call this for unpacked refs */
|
||||
fallback:
|
||||
o = parse_object(base);
|
||||
if (o->type == OBJ_TAG) {
|
||||
if (o && o->type == OBJ_TAG) {
|
||||
o = deref_tag(o, ref, 0);
|
||||
if (o) {
|
||||
hashcpy(sha1, o->sha1);
|
||||
@ -519,7 +531,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
|
||||
static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
|
||||
void *cb_data)
|
||||
{
|
||||
int retval;
|
||||
int retval = 0;
|
||||
struct ref_list *packed = get_packed_refs();
|
||||
struct ref_list *loose = get_loose_refs();
|
||||
|
||||
@ -539,15 +551,18 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
|
||||
}
|
||||
retval = do_one_ref(base, fn, trim, cb_data, entry);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto end_each;
|
||||
}
|
||||
|
||||
for (packed = packed ? packed : loose; packed; packed = packed->next) {
|
||||
retval = do_one_ref(base, fn, trim, cb_data, packed);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto end_each;
|
||||
}
|
||||
return 0;
|
||||
|
||||
end_each:
|
||||
current_ref = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int head_ref(each_ref_fn fn, void *cb_data)
|
||||
|
250
remote.c
250
remote.c
@ -2,123 +2,184 @@
|
||||
#include "remote.h"
|
||||
#include "refs.h"
|
||||
|
||||
struct counted_string {
|
||||
size_t len;
|
||||
const char *s;
|
||||
};
|
||||
struct rewrite {
|
||||
const char *base;
|
||||
size_t baselen;
|
||||
struct counted_string *instead_of;
|
||||
int instead_of_nr;
|
||||
int instead_of_alloc;
|
||||
};
|
||||
|
||||
static struct remote **remotes;
|
||||
static int allocated_remotes;
|
||||
static int remotes_alloc;
|
||||
static int remotes_nr;
|
||||
|
||||
static struct branch **branches;
|
||||
static int allocated_branches;
|
||||
static int branches_alloc;
|
||||
static int branches_nr;
|
||||
|
||||
static struct branch *current_branch;
|
||||
static const char *default_remote_name;
|
||||
|
||||
static struct rewrite **rewrite;
|
||||
static int rewrite_alloc;
|
||||
static int rewrite_nr;
|
||||
|
||||
#define BUF_SIZE (2048)
|
||||
static char buffer[BUF_SIZE];
|
||||
|
||||
static const char *alias_url(const char *url)
|
||||
{
|
||||
int i, j;
|
||||
char *ret;
|
||||
struct counted_string *longest;
|
||||
int longest_i;
|
||||
|
||||
longest = NULL;
|
||||
longest_i = -1;
|
||||
for (i = 0; i < rewrite_nr; i++) {
|
||||
if (!rewrite[i])
|
||||
continue;
|
||||
for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
|
||||
if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
|
||||
(!longest ||
|
||||
longest->len < rewrite[i]->instead_of[j].len)) {
|
||||
longest = &(rewrite[i]->instead_of[j]);
|
||||
longest_i = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!longest)
|
||||
return url;
|
||||
|
||||
ret = malloc(rewrite[longest_i]->baselen +
|
||||
(strlen(url) - longest->len) + 1);
|
||||
strcpy(ret, rewrite[longest_i]->base);
|
||||
strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void add_push_refspec(struct remote *remote, const char *ref)
|
||||
{
|
||||
int nr = remote->push_refspec_nr + 1;
|
||||
remote->push_refspec =
|
||||
xrealloc(remote->push_refspec, nr * sizeof(char *));
|
||||
remote->push_refspec[nr-1] = ref;
|
||||
remote->push_refspec_nr = nr;
|
||||
ALLOC_GROW(remote->push_refspec,
|
||||
remote->push_refspec_nr + 1,
|
||||
remote->push_refspec_alloc);
|
||||
remote->push_refspec[remote->push_refspec_nr++] = ref;
|
||||
}
|
||||
|
||||
static void add_fetch_refspec(struct remote *remote, const char *ref)
|
||||
{
|
||||
int nr = remote->fetch_refspec_nr + 1;
|
||||
remote->fetch_refspec =
|
||||
xrealloc(remote->fetch_refspec, nr * sizeof(char *));
|
||||
remote->fetch_refspec[nr-1] = ref;
|
||||
remote->fetch_refspec_nr = nr;
|
||||
ALLOC_GROW(remote->fetch_refspec,
|
||||
remote->fetch_refspec_nr + 1,
|
||||
remote->fetch_refspec_alloc);
|
||||
remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
|
||||
}
|
||||
|
||||
static void add_url(struct remote *remote, const char *url)
|
||||
{
|
||||
int nr = remote->url_nr + 1;
|
||||
remote->url =
|
||||
xrealloc(remote->url, nr * sizeof(char *));
|
||||
remote->url[nr-1] = url;
|
||||
remote->url_nr = nr;
|
||||
ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
|
||||
remote->url[remote->url_nr++] = url;
|
||||
}
|
||||
|
||||
static void add_url_alias(struct remote *remote, const char *url)
|
||||
{
|
||||
add_url(remote, alias_url(url));
|
||||
}
|
||||
|
||||
static struct remote *make_remote(const char *name, int len)
|
||||
{
|
||||
int i, empty = -1;
|
||||
struct remote *ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < allocated_remotes; i++) {
|
||||
if (!remotes[i]) {
|
||||
if (empty < 0)
|
||||
empty = i;
|
||||
} else {
|
||||
if (len ? (!strncmp(name, remotes[i]->name, len) &&
|
||||
!remotes[i]->name[len]) :
|
||||
!strcmp(name, remotes[i]->name))
|
||||
return remotes[i];
|
||||
}
|
||||
for (i = 0; i < remotes_nr; i++) {
|
||||
if (len ? (!strncmp(name, remotes[i]->name, len) &&
|
||||
!remotes[i]->name[len]) :
|
||||
!strcmp(name, remotes[i]->name))
|
||||
return remotes[i];
|
||||
}
|
||||
|
||||
if (empty < 0) {
|
||||
empty = allocated_remotes;
|
||||
allocated_remotes += allocated_remotes ? allocated_remotes : 1;
|
||||
remotes = xrealloc(remotes,
|
||||
sizeof(*remotes) * allocated_remotes);
|
||||
memset(remotes + empty, 0,
|
||||
(allocated_remotes - empty) * sizeof(*remotes));
|
||||
}
|
||||
remotes[empty] = xcalloc(1, sizeof(struct remote));
|
||||
ret = xcalloc(1, sizeof(struct remote));
|
||||
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
|
||||
remotes[remotes_nr++] = ret;
|
||||
if (len)
|
||||
remotes[empty]->name = xstrndup(name, len);
|
||||
ret->name = xstrndup(name, len);
|
||||
else
|
||||
remotes[empty]->name = xstrdup(name);
|
||||
return remotes[empty];
|
||||
ret->name = xstrdup(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void add_merge(struct branch *branch, const char *name)
|
||||
{
|
||||
int nr = branch->merge_nr + 1;
|
||||
branch->merge_name =
|
||||
xrealloc(branch->merge_name, nr * sizeof(char *));
|
||||
branch->merge_name[nr-1] = name;
|
||||
branch->merge_nr = nr;
|
||||
ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
|
||||
branch->merge_alloc);
|
||||
branch->merge_name[branch->merge_nr++] = name;
|
||||
}
|
||||
|
||||
static struct branch *make_branch(const char *name, int len)
|
||||
{
|
||||
int i, empty = -1;
|
||||
struct branch *ret;
|
||||
int i;
|
||||
char *refname;
|
||||
|
||||
for (i = 0; i < allocated_branches; i++) {
|
||||
if (!branches[i]) {
|
||||
if (empty < 0)
|
||||
empty = i;
|
||||
} else {
|
||||
if (len ? (!strncmp(name, branches[i]->name, len) &&
|
||||
!branches[i]->name[len]) :
|
||||
!strcmp(name, branches[i]->name))
|
||||
return branches[i];
|
||||
}
|
||||
for (i = 0; i < branches_nr; i++) {
|
||||
if (len ? (!strncmp(name, branches[i]->name, len) &&
|
||||
!branches[i]->name[len]) :
|
||||
!strcmp(name, branches[i]->name))
|
||||
return branches[i];
|
||||
}
|
||||
|
||||
if (empty < 0) {
|
||||
empty = allocated_branches;
|
||||
allocated_branches += allocated_branches ? allocated_branches : 1;
|
||||
branches = xrealloc(branches,
|
||||
sizeof(*branches) * allocated_branches);
|
||||
memset(branches + empty, 0,
|
||||
(allocated_branches - empty) * sizeof(*branches));
|
||||
}
|
||||
branches[empty] = xcalloc(1, sizeof(struct branch));
|
||||
ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
|
||||
ret = xcalloc(1, sizeof(struct branch));
|
||||
branches[branches_nr++] = ret;
|
||||
if (len)
|
||||
branches[empty]->name = xstrndup(name, len);
|
||||
ret->name = xstrndup(name, len);
|
||||
else
|
||||
branches[empty]->name = xstrdup(name);
|
||||
ret->name = xstrdup(name);
|
||||
refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
|
||||
strcpy(refname, "refs/heads/");
|
||||
strcpy(refname + strlen("refs/heads/"),
|
||||
branches[empty]->name);
|
||||
branches[empty]->refname = refname;
|
||||
strcpy(refname + strlen("refs/heads/"), ret->name);
|
||||
ret->refname = refname;
|
||||
|
||||
return branches[empty];
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct rewrite *make_rewrite(const char *base, int len)
|
||||
{
|
||||
struct rewrite *ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < rewrite_nr; i++) {
|
||||
if (len
|
||||
? (len == rewrite[i]->baselen &&
|
||||
!strncmp(base, rewrite[i]->base, len))
|
||||
: !strcmp(base, rewrite[i]->base))
|
||||
return rewrite[i];
|
||||
}
|
||||
|
||||
ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
|
||||
ret = xcalloc(1, sizeof(struct rewrite));
|
||||
rewrite[rewrite_nr++] = ret;
|
||||
if (len) {
|
||||
ret->base = xstrndup(base, len);
|
||||
ret->baselen = len;
|
||||
}
|
||||
else {
|
||||
ret->base = xstrdup(base);
|
||||
ret->baselen = strlen(base);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
|
||||
{
|
||||
ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
|
||||
rewrite->instead_of[rewrite->instead_of_nr].s = instead_of;
|
||||
rewrite->instead_of[rewrite->instead_of_nr].len = strlen(instead_of);
|
||||
rewrite->instead_of_nr++;
|
||||
}
|
||||
|
||||
static void read_remotes_file(struct remote *remote)
|
||||
@ -154,7 +215,7 @@ static void read_remotes_file(struct remote *remote)
|
||||
|
||||
switch (value_list) {
|
||||
case 0:
|
||||
add_url(remote, xstrdup(s));
|
||||
add_url_alias(remote, xstrdup(s));
|
||||
break;
|
||||
case 1:
|
||||
add_push_refspec(remote, xstrdup(s));
|
||||
@ -206,7 +267,7 @@ static void read_branches_file(struct remote *remote)
|
||||
} else {
|
||||
branch = "refs/heads/master";
|
||||
}
|
||||
add_url(remote, p);
|
||||
add_url_alias(remote, p);
|
||||
add_fetch_refspec(remote, branch);
|
||||
remote->fetch_tags = 1; /* always auto-follow */
|
||||
}
|
||||
@ -236,6 +297,19 @@ static int handle_config(const char *key, const char *value)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (!prefixcmp(key, "url.")) {
|
||||
struct rewrite *rewrite;
|
||||
name = key + 5;
|
||||
subkey = strrchr(name, '.');
|
||||
if (!subkey)
|
||||
return 0;
|
||||
rewrite = make_rewrite(name, subkey - name);
|
||||
if (!strcmp(subkey, ".insteadof")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(key);
|
||||
add_instead_of(rewrite, xstrdup(value));
|
||||
}
|
||||
}
|
||||
if (prefixcmp(key, "remote."))
|
||||
return 0;
|
||||
name = key + 7;
|
||||
@ -287,6 +361,18 @@ static int handle_config(const char *key, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alias_all_urls(void)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < remotes_nr; i++) {
|
||||
if (!remotes[i])
|
||||
continue;
|
||||
for (j = 0; j < remotes[i]->url_nr; j++) {
|
||||
remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void read_config(void)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
@ -303,6 +389,7 @@ static void read_config(void)
|
||||
make_branch(head_ref + strlen("refs/heads/"), 0);
|
||||
}
|
||||
git_config(handle_config);
|
||||
alias_all_urls();
|
||||
}
|
||||
|
||||
struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
|
||||
@ -368,7 +455,7 @@ struct remote *remote_get(const char *name)
|
||||
read_branches_file(ret);
|
||||
}
|
||||
if (!ret->url)
|
||||
add_url(ret, name);
|
||||
add_url_alias(ret, name);
|
||||
if (!ret->url)
|
||||
return NULL;
|
||||
ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
|
||||
@ -380,7 +467,7 @@ int for_each_remote(each_remote_fn fn, void *priv)
|
||||
{
|
||||
int i, result = 0;
|
||||
read_config();
|
||||
for (i = 0; i < allocated_remotes && !result; i++) {
|
||||
for (i = 0; i < remotes_nr && !result; i++) {
|
||||
struct remote *r = remotes[i];
|
||||
if (!r)
|
||||
continue;
|
||||
@ -506,8 +593,7 @@ void free_refs(struct ref *ref)
|
||||
struct ref *next;
|
||||
while (ref) {
|
||||
next = ref->next;
|
||||
if (ref->peer_ref)
|
||||
free(ref->peer_ref);
|
||||
free(ref->peer_ref);
|
||||
free(ref);
|
||||
ref = next;
|
||||
}
|
||||
@ -643,9 +729,17 @@ static int match_explicit(struct ref *src, struct ref *dst,
|
||||
errs = 1;
|
||||
|
||||
if (!dst_value) {
|
||||
unsigned char sha1[20];
|
||||
int flag;
|
||||
|
||||
if (!matched_src)
|
||||
return errs;
|
||||
dst_value = matched_src->name;
|
||||
dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
|
||||
if (!dst_value ||
|
||||
((flag & REF_ISSYMREF) &&
|
||||
prefixcmp(dst_value, "refs/heads/")))
|
||||
die("%s cannot be resolved to branch.",
|
||||
matched_src->name);
|
||||
}
|
||||
|
||||
switch (count_refspec_match(dst_value, dst, &matched_dst)) {
|
||||
|
4
remote.h
4
remote.h
@ -6,14 +6,17 @@ struct remote {
|
||||
|
||||
const char **url;
|
||||
int url_nr;
|
||||
int url_alloc;
|
||||
|
||||
const char **push_refspec;
|
||||
struct refspec *push;
|
||||
int push_refspec_nr;
|
||||
int push_refspec_alloc;
|
||||
|
||||
const char **fetch_refspec;
|
||||
struct refspec *fetch;
|
||||
int fetch_refspec_nr;
|
||||
int fetch_refspec_alloc;
|
||||
|
||||
/*
|
||||
* -1 to never fetch tags
|
||||
@ -100,6 +103,7 @@ struct branch {
|
||||
const char **merge_name;
|
||||
struct refspec **merge;
|
||||
int merge_nr;
|
||||
int merge_alloc;
|
||||
};
|
||||
|
||||
struct branch *branch_get(const char *name);
|
||||
|
14
revision.c
14
revision.c
@ -738,6 +738,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
|
||||
revs->commit_format = CMIT_FMT_DEFAULT;
|
||||
|
||||
diff_setup(&revs->diffopt);
|
||||
if (prefix && !revs->diffopt.prefix) {
|
||||
revs->diffopt.prefix = prefix;
|
||||
revs->diffopt.prefix_length = strlen(prefix);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_pending_commit_list(struct rev_info *revs,
|
||||
@ -942,6 +946,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
int left = 1;
|
||||
int all_match = 0;
|
||||
int regflags = 0;
|
||||
int fixed = 0;
|
||||
|
||||
/* First, search for "--" */
|
||||
seen_dashdash = 0;
|
||||
@ -1238,6 +1243,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
regflags |= REG_ICASE;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--fixed-strings") ||
|
||||
!strcmp(arg, "-F")) {
|
||||
fixed = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all-match")) {
|
||||
all_match = 1;
|
||||
continue;
|
||||
@ -1293,8 +1303,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
}
|
||||
}
|
||||
|
||||
if (revs->grep_filter)
|
||||
if (revs->grep_filter) {
|
||||
revs->grep_filter->regflags |= regflags;
|
||||
revs->grep_filter->fixed = fixed;
|
||||
}
|
||||
|
||||
if (show_merge)
|
||||
prepare_show_merge(revs);
|
||||
|
@ -75,7 +75,7 @@ struct rev_info {
|
||||
struct log_info *loginfo;
|
||||
int nr, total;
|
||||
const char *mime_boundary;
|
||||
const char *message_id;
|
||||
char *message_id;
|
||||
const char *ref_message_id;
|
||||
const char *add_signoff;
|
||||
const char *extra_headers;
|
||||
|
@ -20,12 +20,19 @@ int start_command(struct child_process *cmd)
|
||||
int need_in, need_out, need_err;
|
||||
int fdin[2], fdout[2], fderr[2];
|
||||
|
||||
/*
|
||||
* In case of errors we must keep the promise to close FDs
|
||||
* that have been passed in via ->in and ->out.
|
||||
*/
|
||||
|
||||
need_in = !cmd->no_stdin && cmd->in < 0;
|
||||
if (need_in) {
|
||||
if (pipe(fdin) < 0)
|
||||
if (pipe(fdin) < 0) {
|
||||
if (cmd->out > 0)
|
||||
close(cmd->out);
|
||||
return -ERR_RUN_COMMAND_PIPE;
|
||||
}
|
||||
cmd->in = fdin[1];
|
||||
cmd->close_in = 1;
|
||||
}
|
||||
|
||||
need_out = !cmd->no_stdout
|
||||
@ -35,10 +42,11 @@ int start_command(struct child_process *cmd)
|
||||
if (pipe(fdout) < 0) {
|
||||
if (need_in)
|
||||
close_pair(fdin);
|
||||
else if (cmd->in)
|
||||
close(cmd->in);
|
||||
return -ERR_RUN_COMMAND_PIPE;
|
||||
}
|
||||
cmd->out = fdout[0];
|
||||
cmd->close_out = 1;
|
||||
}
|
||||
|
||||
need_err = !cmd->no_stderr && cmd->err < 0;
|
||||
@ -46,8 +54,12 @@ int start_command(struct child_process *cmd)
|
||||
if (pipe(fderr) < 0) {
|
||||
if (need_in)
|
||||
close_pair(fdin);
|
||||
else if (cmd->in)
|
||||
close(cmd->in);
|
||||
if (need_out)
|
||||
close_pair(fdout);
|
||||
else if (cmd->out)
|
||||
close(cmd->out);
|
||||
return -ERR_RUN_COMMAND_PIPE;
|
||||
}
|
||||
cmd->err = fderr[0];
|
||||
@ -57,8 +69,12 @@ int start_command(struct child_process *cmd)
|
||||
if (cmd->pid < 0) {
|
||||
if (need_in)
|
||||
close_pair(fdin);
|
||||
else if (cmd->in)
|
||||
close(cmd->in);
|
||||
if (need_out)
|
||||
close_pair(fdout);
|
||||
else if (cmd->out)
|
||||
close(cmd->out);
|
||||
if (need_err)
|
||||
close_pair(fderr);
|
||||
return -ERR_RUN_COMMAND_FORK;
|
||||
@ -120,7 +136,7 @@ int start_command(struct child_process *cmd)
|
||||
|
||||
if (need_out)
|
||||
close(fdout[1]);
|
||||
else if (cmd->out > 1)
|
||||
else if (cmd->out)
|
||||
close(cmd->out);
|
||||
|
||||
if (need_err)
|
||||
@ -157,10 +173,6 @@ static int wait_or_whine(pid_t pid)
|
||||
|
||||
int finish_command(struct child_process *cmd)
|
||||
{
|
||||
if (cmd->close_in)
|
||||
close(cmd->in);
|
||||
if (cmd->close_out)
|
||||
close(cmd->out);
|
||||
return wait_or_whine(cmd->pid);
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,29 @@ enum {
|
||||
struct child_process {
|
||||
const char **argv;
|
||||
pid_t pid;
|
||||
/*
|
||||
* Using .in, .out, .err:
|
||||
* - Specify 0 for no redirections (child inherits stdin, stdout,
|
||||
* stderr from parent).
|
||||
* - Specify -1 to have a pipe allocated as follows:
|
||||
* .in: returns the writable pipe end; parent writes to it,
|
||||
* the readable pipe end becomes child's stdin
|
||||
* .out, .err: returns the readable pipe end; parent reads from
|
||||
* it, the writable pipe end becomes child's stdout/stderr
|
||||
* The caller of start_command() must close the returned FDs
|
||||
* after it has completed reading from/writing to it!
|
||||
* - Specify > 0 to set a channel to a particular FD as follows:
|
||||
* .in: a readable FD, becomes child's stdin
|
||||
* .out: a writable FD, becomes child's stdout/stderr
|
||||
* .err > 0 not supported
|
||||
* The specified FD is closed by start_command(), even in case
|
||||
* of errors!
|
||||
*/
|
||||
int in;
|
||||
int out;
|
||||
int err;
|
||||
const char *dir;
|
||||
const char *const *env;
|
||||
unsigned close_in:1;
|
||||
unsigned close_out:1;
|
||||
unsigned no_stdin:1;
|
||||
unsigned no_stdout:1;
|
||||
unsigned no_stderr:1;
|
||||
|
3
setup.c
3
setup.c
@ -448,8 +448,7 @@ int check_repository_format_version(const char *var, const char *value)
|
||||
} else if (strcmp(var, "core.worktree") == 0) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
if (git_work_tree_cfg)
|
||||
free(git_work_tree_cfg);
|
||||
free(git_work_tree_cfg);
|
||||
git_work_tree_cfg = xstrdup(value);
|
||||
inside_work_tree = -1;
|
||||
}
|
||||
|
@ -625,8 +625,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
|
||||
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
|
||||
if (!parse_object(commit->object.sha1))
|
||||
continue;
|
||||
if (temp_commit_buffer)
|
||||
free(temp_commit_buffer);
|
||||
free(temp_commit_buffer);
|
||||
if (commit->buffer)
|
||||
p = commit->buffer;
|
||||
else {
|
||||
@ -643,8 +642,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (temp_commit_buffer)
|
||||
free(temp_commit_buffer);
|
||||
free(temp_commit_buffer);
|
||||
free_commit_list(list);
|
||||
for (l = backup; l; l = l->next)
|
||||
clear_commit_marks(l->item, ONELINE_SEEN);
|
||||
|
26
shortlog.h
Normal file
26
shortlog.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef SHORTLOG_H
|
||||
#define SHORTLOG_H
|
||||
|
||||
#include "path-list.h"
|
||||
|
||||
struct shortlog {
|
||||
struct path_list list;
|
||||
int summary;
|
||||
int wrap_lines;
|
||||
int sort_by_number;
|
||||
int wrap;
|
||||
int in1;
|
||||
int in2;
|
||||
|
||||
char *common_repo_prefix;
|
||||
int email;
|
||||
struct path_list mailmap;
|
||||
};
|
||||
|
||||
void shortlog_init(struct shortlog *log);
|
||||
|
||||
void shortlog_add_commit(struct shortlog *log, struct commit *commit);
|
||||
|
||||
void shortlog_output(struct shortlog *log);
|
||||
|
||||
#endif
|
93
t/t0050-filesystem.sh
Executable file
93
t/t0050-filesystem.sh
Executable file
@ -0,0 +1,93 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='Various filesystem issues'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
auml=`perl -CO -e 'print pack("U",0x00E4)'`
|
||||
aumlcdiar=`perl -CO -e 'print pack("U",0x0061).pack("U",0x0308)'`
|
||||
|
||||
test_expect_success 'see if we expect ' '
|
||||
|
||||
test_case=test_expect_success
|
||||
test_unicode=test_expect_success
|
||||
mkdir junk &&
|
||||
echo good >junk/CamelCase &&
|
||||
echo bad >junk/camelcase &&
|
||||
if test "$(cat junk/CamelCase)" != good
|
||||
then
|
||||
test_case=test_expect_failure
|
||||
say "will test on a case insensitive filesystem"
|
||||
fi &&
|
||||
rm -fr junk &&
|
||||
mkdir junk &&
|
||||
>junk/"$auml" &&
|
||||
case "$(cd junk && echo *)" in
|
||||
"$aumlcdiar")
|
||||
test_unicode=test_expect_failure
|
||||
say "will test on a unicode corrupting filesystem"
|
||||
;;
|
||||
*) ;;
|
||||
esac &&
|
||||
rm -fr junk
|
||||
'
|
||||
|
||||
test_expect_success "setup case tests" '
|
||||
|
||||
touch camelcase &&
|
||||
git add camelcase &&
|
||||
git commit -m "initial" &&
|
||||
git tag initial &&
|
||||
git checkout -b topic &&
|
||||
git mv camelcase tmp &&
|
||||
git mv tmp CamelCase &&
|
||||
git commit -m "rename" &&
|
||||
git checkout -f master
|
||||
|
||||
'
|
||||
|
||||
$test_case 'rename (case change)' '
|
||||
|
||||
git mv camelcase CamelCase &&
|
||||
git commit -m "rename"
|
||||
|
||||
'
|
||||
|
||||
$test_case 'merge (case change)' '
|
||||
|
||||
git reset --hard initial &&
|
||||
git merge topic
|
||||
|
||||
'
|
||||
|
||||
test_expect_success "setup unicode normalization tests" '
|
||||
|
||||
test_create_repo unicode &&
|
||||
cd unicode &&
|
||||
touch "$aumlcdiar" &&
|
||||
git add "$aumlcdiar" &&
|
||||
git commit -m initial
|
||||
git tag initial &&
|
||||
git checkout -b topic &&
|
||||
git mv $aumlcdiar tmp &&
|
||||
git mv tmp "$auml" &&
|
||||
git commit -m rename &&
|
||||
git checkout -f master
|
||||
|
||||
'
|
||||
|
||||
$test_unicode 'rename (silent unicode normalization)' '
|
||||
|
||||
git mv "$aumlcdiar" "$auml" &&
|
||||
git commit -m rename
|
||||
|
||||
'
|
||||
|
||||
$test_unicode 'merge (silent unicode normalization)' '
|
||||
|
||||
git reset --hard initial &&
|
||||
git merge topic
|
||||
|
||||
'
|
||||
|
||||
test_done
|
43
t/t1502-rev-parse-parseopt.sh
Executable file
43
t/t1502-rev-parse-parseopt.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test git rev-parse --parseopt'
|
||||
. ./test-lib.sh
|
||||
|
||||
cat > expect.err <<EOF
|
||||
usage: some-command [options] <args>...
|
||||
|
||||
some-command does foo and bar!
|
||||
|
||||
-h, --help show the help
|
||||
--foo some nifty option --foo
|
||||
--bar ... some cool option --bar with an argument
|
||||
|
||||
An option group Header
|
||||
-C [...] option C with an optional argument
|
||||
|
||||
Extras
|
||||
--extra1 line above used to cause a segfault but no longer does
|
||||
|
||||
EOF
|
||||
|
||||
test_expect_success 'test --parseopt help output' '
|
||||
git rev-parse --parseopt -- -h 2> output.err <<EOF
|
||||
some-command [options] <args>...
|
||||
|
||||
some-command does foo and bar!
|
||||
--
|
||||
h,help show the help
|
||||
|
||||
foo some nifty option --foo
|
||||
bar= some cool option --bar with an argument
|
||||
|
||||
An option group Header
|
||||
C? option C with an optional argument
|
||||
|
||||
Extras
|
||||
extra1 line above used to cause a segfault but no longer does
|
||||
EOF
|
||||
git diff expect.err output.err
|
||||
'
|
||||
|
||||
test_done
|
@ -68,15 +68,15 @@ test_expect_success 'checkout with simple prefix' '
|
||||
'
|
||||
|
||||
test_expect_success 'relative path outside tree should fail' \
|
||||
'! git checkout HEAD -- ../../Makefile'
|
||||
'test_must_fail git checkout HEAD -- ../../Makefile'
|
||||
|
||||
test_expect_success 'incorrect relative path to file should fail (1)' \
|
||||
'! git checkout HEAD -- ../file0'
|
||||
'test_must_fail git checkout HEAD -- ../file0'
|
||||
|
||||
test_expect_success 'incorrect relative path should fail (2)' \
|
||||
'( cd dir1 && ! git checkout HEAD -- ./file0 )'
|
||||
'( cd dir1 && test_must_fail git checkout HEAD -- ./file0 )'
|
||||
|
||||
test_expect_success 'incorrect relative path should fail (3)' \
|
||||
'( cd dir1 && ! git checkout HEAD -- ../../file0 )'
|
||||
'( cd dir1 && test_must_fail git checkout HEAD -- ../../file0 )'
|
||||
|
||||
test_done
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user