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:
Junio C Hamano 2008-03-03 01:20:19 -08:00
commit f830d45b9f
125 changed files with 4189 additions and 1334 deletions

View 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.

View 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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ]

View File

@ -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'::

View File

@ -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].

View File

@ -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.

View File

@ -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

View File

@ -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".

View File

@ -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');

View File

@ -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
View 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
View 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
View 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

View File

@ -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");

View File

@ -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);
}
}

View File

@ -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
View 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);
}

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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]);

View File

@ -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;

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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(&current_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) ||

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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
View File

@ -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 */

View File

@ -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.

View File

@ -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;

View File

@ -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);
}

View File

@ -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
;;

View File

@ -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)))))

View File

@ -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.

View File

@ -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
View File

@ -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
View File

@ -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 */
};

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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)

View File

@ -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;

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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
View File

@ -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 },

View File

@ -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);

View File

@ -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
View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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
View 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

View File

@ -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 (;;) {

View File

@ -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;

View File

@ -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
View File

@ -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
View File

@ -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)) {

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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
View 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
View 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
View 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

View File

@ -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