mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'mingw/master' into devel
Conflicts: Makefile builtin-tag.c compat/winansi.c convert.c t/t4109-apply-multifrag.sh
This commit is contained in:
@@ -97,15 +97,50 @@ Updates since v1.5.6
|
||||
when you are working in a slow network disk and are outside any working tree,
|
||||
as bash-completion and "git help" may still need to run in these places.
|
||||
|
||||
* By default, stash entries never expire. Set reflogexpire in [gc
|
||||
"refs/stash"] to a reasonable value to get traditional auto-expiration
|
||||
behaviour back
|
||||
|
||||
* Longstanding latency issue with bash completion script has been
|
||||
addressed. This will need to be backmerged to 'maint' later.
|
||||
|
||||
* pager.<cmd> configuration variable can be used to enable/disable the
|
||||
default paging behaviour per command.
|
||||
|
||||
* "git-add -i" has a new action 'e/dit' to allow you edit the patch hunk
|
||||
manually.
|
||||
|
||||
* git-am records the original tip of the branch in ORIG_HEAD before it
|
||||
starts applying patches.
|
||||
|
||||
* git-apply can handle a patch that touches the same path more than once
|
||||
much better than before.
|
||||
|
||||
* git-apply can be told not to trust the line counts recorded in the input
|
||||
patch but recount, with the new --recount option.
|
||||
|
||||
* git-apply can be told to apply a patch to a path deeper than what the
|
||||
patch records with --directory option.
|
||||
|
||||
* git-archive can be told to omit certain paths from its output using
|
||||
export-ignore attributes.
|
||||
|
||||
* With -v option, git-branch describes the remote tracking statistics
|
||||
similar to the way git-checkout reports by how many commits your branch
|
||||
is ahead/behind.
|
||||
|
||||
* git-branch's --contains option used to always require a commit parameter
|
||||
to limit the branches with; it now defaults to list branches that
|
||||
contains HEAD if this parameter is omitted.
|
||||
|
||||
* git-branch's --merged and --no-merged option used to always limit the
|
||||
branches relative to the HEAD, but they can now take an optional commit
|
||||
argument that is used in place of HEAD.
|
||||
|
||||
* git-bundle can read the revision arguments from the standard input.
|
||||
|
||||
* git-cherry-pick can replay a root commit now.
|
||||
|
||||
* git-clone can clone from a remote whose URL would be rewritten by
|
||||
configuration stored in $HOME/.gitconfig now.
|
||||
|
||||
@@ -120,11 +155,29 @@ Updates since v1.5.6
|
||||
* fast-export learned to export and import marks file; this can be used to
|
||||
interface with fast-import incrementally.
|
||||
|
||||
* git-rebase records the original tip of branch in ORIG_HEAD before it is
|
||||
rewound.
|
||||
|
||||
* "git rerere" can be told to update the index with auto-reused resolution
|
||||
with rerere.autoupdate configuration variable.
|
||||
|
||||
* git-rev-list learned --children option to show child commits it
|
||||
encountered during the traversal, instead of shoing parent commits.
|
||||
|
||||
* git-send-mail can talk not just over SSL but over TLS now.
|
||||
|
||||
* git-shortlog honors custom output format specified with "--pretty=format:".
|
||||
|
||||
* "git-stash save" learned --keep-index option. This lets you stash away the
|
||||
local changes and bring the changes staged in the index to your working
|
||||
tree for examination and testing.
|
||||
|
||||
* git-stash also learned branch subcommand to create a new branch out of
|
||||
stashed changes.
|
||||
|
||||
* git-status gives the remote tracking statistics similar to the way
|
||||
git-checkout reports by how many commits your branch is ahead/behind.
|
||||
|
||||
* You can tell "git status -u" to even more aggressively omit checking
|
||||
untracked files with --untracked-files=no.
|
||||
|
||||
@@ -148,6 +201,6 @@ this release, unless otherwise noted.
|
||||
|
||||
---
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.6.2-246-g86d7244
|
||||
O=v1.5.6.3-350-g499027b
|
||||
echo O=$(git describe refs/heads/master)
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
|
||||
@@ -301,7 +301,7 @@ If it does not apply correctly, there can be various reasons.
|
||||
patch appropriately.
|
||||
|
||||
* Your MUA corrupted your patch; "am" would complain that
|
||||
the patch does not apply. Look at .dotest/ subdirectory and
|
||||
the patch does not apply. Look at .git/rebase/ subdirectory and
|
||||
see what 'patch' file contains and check for the common
|
||||
corruption patterns mentioned above.
|
||||
|
||||
|
||||
@@ -187,8 +187,9 @@ update::
|
||||
"Update>>". When the prompt ends with double '>>', you can
|
||||
make more than one selection, concatenated with whitespace or
|
||||
comma. Also you can say ranges. E.g. "2-5 7,9" to choose
|
||||
2,3,4,5,7,9 from the list. You can say '*' to choose
|
||||
everything.
|
||||
2,3,4,5,7,9 from the list. If the second number in a range is
|
||||
omitted, all remaining patches are taken. E.g. "7-" to choose
|
||||
7,8,9 from the list. You can say '*' to choose everything.
|
||||
+
|
||||
What you chose are then highlighted with '*',
|
||||
like this:
|
||||
|
||||
@@ -140,11 +140,17 @@ aborts in the middle,. You can recover from this in one of two ways:
|
||||
the index file to bring it in a state that the patch should
|
||||
have produced. Then run the command with '--resolved' option.
|
||||
|
||||
The command refuses to process new mailboxes while `.dotest`
|
||||
The command refuses to process new mailboxes while `.git/rebase`
|
||||
directory exists, so if you decide to start over from scratch,
|
||||
run `rm -f -r .dotest` before running the command with mailbox
|
||||
run `rm -f -r .git/rebase` before running the command with mailbox
|
||||
names.
|
||||
|
||||
Before any patches are applied, ORIG_HEAD is set to the tip of the
|
||||
current branch. This is useful if you have problems with multiple
|
||||
commits, like running 'git am' on the wrong branch or an error in the
|
||||
commits that is more easily fixed by changing the mailbox (e.g.
|
||||
errors in the "From:" lines).
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
@@ -8,24 +8,27 @@ git-branch - List, create, or delete branches
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git branch' [--color | --no-color] [-r | -a] [--merged | --no-merged]
|
||||
[-v [--abbrev=<length> | --no-abbrev]]
|
||||
[--contains <commit>]
|
||||
'git branch' [--color | --no-color] [-r | -a]
|
||||
[-v [--abbrev=<length> | --no-abbrev]]
|
||||
[(--merged | --no-merged | --contains) [<commit>]]
|
||||
'git branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
|
||||
'git branch' (-m | -M) [<oldbranch>] <newbranch>
|
||||
'git branch' (-d | -D) [-r] <branchname>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
With no arguments given a list of existing branches
|
||||
will be shown, the current branch will be highlighted with an asterisk.
|
||||
Option `-r` causes the remote-tracking branches to be listed,
|
||||
and option `-a` shows both.
|
||||
With `--contains <commit>`, shows only the branches that
|
||||
contains the named commit (in other words, the branches whose
|
||||
tip commits are descendant of the named commit).
|
||||
With `--merged`, only branches merged into HEAD will be listed, and
|
||||
with `--no-merged` only branches not merged into HEAD will be listed.
|
||||
|
||||
With no arguments, existing branches are listed, the current branch will
|
||||
be highlighted with an asterisk. Option `-r` causes the remote-tracking
|
||||
branches to be listed, and option `-a` shows both.
|
||||
|
||||
With `--contains`, shows only the branches that contains the named commit
|
||||
(in other words, the branches whose tip commits are descendant of the
|
||||
named commit). With `--merged`, only branches merged into the named
|
||||
commit (i.e. the branches whose tip commits are reachable from the named
|
||||
commit) will be listed. With `--no-merged` only branches not merged into
|
||||
the named commit will be listed. Missing <commit> argument defaults to
|
||||
'HEAD' (i.e. the tip of the current branch).
|
||||
|
||||
In its second form, a new branch named <branchname> will be created.
|
||||
It will start out with a head equal to the one given as <start-point>.
|
||||
|
||||
@@ -81,7 +81,9 @@ Otherwise, merge will refuse to do any harm to your repository
|
||||
(that is, it may fetch the objects from remote, and it may even
|
||||
update the local branch used to keep track of the remote branch
|
||||
with `git pull remote rbranch:lbranch`, but your working tree,
|
||||
`.git/HEAD` pointer and index file are left intact).
|
||||
`.git/HEAD` pointer and index file are left intact). In addition,
|
||||
merge always sets `.git/ORIG_HEAD` to the original state of HEAD so
|
||||
a problematic merge can be removed by using `git reset ORIG_HEAD`.
|
||||
|
||||
You may have local modifications in the working tree files. In
|
||||
other words, 'git-diff' is allowed to report changes.
|
||||
|
||||
@@ -26,7 +26,8 @@ of commits that would be shown by `git log <upstream>..HEAD`.
|
||||
|
||||
The current branch is reset to <upstream>, or <newbase> if the
|
||||
--onto option was supplied. This has the exact same effect as
|
||||
`git reset --hard <upstream>` (or <newbase>).
|
||||
`git reset --hard <upstream>` (or <newbase>). ORIG_HEAD is set
|
||||
to point at the tip of the branch before the reset.
|
||||
|
||||
The commits that were previously saved into the temporary area are
|
||||
then reapplied to the current branch, one by one, in order. Note that
|
||||
@@ -38,7 +39,7 @@ It is possible that a merge failure will prevent this process from being
|
||||
completely automatic. You will have to resolve any such merge failure
|
||||
and run `git rebase --continue`. Another option is to bypass the commit
|
||||
that caused the merge failure with `git rebase --skip`. To restore the
|
||||
original <branch> and remove the .dotest working files, use the command
|
||||
original <branch> and remove the .git/rebase working files, use the command
|
||||
`git rebase --abort` instead.
|
||||
|
||||
Assume the following history exists and the current branch is "topic":
|
||||
|
||||
@@ -9,7 +9,7 @@ git-submodule - Initialize, update or inspect submodules
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git submodule' [--quiet] add [-b branch] [--] <repository> [<path>]
|
||||
'git submodule' [--quiet] add [-b branch] [--] <repository> <path>
|
||||
'git submodule' [--quiet] status [--cached] [--] [<path>...]
|
||||
'git submodule' [--quiet] init [--] [<path>...]
|
||||
'git submodule' [--quiet] update [--init] [--] [<path>...]
|
||||
@@ -20,14 +20,31 @@ COMMANDS
|
||||
--------
|
||||
add::
|
||||
Add the given repository as a submodule at the given path
|
||||
to the changeset to be committed next. If path is a valid
|
||||
repository within the project, it is added as is. Otherwise,
|
||||
repository is cloned at the specified path. path is added to the
|
||||
changeset and registered in .gitmodules. If no path is
|
||||
specified, the path is deduced from the repository specification.
|
||||
If the repository url begins with ./ or ../, it is stored as
|
||||
given but resolved as a relative path from the main project's
|
||||
url when cloning.
|
||||
to the changeset to be committed next to the current
|
||||
project: the current project is termed termed the "superproject".
|
||||
+
|
||||
This requires two arguments: <repository> and <path>.
|
||||
+
|
||||
<repository> is the URL of the new submodule's origin repository.
|
||||
This may be either an absolute URL, or (if it begins with ./
|
||||
or ../), the location relative to the superproject's origin
|
||||
repository.
|
||||
+
|
||||
<path> is the relative location for the cloned submodule to
|
||||
exist in the superproject. If <path> does not exist, then the
|
||||
submodule is created by cloning from the named URL. If <path> does
|
||||
exist and is already a valid git repository, then this is added
|
||||
to the changeset without cloning. This second form is provided
|
||||
to ease creating a new submodule from scratch, and presumes
|
||||
the user will later push the submodule to the given URL.
|
||||
+
|
||||
In either case, the given URL is recorded into .gitmodules for
|
||||
use by subsequent users cloning the superproject. If the URL is
|
||||
given relative to the superproject's repository, the presumption
|
||||
is the superproject and submodule repositories will be kept
|
||||
together in the same relative location, and only the
|
||||
superproject's URL need be provided: git-submodule will correctly
|
||||
locate the submodule using the relative URL in .gitmodules.
|
||||
|
||||
status::
|
||||
Show the status of the submodules. This will print the SHA-1 of the
|
||||
@@ -85,6 +102,7 @@ OPTIONS
|
||||
<path>::
|
||||
Path to submodule(s). When specified this will restrict the command
|
||||
to only operate on the submodules found at the specified paths.
|
||||
(This argument is required with add).
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
||||
@@ -274,7 +274,7 @@ same machine, wants to contribute.
|
||||
Bob begins with:
|
||||
|
||||
------------------------------------------------
|
||||
$ git clone /home/alice/project myrepo
|
||||
bob$ git clone /home/alice/project myrepo
|
||||
------------------------------------------------
|
||||
|
||||
This creates a new directory "myrepo" containing a clone of Alice's
|
||||
@@ -285,7 +285,7 @@ Bob then makes some changes and commits them:
|
||||
|
||||
------------------------------------------------
|
||||
(edit files)
|
||||
$ git commit -a
|
||||
bob$ git commit -a
|
||||
(repeat as necessary)
|
||||
------------------------------------------------
|
||||
|
||||
@@ -293,8 +293,8 @@ When he's ready, he tells Alice to pull changes from the repository
|
||||
at /home/bob/myrepo. She does this with:
|
||||
|
||||
------------------------------------------------
|
||||
$ cd /home/alice/project
|
||||
$ git pull /home/bob/myrepo master
|
||||
alice$ cd /home/alice/project
|
||||
alice$ git pull /home/bob/myrepo master
|
||||
------------------------------------------------
|
||||
|
||||
This merges the changes from Bob's "master" branch into Alice's
|
||||
@@ -306,21 +306,47 @@ is the default.)
|
||||
The "pull" command thus performs two operations: it fetches changes
|
||||
from a remote branch, then merges them into the current branch.
|
||||
|
||||
Note that in general, Alice would want her local changes committed before
|
||||
initiating this "pull". If Bob's work conflicts with what Alice did since
|
||||
their histories forked, Alice will use her working tree and the index to
|
||||
resolve conflicts, and existing local changes will interfere with the
|
||||
conflict resolution process (git will still perform the fetch but will
|
||||
refuse to merge --- Alice will have to get rid of her local changes in
|
||||
some way and pull again when this happens).
|
||||
|
||||
Alice can peek at what Bob did without merging first, using the "fetch"
|
||||
command; this allows Alice to inspect what Bob did, using a special
|
||||
symbol "FETCH_HEAD", in order to determine if he has anything worth
|
||||
pulling, like this:
|
||||
|
||||
------------------------------------------------
|
||||
alice$ git fetch /home/bob/myrepo master
|
||||
alice$ git log -p ..FETCH_HEAD
|
||||
------------------------------------------------
|
||||
|
||||
This operation is safe even if Alice has uncommitted local changes.
|
||||
|
||||
After inspecting what Bob did, if there is nothing urgent, Alice may
|
||||
decide to continue working without pulling from Bob. If Bob's history
|
||||
does have something Alice would immediately need, Alice may choose to
|
||||
stash her work-in-progress first, do a "pull", and then finally unstash
|
||||
her work-in-progress on top of the resulting history.
|
||||
|
||||
When you are working in a small closely knit group, it is not
|
||||
unusual to interact with the same repository over and over
|
||||
again. By defining 'remote' repository shorthand, you can make
|
||||
it easier:
|
||||
|
||||
------------------------------------------------
|
||||
$ git remote add bob /home/bob/myrepo
|
||||
alice$ git remote add bob /home/bob/myrepo
|
||||
------------------------------------------------
|
||||
|
||||
With this, Alice can perform the first operation alone using the
|
||||
With this, Alice can perform the first part of the "pull" operation alone using the
|
||||
'git-fetch' command without merging them with her own branch,
|
||||
using:
|
||||
|
||||
-------------------------------------
|
||||
$ git fetch bob
|
||||
alice$ git fetch bob
|
||||
-------------------------------------
|
||||
|
||||
Unlike the longhand form, when Alice fetches from Bob using a
|
||||
@@ -329,7 +355,7 @@ fetched is stored in a remote tracking branch, in this case
|
||||
`bob/master`. So after this:
|
||||
|
||||
-------------------------------------
|
||||
$ git log -p master..bob/master
|
||||
alice$ git log -p master..bob/master
|
||||
-------------------------------------
|
||||
|
||||
shows a list of all the changes that Bob made since he branched from
|
||||
@@ -339,14 +365,14 @@ After examining those changes, Alice
|
||||
could merge the changes into her master branch:
|
||||
|
||||
-------------------------------------
|
||||
$ git merge bob/master
|
||||
alice$ git merge bob/master
|
||||
-------------------------------------
|
||||
|
||||
This `merge` can also be done by 'pulling from her own remote
|
||||
tracking branch', like this:
|
||||
|
||||
-------------------------------------
|
||||
$ git pull . remotes/bob/master
|
||||
alice$ git pull . remotes/bob/master
|
||||
-------------------------------------
|
||||
|
||||
Note that git pull always merges into the current branch,
|
||||
@@ -355,7 +381,7 @@ regardless of what else is given on the command line.
|
||||
Later, Bob can update his repo with Alice's latest changes using
|
||||
|
||||
-------------------------------------
|
||||
$ git pull
|
||||
bob$ git pull
|
||||
-------------------------------------
|
||||
|
||||
Note that he doesn't need to give the path to Alice's repository;
|
||||
@@ -364,7 +390,7 @@ repository in the repository configuration, and that location is
|
||||
used for pulls:
|
||||
|
||||
-------------------------------------
|
||||
$ git config --get remote.origin.url
|
||||
bob$ git config --get remote.origin.url
|
||||
/home/alice/project
|
||||
-------------------------------------
|
||||
|
||||
@@ -376,7 +402,7 @@ Git also keeps a pristine copy of Alice's master branch under the
|
||||
name "origin/master":
|
||||
|
||||
-------------------------------------
|
||||
$ git branch -r
|
||||
bob$ git branch -r
|
||||
origin/master
|
||||
-------------------------------------
|
||||
|
||||
@@ -384,7 +410,7 @@ If Bob later decides to work from a different host, he can still
|
||||
perform clones and pulls using the ssh protocol:
|
||||
|
||||
-------------------------------------
|
||||
$ git clone alice.org:/home/alice/project myrepo
|
||||
bob$ git clone alice.org:/home/alice/project myrepo
|
||||
-------------------------------------
|
||||
|
||||
Alternatively, git has a native protocol, or can use rsync or http;
|
||||
|
||||
@@ -101,6 +101,7 @@ The placeholders are:
|
||||
- '%P': parent hashes
|
||||
- '%p': abbreviated parent hashes
|
||||
- '%an': author name
|
||||
- '%aN': author name (respecting .mailmap)
|
||||
- '%ae': author email
|
||||
- '%ad': author date
|
||||
- '%aD': author date, RFC2822 style
|
||||
@@ -108,6 +109,7 @@ The placeholders are:
|
||||
- '%at': author date, UNIX timestamp
|
||||
- '%ai': author date, ISO 8601 format
|
||||
- '%cn': committer name
|
||||
- '%cN': committer name (respecting .mailmap)
|
||||
- '%ce': committer email
|
||||
- '%cd': committer date
|
||||
- '%cD': committer date, RFC2822 style
|
||||
|
||||
@@ -2431,7 +2431,7 @@ $ git rebase origin
|
||||
-------------------------------------------------
|
||||
|
||||
This will remove each of your commits from mywork, temporarily saving
|
||||
them as patches (in a directory named ".dotest"), update mywork to
|
||||
them as patches (in a directory named ".git/rebase"), update mywork to
|
||||
point at the latest version of origin, then apply each of the saved
|
||||
patches to the new mywork. The result will look like:
|
||||
|
||||
|
||||
9
Makefile
9
Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
|
||||
SCRIPT_SH += git-merge-octopus.sh
|
||||
SCRIPT_SH += git-merge-one-file.sh
|
||||
SCRIPT_SH += git-merge-resolve.sh
|
||||
SCRIPT_SH += git-merge.sh
|
||||
SCRIPT_SH += git-mergetool.sh
|
||||
SCRIPT_SH += git-parse-remote.sh
|
||||
SCRIPT_SH += git-pull.sh
|
||||
@@ -363,6 +362,7 @@ LIB_H += quote.h
|
||||
LIB_H += reflog-walk.h
|
||||
LIB_H += refs.h
|
||||
LIB_H += remote.h
|
||||
LIB_H += rerere.h
|
||||
LIB_H += revision.h
|
||||
LIB_H += run-command.h
|
||||
LIB_H += sha1-lookup.h
|
||||
@@ -447,6 +447,7 @@ LIB_OBJS += read-cache.o
|
||||
LIB_OBJS += reflog-walk.o
|
||||
LIB_OBJS += refs.o
|
||||
LIB_OBJS += remote.o
|
||||
LIB_OBJS += rerere.o
|
||||
LIB_OBJS += revision.o
|
||||
LIB_OBJS += run-command.o
|
||||
LIB_OBJS += server-info.o
|
||||
@@ -513,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
|
||||
BUILTIN_OBJS += builtin-ls-tree.o
|
||||
BUILTIN_OBJS += builtin-mailinfo.o
|
||||
BUILTIN_OBJS += builtin-mailsplit.o
|
||||
BUILTIN_OBJS += builtin-merge.o
|
||||
BUILTIN_OBJS += builtin-merge-base.o
|
||||
BUILTIN_OBJS += builtin-merge-file.o
|
||||
BUILTIN_OBJS += builtin-merge-ours.o
|
||||
@@ -741,10 +743,11 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
|
||||
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
|
||||
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
|
||||
COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o
|
||||
COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o
|
||||
EXTLIBS += -lws2_32
|
||||
X = .exe
|
||||
NOEXECTEMPL = .noexec
|
||||
gitexecdir = $(bindir)
|
||||
template_dir = ../share/git-core/templates/
|
||||
ETC_GITCONFIG = ../etc/gitconfig
|
||||
htmldir=../doc/git/html/
|
||||
@@ -1066,7 +1069,7 @@ ifndef NO_TCLTK
|
||||
$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
|
||||
endif
|
||||
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
|
||||
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) NOEXECTEMPL='$(NOEXECTEMPL)'
|
||||
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
|
||||
|
||||
strip: $(PROGRAMS) git$X
|
||||
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
|
||||
|
||||
54
alias.c
54
alias.c
@@ -21,3 +21,57 @@ char *alias_lookup(const char *alias)
|
||||
git_config(alias_lookup_cb, NULL);
|
||||
return alias_val;
|
||||
}
|
||||
|
||||
int split_cmdline(char *cmdline, const char ***argv)
|
||||
{
|
||||
int src, dst, count = 0, size = 16;
|
||||
char quoted = 0;
|
||||
|
||||
*argv = xmalloc(sizeof(char*) * size);
|
||||
|
||||
/* split alias_string */
|
||||
(*argv)[count++] = cmdline;
|
||||
for (src = dst = 0; cmdline[src];) {
|
||||
char c = cmdline[src];
|
||||
if (!quoted && isspace(c)) {
|
||||
cmdline[dst++] = 0;
|
||||
while (cmdline[++src]
|
||||
&& isspace(cmdline[src]))
|
||||
; /* skip */
|
||||
if (count >= size) {
|
||||
size += 16;
|
||||
*argv = xrealloc(*argv, sizeof(char*) * size);
|
||||
}
|
||||
(*argv)[count++] = cmdline + dst;
|
||||
} else if (!quoted && (c == '\'' || c == '"')) {
|
||||
quoted = c;
|
||||
src++;
|
||||
} else if (c == quoted) {
|
||||
quoted = 0;
|
||||
src++;
|
||||
} else {
|
||||
if (c == '\\' && quoted != '\'') {
|
||||
src++;
|
||||
c = cmdline[src];
|
||||
if (!c) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("cmdline ends with \\");
|
||||
}
|
||||
}
|
||||
cmdline[dst++] = c;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
cmdline[dst] = 0;
|
||||
|
||||
if (quoted) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("unclosed quote");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
2
branch.c
2
branch.c
@@ -166,7 +166,7 @@ void create_branch(const char *head,
|
||||
void remove_branch_state(void)
|
||||
{
|
||||
unlink(git_path("MERGE_HEAD"));
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
unlink(git_path("MERGE_RR"));
|
||||
unlink(git_path("MERGE_MSG"));
|
||||
unlink(git_path("SQUASH_MSG"));
|
||||
}
|
||||
|
||||
@@ -46,7 +46,12 @@ enum color_branch {
|
||||
COLOR_BRANCH_CURRENT = 4,
|
||||
};
|
||||
|
||||
static int mergefilter = -1;
|
||||
static enum merge_filter {
|
||||
NO_FILTER = 0,
|
||||
SHOW_NOT_MERGED,
|
||||
SHOW_MERGED,
|
||||
} merge_filter;
|
||||
static unsigned char merge_filter_ref[20];
|
||||
|
||||
static int parse_branch_color_slot(const char *var, int ofs)
|
||||
{
|
||||
@@ -234,13 +239,15 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
|
||||
if ((kind & ref_list->kinds) == 0)
|
||||
return 0;
|
||||
|
||||
if (mergefilter > -1) {
|
||||
if (merge_filter != NO_FILTER) {
|
||||
branch.item = lookup_commit_reference_gently(sha1, 1);
|
||||
if (!branch.item)
|
||||
die("Unable to lookup tip of branch %s", refname);
|
||||
if (mergefilter == 0 && has_commit(head_sha1, &branch))
|
||||
if (merge_filter == SHOW_NOT_MERGED &&
|
||||
has_commit(merge_filter_ref, &branch))
|
||||
return 0;
|
||||
if (mergefilter == 1 && !has_commit(head_sha1, &branch))
|
||||
if (merge_filter == SHOW_MERGED &&
|
||||
!has_commit(merge_filter_ref, &branch))
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -443,6 +450,20 @@ static int opt_parse_with_commit(const struct option *opt, const char *arg, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
merge_filter = ((opt->long_name[0] == 'n')
|
||||
? SHOW_NOT_MERGED
|
||||
: SHOW_MERGED);
|
||||
if (unset)
|
||||
merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */
|
||||
if (!arg)
|
||||
arg = "HEAD";
|
||||
if (get_sha1(arg, merge_filter_ref))
|
||||
die("malformed object name %s", arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int delete = 0, rename = 0, force_create = 0;
|
||||
@@ -460,13 +481,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
|
||||
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
|
||||
REF_REMOTE_BRANCH),
|
||||
OPT_CALLBACK(0, "contains", &with_commit, "commit",
|
||||
"print only branches that contain the commit",
|
||||
opt_parse_with_commit),
|
||||
{
|
||||
OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
|
||||
"print only branches that contain the commit",
|
||||
PARSE_OPT_LASTARG_DEFAULT,
|
||||
opt_parse_with_commit, (intptr_t)"HEAD",
|
||||
},
|
||||
{
|
||||
OPTION_CALLBACK, 0, "with", &with_commit, "commit",
|
||||
"print only branches that contain the commit",
|
||||
PARSE_OPT_HIDDEN, opt_parse_with_commit,
|
||||
PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
|
||||
opt_parse_with_commit, (intptr_t) "HEAD",
|
||||
},
|
||||
OPT__ABBREV(&abbrev),
|
||||
|
||||
@@ -479,7 +504,18 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
|
||||
OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
|
||||
OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
|
||||
OPT_SET_INT(0, "merged", &mergefilter, "list only merged branches", 1),
|
||||
{
|
||||
OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
|
||||
"commit", "print only not merged branches",
|
||||
PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
|
||||
opt_parse_merge_filter, (intptr_t) "HEAD",
|
||||
},
|
||||
{
|
||||
OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
|
||||
"commit", "print only merged branches",
|
||||
PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
|
||||
opt_parse_merge_filter, (intptr_t) "HEAD",
|
||||
},
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
@@ -489,9 +525,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
branch_use_color = git_use_color_default;
|
||||
|
||||
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);
|
||||
|
||||
head = resolve_ref("HEAD", head_sha1, 0, NULL);
|
||||
if (!head)
|
||||
@@ -504,6 +537,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
die("HEAD not found below refs/heads!");
|
||||
head += 11;
|
||||
}
|
||||
hashcpy(merge_filter_ref, head_sha1);
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
|
||||
if (!!delete + !!rename + !!force_create > 1)
|
||||
usage_with_options(builtin_branch_usage, options);
|
||||
|
||||
if (delete)
|
||||
return delete_branches(argc, argv, delete > 1, kinds);
|
||||
|
||||
@@ -115,7 +115,7 @@ static char *guess_dir_name(const char *repo, int is_bundle)
|
||||
if (!after_slash_or_colon)
|
||||
end = p;
|
||||
p += 7;
|
||||
} else if (*p == '/' || *p == ':') {
|
||||
} else if (is_dir_sep(*p) || *p == ':') {
|
||||
if (end == limit)
|
||||
end = p;
|
||||
after_slash_or_colon = 1;
|
||||
|
||||
@@ -45,41 +45,19 @@ static const char commit_utf8_warn[] =
|
||||
"You may want to amend it after fixing the message, or set the config\n"
|
||||
"variable i18n.commitencoding to the encoding your project uses.\n";
|
||||
|
||||
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
int commit_tree(const char *msg, unsigned char *tree,
|
||||
struct commit_list *parents, unsigned char *ret)
|
||||
{
|
||||
int i;
|
||||
struct commit_list *parents = NULL;
|
||||
unsigned char tree_sha1[20];
|
||||
unsigned char commit_sha1[20];
|
||||
struct strbuf buffer;
|
||||
int encoding_is_utf8;
|
||||
struct strbuf buffer;
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
if (argc < 2)
|
||||
usage(commit_tree_usage);
|
||||
if (get_sha1(argv[1], tree_sha1))
|
||||
die("Not a valid object name %s", argv[1]);
|
||||
|
||||
check_valid(tree_sha1, OBJ_TREE);
|
||||
for (i = 2; i < argc; i += 2) {
|
||||
unsigned char sha1[20];
|
||||
const char *a, *b;
|
||||
a = argv[i]; b = argv[i+1];
|
||||
if (!b || strcmp(a, "-p"))
|
||||
usage(commit_tree_usage);
|
||||
|
||||
if (get_sha1(b, sha1))
|
||||
die("Not a valid object name %s", b);
|
||||
check_valid(sha1, OBJ_COMMIT);
|
||||
new_parent(lookup_commit(sha1), &parents);
|
||||
}
|
||||
check_valid(tree, OBJ_TREE);
|
||||
|
||||
/* Not having i18n.commitencoding is the same as having utf-8 */
|
||||
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
|
||||
|
||||
strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
|
||||
strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1));
|
||||
strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
|
||||
|
||||
/*
|
||||
* NOTE! This ordering means that the same exact tree merged with a
|
||||
@@ -102,14 +80,47 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
strbuf_addch(&buffer, '\n');
|
||||
|
||||
/* And add the comment */
|
||||
if (strbuf_read(&buffer, 0, 0) < 0)
|
||||
die("git-commit-tree: read returned %s", strerror(errno));
|
||||
strbuf_addstr(&buffer, msg);
|
||||
|
||||
/* And check the encoding */
|
||||
if (encoding_is_utf8 && !is_utf8(buffer.buf))
|
||||
fprintf(stderr, commit_utf8_warn);
|
||||
|
||||
if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) {
|
||||
return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
|
||||
}
|
||||
|
||||
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
struct commit_list *parents = NULL;
|
||||
unsigned char tree_sha1[20];
|
||||
unsigned char commit_sha1[20];
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
if (argc < 2)
|
||||
usage(commit_tree_usage);
|
||||
if (get_sha1(argv[1], tree_sha1))
|
||||
die("Not a valid object name %s", argv[1]);
|
||||
|
||||
for (i = 2; i < argc; i += 2) {
|
||||
unsigned char sha1[20];
|
||||
const char *a, *b;
|
||||
a = argv[i]; b = argv[i+1];
|
||||
if (!b || strcmp(a, "-p"))
|
||||
usage(commit_tree_usage);
|
||||
|
||||
if (get_sha1(b, sha1))
|
||||
die("Not a valid object name %s", b);
|
||||
check_valid(sha1, OBJ_COMMIT);
|
||||
new_parent(lookup_commit(sha1), &parents);
|
||||
}
|
||||
|
||||
if (strbuf_read(&buffer, 0, 0) < 0)
|
||||
die("git-commit-tree: read returned %s", strerror(errno));
|
||||
|
||||
if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1)) {
|
||||
printf("%s\n", sha1_to_hex(commit_sha1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "utf8.h"
|
||||
#include "parse-options.h"
|
||||
#include "path-list.h"
|
||||
#include "rerere.h"
|
||||
#include "unpack-trees.h"
|
||||
|
||||
static const char * const builtin_commit_usage[] = {
|
||||
|
||||
@@ -159,23 +159,24 @@ static int handle_line(char *line)
|
||||
}
|
||||
|
||||
static void print_joined(const char *singular, const char *plural,
|
||||
struct list *list)
|
||||
struct list *list, struct strbuf *out)
|
||||
{
|
||||
if (list->nr == 0)
|
||||
return;
|
||||
if (list->nr == 1) {
|
||||
printf("%s%s", singular, list->list[0]);
|
||||
strbuf_addf(out, "%s%s", singular, list->list[0]);
|
||||
} else {
|
||||
int i;
|
||||
printf("%s", plural);
|
||||
strbuf_addstr(out, plural);
|
||||
for (i = 0; i < list->nr - 1; i++)
|
||||
printf("%s%s", i > 0 ? ", " : "", list->list[i]);
|
||||
printf(" and %s", list->list[list->nr - 1]);
|
||||
strbuf_addf(out, "%s%s", i > 0 ? ", " : "", list->list[i]);
|
||||
strbuf_addf(out, " and %s", list->list[list->nr - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void shortlog(const char *name, unsigned char *sha1,
|
||||
struct commit *head, struct rev_info *rev, int limit)
|
||||
struct commit *head, struct rev_info *rev, int limit,
|
||||
struct strbuf *out)
|
||||
{
|
||||
int i, count = 0;
|
||||
struct commit *commit;
|
||||
@@ -232,15 +233,15 @@ static void shortlog(const char *name, unsigned char *sha1,
|
||||
}
|
||||
|
||||
if (count > limit)
|
||||
printf("\n* %s: (%d commits)\n", name, count);
|
||||
strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
|
||||
else
|
||||
printf("\n* %s:\n", name);
|
||||
strbuf_addf(out, "\n* %s:\n", name);
|
||||
|
||||
for (i = 0; i < subjects.nr; i++)
|
||||
if (i >= limit)
|
||||
printf(" ...\n");
|
||||
strbuf_addf(out, " ...\n");
|
||||
else
|
||||
printf(" %s\n", subjects.list[i]);
|
||||
strbuf_addf(out, " %s\n", subjects.list[i]);
|
||||
|
||||
clear_commit_marks((struct commit *)branch, flags);
|
||||
clear_commit_marks(head, flags);
|
||||
@@ -251,15 +252,105 @@ static void shortlog(const char *name, unsigned char *sha1,
|
||||
free_list(&subjects);
|
||||
}
|
||||
|
||||
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int limit = 20, i = 0;
|
||||
int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
|
||||
int limit = 20, i = 0, pos = 0;
|
||||
char line[1024];
|
||||
FILE *in = stdin;
|
||||
const char *sep = "";
|
||||
char *p = line, *sep = "";
|
||||
unsigned char head_sha1[20];
|
||||
const char *current_branch;
|
||||
|
||||
/* get current branch */
|
||||
current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
|
||||
if (!current_branch)
|
||||
die("No current branch");
|
||||
if (!prefixcmp(current_branch, "refs/heads/"))
|
||||
current_branch += 11;
|
||||
|
||||
/* get a line */
|
||||
while (pos < in->len) {
|
||||
int len;
|
||||
char *newline;
|
||||
|
||||
p = in->buf + pos;
|
||||
newline = strchr(p, '\n');
|
||||
len = newline ? newline - p : strlen(p);
|
||||
pos += len + !!newline;
|
||||
i++;
|
||||
p[len] = 0;
|
||||
if (handle_line(p))
|
||||
die ("Error in line %d: %.*s", i, len, p);
|
||||
}
|
||||
|
||||
strbuf_addstr(out, "Merge ");
|
||||
for (i = 0; i < srcs.nr; i++) {
|
||||
struct src_data *src_data = srcs.payload[i];
|
||||
const char *subsep = "";
|
||||
|
||||
strbuf_addstr(out, sep);
|
||||
sep = "; ";
|
||||
|
||||
if (src_data->head_status == 1) {
|
||||
strbuf_addstr(out, srcs.list[i]);
|
||||
continue;
|
||||
}
|
||||
if (src_data->head_status == 3) {
|
||||
subsep = ", ";
|
||||
strbuf_addstr(out, "HEAD");
|
||||
}
|
||||
if (src_data->branch.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
subsep = ", ";
|
||||
print_joined("branch ", "branches ", &src_data->branch,
|
||||
out);
|
||||
}
|
||||
if (src_data->r_branch.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
subsep = ", ";
|
||||
print_joined("remote branch ", "remote branches ",
|
||||
&src_data->r_branch, out);
|
||||
}
|
||||
if (src_data->tag.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
subsep = ", ";
|
||||
print_joined("tag ", "tags ", &src_data->tag, out);
|
||||
}
|
||||
if (src_data->generic.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
print_joined("commit ", "commits ", &src_data->generic,
|
||||
out);
|
||||
}
|
||||
if (strcmp(".", srcs.list[i]))
|
||||
strbuf_addf(out, " of %s", srcs.list[i]);
|
||||
}
|
||||
|
||||
if (!strcmp("master", current_branch))
|
||||
strbuf_addch(out, '\n');
|
||||
else
|
||||
strbuf_addf(out, " into %s\n", current_branch);
|
||||
|
||||
if (merge_summary) {
|
||||
struct commit *head;
|
||||
struct rev_info rev;
|
||||
|
||||
head = lookup_commit(head_sha1);
|
||||
init_revisions(&rev, NULL);
|
||||
rev.commit_format = CMIT_FMT_ONELINE;
|
||||
rev.ignore_merges = 1;
|
||||
rev.limited = 1;
|
||||
|
||||
for (i = 0; i < origins.nr; i++)
|
||||
shortlog(origins.list[i], origins.payload[i],
|
||||
head, &rev, limit, out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
FILE *in = stdin;
|
||||
struct strbuf input, output;
|
||||
int ret;
|
||||
|
||||
git_config(fmt_merge_msg_config, NULL);
|
||||
|
||||
while (argc > 1) {
|
||||
@@ -288,82 +379,14 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
|
||||
if (argc > 1)
|
||||
usage(fmt_merge_msg_usage);
|
||||
|
||||
/* get current branch */
|
||||
current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
|
||||
if (!current_branch)
|
||||
die("No current branch");
|
||||
if (!prefixcmp(current_branch, "refs/heads/"))
|
||||
current_branch += 11;
|
||||
|
||||
while (fgets(line, sizeof(line), in)) {
|
||||
i++;
|
||||
if (line[0] == 0)
|
||||
continue;
|
||||
if (handle_line(line))
|
||||
die ("Error in line %d: %s", i, line);
|
||||
}
|
||||
|
||||
printf("Merge ");
|
||||
for (i = 0; i < srcs.nr; i++) {
|
||||
struct src_data *src_data = srcs.payload[i];
|
||||
const char *subsep = "";
|
||||
|
||||
printf(sep);
|
||||
sep = "; ";
|
||||
|
||||
if (src_data->head_status == 1) {
|
||||
printf(srcs.list[i]);
|
||||
continue;
|
||||
}
|
||||
if (src_data->head_status == 3) {
|
||||
subsep = ", ";
|
||||
printf("HEAD");
|
||||
}
|
||||
if (src_data->branch.nr) {
|
||||
printf(subsep);
|
||||
subsep = ", ";
|
||||
print_joined("branch ", "branches ", &src_data->branch);
|
||||
}
|
||||
if (src_data->r_branch.nr) {
|
||||
printf(subsep);
|
||||
subsep = ", ";
|
||||
print_joined("remote branch ", "remote branches ",
|
||||
&src_data->r_branch);
|
||||
}
|
||||
if (src_data->tag.nr) {
|
||||
printf(subsep);
|
||||
subsep = ", ";
|
||||
print_joined("tag ", "tags ", &src_data->tag);
|
||||
}
|
||||
if (src_data->generic.nr) {
|
||||
printf(subsep);
|
||||
print_joined("commit ", "commits ", &src_data->generic);
|
||||
}
|
||||
if (strcmp(".", srcs.list[i]))
|
||||
printf(" of %s", srcs.list[i]);
|
||||
}
|
||||
|
||||
if (!strcmp("master", current_branch))
|
||||
putchar('\n');
|
||||
else
|
||||
printf(" into %s\n", current_branch);
|
||||
|
||||
if (merge_summary) {
|
||||
struct commit *head;
|
||||
struct rev_info rev;
|
||||
|
||||
head = lookup_commit(head_sha1);
|
||||
init_revisions(&rev, prefix);
|
||||
rev.commit_format = CMIT_FMT_ONELINE;
|
||||
rev.ignore_merges = 1;
|
||||
rev.limited = 1;
|
||||
|
||||
for (i = 0; i < origins.nr; i++)
|
||||
shortlog(origins.list[i], origins.payload[i],
|
||||
head, &rev, limit);
|
||||
}
|
||||
|
||||
/* No cleanup yet; is standalone anyway */
|
||||
strbuf_init(&input, 0);
|
||||
if (strbuf_read(&input, fileno(in), 0) < 0)
|
||||
die("could not read input file %s", strerror(errno));
|
||||
strbuf_init(&output, 0);
|
||||
|
||||
ret = fmt_merge_msg(merge_summary, &input, &output);
|
||||
if (ret)
|
||||
return ret;
|
||||
printf("%s", output.buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -334,7 +334,9 @@ static int check_header(char *line, unsigned linesize, char **hdr_data, int over
|
||||
return 1;
|
||||
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
|
||||
for (i = 0; header[i]; i++) {
|
||||
if (!memcmp("Subject: ", header[i], 9)) {
|
||||
if (!memcmp("Subject", header[i], 7)) {
|
||||
if (!hdr_data[i])
|
||||
hdr_data[i] = xmalloc(linesize + 20);
|
||||
if (! handle_header(line, hdr_data[i], 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -42,14 +42,6 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)
|
||||
* - *(int *)commit->object.sha1 set to the virtual id.
|
||||
*/
|
||||
|
||||
static unsigned commit_list_count(const struct commit_list *l)
|
||||
{
|
||||
unsigned c = 0;
|
||||
for (; l; l = l->next )
|
||||
c++;
|
||||
return c;
|
||||
}
|
||||
|
||||
static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
|
||||
{
|
||||
struct commit *commit = xcalloc(1, sizeof(struct commit));
|
||||
|
||||
1156
builtin-merge.c
Normal file
1156
builtin-merge.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,30 +29,6 @@ static int list_tree(unsigned char *sha1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cache_unmerged(void)
|
||||
{
|
||||
int i;
|
||||
struct cache_entry **dst;
|
||||
struct cache_entry *last = NULL;
|
||||
|
||||
read_cache();
|
||||
dst = active_cache;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce)) {
|
||||
remove_name_hash(ce);
|
||||
if (last && !strcmp(ce->name, last->name))
|
||||
continue;
|
||||
cache_tree_invalidate_path(active_cache_tree, ce->name);
|
||||
last = ce;
|
||||
continue;
|
||||
}
|
||||
*dst++ = ce;
|
||||
}
|
||||
active_nr = dst - active_cache;
|
||||
return !!last;
|
||||
}
|
||||
|
||||
static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
|
||||
{
|
||||
struct tree_desc desc;
|
||||
|
||||
@@ -29,12 +29,6 @@ static inline int postfixcmp(const char *string, const char *postfix)
|
||||
return strcmp(string + len1 - len2, postfix);
|
||||
}
|
||||
|
||||
static inline const char *skip_prefix(const char *name, const char *prefix)
|
||||
{
|
||||
return !name ? "" :
|
||||
prefixcmp(name, prefix) ? name : name + strlen(prefix);
|
||||
}
|
||||
|
||||
static int opt_parse_track(const struct option *opt, const char *arg, int not)
|
||||
{
|
||||
struct path_list *list = opt->value;
|
||||
@@ -182,12 +176,18 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
info->remote = xstrdup(value);
|
||||
} else {
|
||||
char *space = strchr(value, ' ');
|
||||
value = skip_prefix(value, "refs/heads/");
|
||||
const char *ptr = skip_prefix(value, "refs/heads/");
|
||||
if (ptr)
|
||||
value = ptr;
|
||||
while (space) {
|
||||
char *merge;
|
||||
merge = xstrndup(value, space - value);
|
||||
path_list_append(merge, &info->merge);
|
||||
value = skip_prefix(space + 1, "refs/heads/");
|
||||
ptr = skip_prefix(space + 1, "refs/heads/");
|
||||
if (ptr)
|
||||
value = ptr;
|
||||
else
|
||||
value = space + 1;
|
||||
space = strchr(value, ' ');
|
||||
}
|
||||
path_list_append(xstrdup(value), &info->merge);
|
||||
@@ -219,7 +219,12 @@ static int handle_one_branch(const char *refname,
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec)) {
|
||||
struct path_list_item *item;
|
||||
const char *name = skip_prefix(refspec.src, "refs/heads/");
|
||||
const char *name, *ptr;
|
||||
ptr = skip_prefix(refspec.src, "refs/heads/");
|
||||
if (ptr)
|
||||
name = ptr;
|
||||
else
|
||||
name = refspec.src;
|
||||
/* symbolic refs pointing nowhere were handled already */
|
||||
if ((flags & REF_ISSYMREF) ||
|
||||
unsorted_path_list_has_path(&states->tracked,
|
||||
@@ -248,6 +253,7 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)
|
||||
struct path_list *target = &states->tracked;
|
||||
unsigned char sha1[20];
|
||||
void *util = NULL;
|
||||
const char *ptr;
|
||||
|
||||
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
|
||||
target = &states->new;
|
||||
@@ -256,8 +262,10 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)
|
||||
if (hashcmp(sha1, ref->new_sha1))
|
||||
util = &states;
|
||||
}
|
||||
path_list_append(skip_prefix(ref->name, "refs/heads/"),
|
||||
target)->util = util;
|
||||
ptr = skip_prefix(ref->name, "refs/heads/");
|
||||
if (!ptr)
|
||||
ptr = ref->name;
|
||||
path_list_append(ptr, target)->util = util;
|
||||
}
|
||||
free_refs(fetch_map);
|
||||
|
||||
@@ -522,10 +530,15 @@ static int show(int argc, const char **argv)
|
||||
"es" : "");
|
||||
for (i = 0; i < states.remote->push_refspec_nr; i++) {
|
||||
struct refspec *spec = states.remote->push + i;
|
||||
const char *p = "", *q = "";
|
||||
if (spec->src)
|
||||
p = skip_prefix(spec->src, "refs/heads/");
|
||||
if (spec->dst)
|
||||
q = skip_prefix(spec->dst, "refs/heads/");
|
||||
printf(" %s%s%s%s", spec->force ? "+" : "",
|
||||
skip_prefix(spec->src, "refs/heads/"),
|
||||
p ? p : spec->src,
|
||||
spec->dst ? ":" : "",
|
||||
skip_prefix(spec->dst, "refs/heads/"));
|
||||
q ? q : spec->dst);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
362
builtin-rerere.c
362
builtin-rerere.c
@@ -1,11 +1,10 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "path-list.h"
|
||||
#include "rerere.h"
|
||||
#include "xdiff/xdiff.h"
|
||||
#include "xdiff-interface.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
static const char git_rerere_usage[] =
|
||||
"git-rerere [clear | status | diff | gc]";
|
||||
|
||||
@@ -13,14 +12,6 @@ static const char git_rerere_usage[] =
|
||||
static int cutoff_noresolve = 15;
|
||||
static int cutoff_resolve = 60;
|
||||
|
||||
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
|
||||
static int rerere_enabled = -1;
|
||||
|
||||
/* automatically update cleanly resolved paths to the index */
|
||||
static int rerere_autoupdate;
|
||||
|
||||
static char *merge_rr_path;
|
||||
|
||||
static const char *rr_path(const char *name, const char *file)
|
||||
{
|
||||
return git_path("rr-cache/%s/%s", name, file);
|
||||
@@ -38,179 +29,6 @@ static int has_resolution(const char *name)
|
||||
return !stat(rr_path(name, "postimage"), &st);
|
||||
}
|
||||
|
||||
static void read_rr(struct path_list *rr)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
char buf[PATH_MAX];
|
||||
FILE *in = fopen(merge_rr_path, "r");
|
||||
if (!in)
|
||||
return;
|
||||
while (fread(buf, 40, 1, in) == 1) {
|
||||
int i;
|
||||
char *name;
|
||||
if (get_sha1_hex(buf, sha1))
|
||||
die("corrupt MERGE_RR");
|
||||
buf[40] = '\0';
|
||||
name = xstrdup(buf);
|
||||
if (fgetc(in) != '\t')
|
||||
die("corrupt MERGE_RR");
|
||||
for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
|
||||
; /* do nothing */
|
||||
if (i == sizeof(buf))
|
||||
die("filename too long");
|
||||
path_list_insert(buf, rr)->util = name;
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
static struct lock_file write_lock;
|
||||
|
||||
static int write_rr(struct path_list *rr, int out_fd)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < rr->nr; i++) {
|
||||
const char *path;
|
||||
int length;
|
||||
if (!rr->items[i].util)
|
||||
continue;
|
||||
path = rr->items[i].path;
|
||||
length = strlen(path) + 1;
|
||||
if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
|
||||
write_in_full(out_fd, "\t", 1) != 1 ||
|
||||
write_in_full(out_fd, path, length) != length)
|
||||
die("unable to write rerere record");
|
||||
}
|
||||
if (commit_lock_file(&write_lock) != 0)
|
||||
die("unable to write rerere record");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_file(const char *path,
|
||||
unsigned char *sha1, const char *output)
|
||||
{
|
||||
SHA_CTX ctx;
|
||||
char buf[1024];
|
||||
int hunk = 0, hunk_no = 0;
|
||||
struct strbuf one, two;
|
||||
FILE *f = fopen(path, "r");
|
||||
FILE *out = NULL;
|
||||
|
||||
if (!f)
|
||||
return error("Could not open %s", path);
|
||||
|
||||
if (output) {
|
||||
out = fopen(output, "w");
|
||||
if (!out) {
|
||||
fclose(f);
|
||||
return error("Could not write %s", output);
|
||||
}
|
||||
}
|
||||
|
||||
if (sha1)
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
strbuf_init(&one, 0);
|
||||
strbuf_init(&two, 0);
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
if (!prefixcmp(buf, "<<<<<<< "))
|
||||
hunk = 1;
|
||||
else if (!prefixcmp(buf, "======="))
|
||||
hunk = 2;
|
||||
else if (!prefixcmp(buf, ">>>>>>> ")) {
|
||||
if (strbuf_cmp(&one, &two) > 0)
|
||||
strbuf_swap(&one, &two);
|
||||
hunk_no++;
|
||||
hunk = 0;
|
||||
if (out) {
|
||||
fputs("<<<<<<<\n", out);
|
||||
fwrite(one.buf, one.len, 1, out);
|
||||
fputs("=======\n", out);
|
||||
fwrite(two.buf, two.len, 1, out);
|
||||
fputs(">>>>>>>\n", out);
|
||||
}
|
||||
if (sha1) {
|
||||
SHA1_Update(&ctx, one.buf ? one.buf : "",
|
||||
one.len + 1);
|
||||
SHA1_Update(&ctx, two.buf ? two.buf : "",
|
||||
two.len + 1);
|
||||
}
|
||||
strbuf_reset(&one);
|
||||
strbuf_reset(&two);
|
||||
} else if (hunk == 1)
|
||||
strbuf_addstr(&one, buf);
|
||||
else if (hunk == 2)
|
||||
strbuf_addstr(&two, buf);
|
||||
else if (out)
|
||||
fputs(buf, out);
|
||||
}
|
||||
strbuf_release(&one);
|
||||
strbuf_release(&two);
|
||||
|
||||
fclose(f);
|
||||
if (out)
|
||||
fclose(out);
|
||||
if (sha1)
|
||||
SHA1_Final(sha1, &ctx);
|
||||
if (hunk) {
|
||||
if (output)
|
||||
unlink(output);
|
||||
return error("Could not parse conflict hunks in %s", path);
|
||||
}
|
||||
return hunk_no;
|
||||
}
|
||||
|
||||
static int find_conflict(struct path_list *conflict)
|
||||
{
|
||||
int i;
|
||||
if (read_cache() < 0)
|
||||
return error("Could not read index");
|
||||
for (i = 0; i+1 < active_nr; i++) {
|
||||
struct cache_entry *e2 = active_cache[i];
|
||||
struct cache_entry *e3 = active_cache[i+1];
|
||||
if (ce_stage(e2) == 2 &&
|
||||
ce_stage(e3) == 3 &&
|
||||
ce_same_name(e2, e3) &&
|
||||
S_ISREG(e2->ce_mode) &&
|
||||
S_ISREG(e3->ce_mode)) {
|
||||
path_list_insert((const char *)e2->name, conflict);
|
||||
i++; /* skip over both #2 and #3 */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge(const char *name, const char *path)
|
||||
{
|
||||
int ret;
|
||||
mmfile_t cur, base, other;
|
||||
mmbuffer_t result = {NULL, 0};
|
||||
xpparam_t xpp = {XDF_NEED_MINIMAL};
|
||||
|
||||
if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
|
||||
return 1;
|
||||
|
||||
if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
|
||||
read_mmfile(&base, rr_path(name, "preimage")) ||
|
||||
read_mmfile(&other, rr_path(name, "postimage")))
|
||||
return 1;
|
||||
ret = xdl_merge(&base, &cur, "", &other, "",
|
||||
&xpp, XDL_MERGE_ZEALOUS, &result);
|
||||
if (!ret) {
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f)
|
||||
return error("Could not write to %s", path);
|
||||
fwrite(result.ptr, result.size, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
free(cur.ptr);
|
||||
free(base.ptr);
|
||||
free(other.ptr);
|
||||
free(result.ptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void unlink_rr_item(const char *name)
|
||||
{
|
||||
unlink(rr_path(name, "thisimage"));
|
||||
@@ -219,6 +37,17 @@ static void unlink_rr_item(const char *name)
|
||||
rmdir(git_path("rr-cache/%s", name));
|
||||
}
|
||||
|
||||
static int git_rerere_gc_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "gc.rerereresolved"))
|
||||
cutoff_resolve = git_config_int(var, value);
|
||||
else if (!strcmp(var, "gc.rerereunresolved"))
|
||||
cutoff_noresolve = git_config_int(var, value);
|
||||
else
|
||||
return git_default_config(var, value, cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void garbage_collect(struct path_list *rr)
|
||||
{
|
||||
struct path_list to_remove = { NULL, 0, 0, 1 };
|
||||
@@ -227,6 +56,7 @@ static void garbage_collect(struct path_list *rr)
|
||||
int i, cutoff;
|
||||
time_t now = time(NULL), then;
|
||||
|
||||
git_config(git_rerere_gc_config, NULL);
|
||||
dir = opendir(git_path("rr-cache"));
|
||||
while ((e = readdir(dir))) {
|
||||
const char *name = e->d_name;
|
||||
@@ -279,181 +109,25 @@ static int diff_two(const char *file1, const char *label1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lock_file index_lock;
|
||||
|
||||
static int update_paths(struct path_list *update)
|
||||
{
|
||||
int i;
|
||||
int fd = hold_locked_index(&index_lock, 0);
|
||||
int status = 0;
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < update->nr; i++) {
|
||||
struct path_list_item *item = &update->items[i];
|
||||
if (add_file_to_cache(item->path, ADD_CACHE_IGNORE_ERRORS))
|
||||
status = -1;
|
||||
}
|
||||
|
||||
if (!status && active_cache_changed) {
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(&index_lock))
|
||||
die("Unable to write new index file");
|
||||
} else if (fd >= 0)
|
||||
rollback_lock_file(&index_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int do_plain_rerere(struct path_list *rr, int fd)
|
||||
{
|
||||
struct path_list conflict = { NULL, 0, 0, 1 };
|
||||
struct path_list update = { NULL, 0, 0, 1 };
|
||||
int i;
|
||||
|
||||
find_conflict(&conflict);
|
||||
|
||||
/*
|
||||
* MERGE_RR records paths with conflicts immediately after merge
|
||||
* failed. Some of the conflicted paths might have been hand resolved
|
||||
* in the working tree since then, but the initial run would catch all
|
||||
* and register their preimages.
|
||||
*/
|
||||
|
||||
for (i = 0; i < conflict.nr; i++) {
|
||||
const char *path = conflict.items[i].path;
|
||||
if (!path_list_has_path(rr, path)) {
|
||||
unsigned char sha1[20];
|
||||
char *hex;
|
||||
int ret;
|
||||
ret = handle_file(path, sha1, NULL);
|
||||
if (ret < 1)
|
||||
continue;
|
||||
hex = xstrdup(sha1_to_hex(sha1));
|
||||
path_list_insert(path, rr)->util = hex;
|
||||
if (mkdir(git_path("rr-cache/%s", hex), 0755))
|
||||
continue;;
|
||||
handle_file(path, NULL, rr_path(hex, "preimage"));
|
||||
fprintf(stderr, "Recorded preimage for '%s'\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now some of the paths that had conflicts earlier might have been
|
||||
* hand resolved. Others may be similar to a conflict already that
|
||||
* was resolved before.
|
||||
*/
|
||||
|
||||
for (i = 0; i < rr->nr; i++) {
|
||||
int ret;
|
||||
const char *path = rr->items[i].path;
|
||||
const char *name = (const char *)rr->items[i].util;
|
||||
|
||||
if (has_resolution(name)) {
|
||||
if (!merge(name, path)) {
|
||||
fprintf(stderr, "Resolved '%s' using "
|
||||
"previous resolution.\n", path);
|
||||
if (rerere_autoupdate)
|
||||
path_list_insert(path, &update);
|
||||
goto mark_resolved;
|
||||
}
|
||||
}
|
||||
|
||||
/* Let's see if we have resolved it. */
|
||||
ret = handle_file(path, NULL, NULL);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "Recorded resolution for '%s'.\n", path);
|
||||
copy_file(rr_path(name, "postimage"), path, 0666);
|
||||
mark_resolved:
|
||||
rr->items[i].util = NULL;
|
||||
}
|
||||
|
||||
if (update.nr)
|
||||
update_paths(&update);
|
||||
|
||||
return write_rr(rr, fd);
|
||||
}
|
||||
|
||||
static int git_rerere_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "gc.rerereresolved"))
|
||||
cutoff_resolve = git_config_int(var, value);
|
||||
else if (!strcmp(var, "gc.rerereunresolved"))
|
||||
cutoff_noresolve = git_config_int(var, value);
|
||||
else if (!strcmp(var, "rerere.enabled"))
|
||||
rerere_enabled = git_config_bool(var, value);
|
||||
else if (!strcmp(var, "rerere.autoupdate"))
|
||||
rerere_autoupdate = git_config_bool(var, value);
|
||||
else
|
||||
return git_default_config(var, value, cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_rerere_enabled(void)
|
||||
{
|
||||
struct stat st;
|
||||
const char *rr_cache;
|
||||
int rr_cache_exists;
|
||||
|
||||
if (!rerere_enabled)
|
||||
return 0;
|
||||
|
||||
rr_cache = git_path("rr-cache");
|
||||
rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
|
||||
if (rerere_enabled < 0)
|
||||
return rr_cache_exists;
|
||||
|
||||
if (!rr_cache_exists &&
|
||||
(mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
|
||||
die("Could not create directory %s", rr_cache);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setup_rerere(struct path_list *merge_rr)
|
||||
{
|
||||
int fd;
|
||||
|
||||
git_config(git_rerere_config, NULL);
|
||||
if (!is_rerere_enabled())
|
||||
return -1;
|
||||
|
||||
merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
|
||||
fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
|
||||
read_rr(merge_rr);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int rerere(void)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int fd;
|
||||
|
||||
fd = setup_rerere(&merge_rr);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
return do_plain_rerere(&merge_rr, fd);
|
||||
}
|
||||
|
||||
int cmd_rerere(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int i, fd;
|
||||
|
||||
if (argc < 2)
|
||||
return rerere();
|
||||
|
||||
fd = setup_rerere(&merge_rr);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
if (argc < 2)
|
||||
return do_plain_rerere(&merge_rr, fd);
|
||||
else if (!strcmp(argv[1], "clear")) {
|
||||
if (!strcmp(argv[1], "clear")) {
|
||||
for (i = 0; i < merge_rr.nr; i++) {
|
||||
const char *name = (const char *)merge_rr.items[i].util;
|
||||
if (!has_resolution(name))
|
||||
unlink_rr_item(name);
|
||||
}
|
||||
unlink(merge_rr_path);
|
||||
unlink(git_path("rr-cache/MERGE_RR"));
|
||||
} else if (!strcmp(argv[1], "gc"))
|
||||
garbage_collect(&merge_rr);
|
||||
else if (!strcmp(argv[1], "status"))
|
||||
|
||||
@@ -154,6 +154,15 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
|
||||
if (!author)
|
||||
die("Missing author: %s",
|
||||
sha1_to_hex(commit->object.sha1));
|
||||
if (log->user_format) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &buf,
|
||||
DEFAULT_ABBREV, "", "", DATE_NORMAL, 0);
|
||||
insert_one_record(log, author, buf.buf);
|
||||
strbuf_release(&buf);
|
||||
return;
|
||||
}
|
||||
if (*buffer)
|
||||
buffer++;
|
||||
insert_one_record(log, author, !*buffer ? "<none>" : buffer);
|
||||
@@ -271,6 +280,8 @@ parse_done:
|
||||
usage_with_options(shortlog_usage, options);
|
||||
}
|
||||
|
||||
log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
|
||||
|
||||
/* assume HEAD if from a tty */
|
||||
if (!nongit && !rev.pending.nr && isatty(0))
|
||||
add_head_to_pending(&rev);
|
||||
|
||||
@@ -202,6 +202,7 @@ static int do_sign(struct strbuf *buffer)
|
||||
const char *args[4];
|
||||
char *bracket;
|
||||
int len;
|
||||
int i, j;
|
||||
|
||||
if (!*signingkey) {
|
||||
if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
|
||||
@@ -241,19 +242,14 @@ static int do_sign(struct strbuf *buffer)
|
||||
if (finish_command(&gpg) || !len || len < 0)
|
||||
return error("gpg failed to sign the tag");
|
||||
|
||||
#ifdef __MINGW32__
|
||||
/* strip CR from the line endings */
|
||||
{
|
||||
int i, j;
|
||||
for (i = j = 0; i < buffer->len; i++)
|
||||
if (buffer->buf[i] != '\r') {
|
||||
if (i != j)
|
||||
buffer->buf[j] = buffer->buf[i];
|
||||
j++;
|
||||
}
|
||||
strbuf_setlen(buffer, j);
|
||||
}
|
||||
#endif
|
||||
/* Strip CR from the line endings, in case we are on Windows. */
|
||||
for (i = j = 0; i < buffer->len; i++)
|
||||
if (buffer->buf[i] != '\r') {
|
||||
if (i != j)
|
||||
buffer->buf[j] = buffer->buf[i];
|
||||
j++;
|
||||
}
|
||||
strbuf_setlen(buffer, j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "git-compat-util.h"
|
||||
#include "strbuf.h"
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
|
||||
extern const char git_version_string[];
|
||||
extern const char git_usage_string[];
|
||||
@@ -11,6 +14,10 @@ extern void list_common_cmds_help(void);
|
||||
extern void help_unknown_cmd(const char *cmd);
|
||||
extern void prune_packed_objects(int);
|
||||
extern int read_line_with_nul(char *buf, int size, FILE *file);
|
||||
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
|
||||
struct strbuf *out);
|
||||
extern int commit_tree(const char *msg, unsigned char *tree,
|
||||
struct commit_list *parents, unsigned char *ret);
|
||||
|
||||
extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
|
||||
@@ -57,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge(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);
|
||||
|
||||
6
cache.h
6
cache.h
@@ -254,6 +254,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
|
||||
|
||||
#define read_cache() read_index(&the_index)
|
||||
#define read_cache_from(path) read_index_from(&the_index, (path))
|
||||
#define read_cache_unmerged() read_index_unmerged(&the_index)
|
||||
#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)
|
||||
@@ -356,6 +357,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
|
||||
/* Initialize and use the cache information */
|
||||
extern int read_index(struct index_state *);
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int read_index_unmerged(struct index_state *);
|
||||
extern int write_index(const struct index_state *, int newfd);
|
||||
extern int discard_index(struct index_state *);
|
||||
extern int unmerged_index(const struct index_state *);
|
||||
@@ -538,6 +540,9 @@ extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsig
|
||||
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
|
||||
extern int force_object_loose(const unsigned char *sha1, time_t mtime);
|
||||
|
||||
/* just like read_sha1_file(), but non fatal in presence of bad objects */
|
||||
extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
|
||||
|
||||
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
|
||||
|
||||
extern int move_temp_to_file(const char *tmpfile, const char *filename);
|
||||
@@ -834,5 +839,6 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
|
||||
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
int split_cmdline(char *cmdline, const char ***argv);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
|
||||
143
commit.c
143
commit.c
@@ -325,6 +325,14 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list *
|
||||
return new_list;
|
||||
}
|
||||
|
||||
unsigned commit_list_count(const struct commit_list *l)
|
||||
{
|
||||
unsigned c = 0;
|
||||
for (; l; l = l->next )
|
||||
c++;
|
||||
return c;
|
||||
}
|
||||
|
||||
void free_commit_list(struct commit_list *list)
|
||||
{
|
||||
while (list) {
|
||||
@@ -525,26 +533,34 @@ static struct commit *interesting(struct commit_list *list)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct commit_list *merge_bases(struct commit *one, struct commit *two)
|
||||
static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
|
||||
{
|
||||
struct commit_list *list = NULL;
|
||||
struct commit_list *result = NULL;
|
||||
int i;
|
||||
|
||||
if (one == two)
|
||||
/* We do not mark this even with RESULT so we do not
|
||||
* have to clean it up.
|
||||
*/
|
||||
return commit_list_insert(one, &result);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (one == twos[i])
|
||||
/*
|
||||
* We do not mark this even with RESULT so we do not
|
||||
* have to clean it up.
|
||||
*/
|
||||
return commit_list_insert(one, &result);
|
||||
}
|
||||
|
||||
if (parse_commit(one))
|
||||
return NULL;
|
||||
if (parse_commit(two))
|
||||
return NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (parse_commit(twos[i]))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
one->object.flags |= PARENT1;
|
||||
two->object.flags |= PARENT2;
|
||||
insert_by_date(one, &list);
|
||||
insert_by_date(two, &list);
|
||||
for (i = 0; i < n; i++) {
|
||||
twos[i]->object.flags |= PARENT2;
|
||||
insert_by_date(twos[i], &list);
|
||||
}
|
||||
|
||||
while (interesting(list)) {
|
||||
struct commit *commit;
|
||||
@@ -592,21 +608,53 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
|
||||
return result;
|
||||
}
|
||||
|
||||
struct commit_list *get_merge_bases(struct commit *one,
|
||||
struct commit *two, int cleanup)
|
||||
struct commit_list *get_octopus_merge_bases(struct commit_list *in)
|
||||
{
|
||||
struct commit_list *i, *j, *k, *ret = NULL;
|
||||
struct commit_list **pptr = &ret;
|
||||
|
||||
for (i = in; i; i = i->next) {
|
||||
if (!ret)
|
||||
pptr = &commit_list_insert(i->item, pptr)->next;
|
||||
else {
|
||||
struct commit_list *new = NULL, *end = NULL;
|
||||
|
||||
for (j = ret; j; j = j->next) {
|
||||
struct commit_list *bases;
|
||||
bases = get_merge_bases(i->item, j->item, 1);
|
||||
if (!new)
|
||||
new = bases;
|
||||
else
|
||||
end->next = bases;
|
||||
for (k = bases; k; k = k->next)
|
||||
end = k;
|
||||
}
|
||||
ret = new;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct commit_list *get_merge_bases_many(struct commit *one,
|
||||
int n,
|
||||
struct commit **twos,
|
||||
int cleanup)
|
||||
{
|
||||
struct commit_list *list;
|
||||
struct commit **rslt;
|
||||
struct commit_list *result;
|
||||
int cnt, i, j;
|
||||
|
||||
result = merge_bases(one, two);
|
||||
if (one == two)
|
||||
return result;
|
||||
result = merge_bases_many(one, n, twos);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (one == twos[i])
|
||||
return result;
|
||||
}
|
||||
if (!result || !result->next) {
|
||||
if (cleanup) {
|
||||
clear_commit_marks(one, all_flags);
|
||||
clear_commit_marks(two, all_flags);
|
||||
for (i = 0; i < n; i++)
|
||||
clear_commit_marks(twos[i], all_flags);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -624,12 +672,13 @@ struct commit_list *get_merge_bases(struct commit *one,
|
||||
free_commit_list(result);
|
||||
|
||||
clear_commit_marks(one, all_flags);
|
||||
clear_commit_marks(two, all_flags);
|
||||
for (i = 0; i < n; i++)
|
||||
clear_commit_marks(twos[i], all_flags);
|
||||
for (i = 0; i < cnt - 1; i++) {
|
||||
for (j = i+1; j < cnt; j++) {
|
||||
if (!rslt[i] || !rslt[j])
|
||||
continue;
|
||||
result = merge_bases(rslt[i], rslt[j]);
|
||||
result = merge_bases_many(rslt[i], 1, &rslt[j]);
|
||||
clear_commit_marks(rslt[i], all_flags);
|
||||
clear_commit_marks(rslt[j], all_flags);
|
||||
for (list = result; list; list = list->next) {
|
||||
@@ -651,6 +700,12 @@ struct commit_list *get_merge_bases(struct commit *one,
|
||||
return result;
|
||||
}
|
||||
|
||||
struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
|
||||
int cleanup)
|
||||
{
|
||||
return get_merge_bases_many(one, 1, &two, cleanup);
|
||||
}
|
||||
|
||||
int in_merge_bases(struct commit *commit, struct commit **reference, int num)
|
||||
{
|
||||
struct commit_list *bases, *b;
|
||||
@@ -670,3 +725,55 @@ int in_merge_bases(struct commit *commit, struct commit **reference, int num)
|
||||
free_commit_list(bases);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct commit_list *reduce_heads(struct commit_list *heads)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_list *result = NULL, **tail = &result;
|
||||
struct commit **other;
|
||||
size_t num_head, num_other;
|
||||
|
||||
if (!heads)
|
||||
return NULL;
|
||||
|
||||
/* Avoid unnecessary reallocations */
|
||||
for (p = heads, num_head = 0; p; p = p->next)
|
||||
num_head++;
|
||||
other = xcalloc(sizeof(*other), num_head);
|
||||
|
||||
/* For each commit, see if it can be reached by others */
|
||||
for (p = heads; p; p = p->next) {
|
||||
struct commit_list *q, *base;
|
||||
|
||||
/* Do we already have this in the result? */
|
||||
for (q = result; q; q = q->next)
|
||||
if (p->item == q->item)
|
||||
break;
|
||||
if (q)
|
||||
continue;
|
||||
|
||||
num_other = 0;
|
||||
for (q = heads; q; q = q->next) {
|
||||
if (p->item == q->item)
|
||||
continue;
|
||||
other[num_other++] = q->item;
|
||||
}
|
||||
if (num_other)
|
||||
base = get_merge_bases_many(p->item, num_other, other, 1);
|
||||
else
|
||||
base = NULL;
|
||||
/*
|
||||
* If p->item does not have anything common with other
|
||||
* commits, there won't be any merge base. If it is
|
||||
* reachable from some of the others, p->item will be
|
||||
* the merge base. If its history is connected with
|
||||
* others, but p->item is not reachable by others, we
|
||||
* will get something other than p->item back.
|
||||
*/
|
||||
if (!base || (base->item != p->item))
|
||||
tail = &(commit_list_insert(p->item, tail)->next);
|
||||
free_commit_list(base);
|
||||
}
|
||||
free(other);
|
||||
return result;
|
||||
}
|
||||
|
||||
5
commit.h
5
commit.h
@@ -41,6 +41,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
|
||||
int parse_commit(struct commit *item);
|
||||
|
||||
struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
|
||||
unsigned commit_list_count(const struct commit_list *l);
|
||||
struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
|
||||
|
||||
void free_commit_list(struct commit_list *list);
|
||||
@@ -120,6 +121,7 @@ int read_graft_file(const char *graft_file);
|
||||
struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
|
||||
|
||||
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
|
||||
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
|
||||
|
||||
extern int register_shallow(const unsigned char *sha1);
|
||||
extern int unregister_shallow(const unsigned char *sha1);
|
||||
@@ -131,11 +133,12 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
|
||||
int in_merge_bases(struct commit *, struct commit **, int);
|
||||
|
||||
extern int interactive_add(int argc, const char **argv, const char *prefix);
|
||||
extern int rerere(void);
|
||||
|
||||
static inline int single_parent(struct commit *commit)
|
||||
{
|
||||
return commit->parents && !commit->parents->next;
|
||||
}
|
||||
|
||||
struct commit_list *reduce_heads(struct commit_list *heads);
|
||||
|
||||
#endif /* COMMIT_H */
|
||||
|
||||
@@ -536,7 +536,8 @@ static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_on
|
||||
return xstrdup(path);
|
||||
path[strlen(path)-4] = '\0';
|
||||
if ((!exe_only || isexe) && access(path, F_OK) == 0)
|
||||
return xstrdup(path);
|
||||
if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY))
|
||||
return xstrdup(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,17 @@ static inline unsigned int git_ntohl(unsigned int x)
|
||||
sig_handler_t mingw_signal(int sig, sig_handler_t handler);
|
||||
#define signal mingw_signal
|
||||
|
||||
/*
|
||||
* ANSI emulation wrappers
|
||||
*/
|
||||
|
||||
int winansi_fputs(const char *str, FILE *stream);
|
||||
int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
|
||||
int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
|
||||
#define fputs winansi_fputs
|
||||
#define printf(...) winansi_printf(__VA_ARGS__)
|
||||
#define fprintf(...) winansi_fprintf(__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* git specific compatibility
|
||||
*/
|
||||
|
||||
502
compat/winansi.c
502
compat/winansi.c
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
* Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "../git-compat-util.h"
|
||||
|
||||
@@ -5,10 +9,17 @@
|
||||
Functions to be wrapped:
|
||||
*/
|
||||
#undef printf
|
||||
#undef fprintf
|
||||
#undef fputs
|
||||
/* TODO: write */
|
||||
|
||||
/*
|
||||
ANSI codes to implement: m, K
|
||||
ANSI codes used by git: m, K
|
||||
|
||||
This file is git-specific. Therefore, this file does not attempt
|
||||
to implement any codes that are not used by git.
|
||||
|
||||
TODO: K
|
||||
*/
|
||||
|
||||
static HANDLE console;
|
||||
@@ -18,24 +29,24 @@ static int negative;
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi;
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi;
|
||||
|
||||
static int initialized = 0;
|
||||
if (initialized)
|
||||
return;
|
||||
static int initialized = 0;
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (console == INVALID_HANDLE_VALUE)
|
||||
console = NULL;
|
||||
console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (console == INVALID_HANDLE_VALUE)
|
||||
console = NULL;
|
||||
|
||||
if (!console)
|
||||
return;
|
||||
if (!console)
|
||||
return;
|
||||
|
||||
GetConsoleScreenBufferInfo(console, &sbi);
|
||||
attr = plain_attr = sbi.wAttributes;
|
||||
negative = 0;
|
||||
GetConsoleScreenBufferInfo(console, &sbi);
|
||||
attr = plain_attr = sbi.wAttributes;
|
||||
negative = 0;
|
||||
|
||||
initialized = 1;
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,266 +55,291 @@ static void init(void)
|
||||
|
||||
static void set_console_attr(void)
|
||||
{
|
||||
WORD attributes = attr;
|
||||
if (negative) {
|
||||
attributes &= ~FOREGROUND_ALL;
|
||||
attributes &= ~BACKGROUND_ALL;
|
||||
WORD attributes = attr;
|
||||
if (negative) {
|
||||
attributes &= ~FOREGROUND_ALL;
|
||||
attributes &= ~BACKGROUND_ALL;
|
||||
|
||||
/* This could probably use a bitmask instead of a series of ifs */
|
||||
if (attr & FOREGROUND_RED)
|
||||
attributes |= BACKGROUND_RED;
|
||||
if (attr & FOREGROUND_GREEN)
|
||||
attributes |= BACKGROUND_GREEN;
|
||||
if (attr & FOREGROUND_BLUE)
|
||||
attributes |= BACKGROUND_BLUE;
|
||||
/* This could probably use a bitmask
|
||||
instead of a series of ifs */
|
||||
if (attr & FOREGROUND_RED)
|
||||
attributes |= BACKGROUND_RED;
|
||||
if (attr & FOREGROUND_GREEN)
|
||||
attributes |= BACKGROUND_GREEN;
|
||||
if (attr & FOREGROUND_BLUE)
|
||||
attributes |= BACKGROUND_BLUE;
|
||||
|
||||
if (attr & BACKGROUND_RED)
|
||||
attributes |= FOREGROUND_RED;
|
||||
if (attr & BACKGROUND_GREEN)
|
||||
attributes |= FOREGROUND_GREEN;
|
||||
if (attr & BACKGROUND_BLUE)
|
||||
attributes |= FOREGROUND_BLUE;
|
||||
}
|
||||
SetConsoleTextAttribute(console, attributes);
|
||||
if (attr & BACKGROUND_RED)
|
||||
attributes |= FOREGROUND_RED;
|
||||
if (attr & BACKGROUND_GREEN)
|
||||
attributes |= FOREGROUND_GREEN;
|
||||
if (attr & BACKGROUND_BLUE)
|
||||
attributes |= FOREGROUND_BLUE;
|
||||
}
|
||||
SetConsoleTextAttribute(console, attributes);
|
||||
}
|
||||
|
||||
static const char *set_attr(const char *str)
|
||||
{
|
||||
const char *func;
|
||||
size_t len = strspn(str, "0123456789;");
|
||||
func = str + len;
|
||||
const char *func;
|
||||
size_t len = strspn(str, "0123456789;");
|
||||
func = str + len;
|
||||
|
||||
switch (*func) {
|
||||
case 'm':
|
||||
do {
|
||||
long val = strtol(str, (char **)&str, 10);
|
||||
switch (val) {
|
||||
case 0: /* reset */
|
||||
attr = plain_attr;
|
||||
negative = 0;
|
||||
switch (*func) {
|
||||
case 'm':
|
||||
do {
|
||||
long val = strtol(str, (char **)&str, 10);
|
||||
switch (val) {
|
||||
case 0: /* reset */
|
||||
attr = plain_attr;
|
||||
negative = 0;
|
||||
break;
|
||||
case 1: /* bold */
|
||||
attr |= FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case 2: /* faint */
|
||||
case 22: /* normal */
|
||||
attr &= ~FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case 3: /* italic */
|
||||
/* Unsupported */
|
||||
break;
|
||||
case 4: /* underline */
|
||||
case 21: /* double underline */
|
||||
/* Wikipedia says this flag does nothing */
|
||||
/* Furthermore, mingw doesn't define this flag
|
||||
attr |= COMMON_LVB_UNDERSCORE; */
|
||||
break;
|
||||
case 24: /* no underline */
|
||||
/* attr &= ~COMMON_LVB_UNDERSCORE; */
|
||||
break;
|
||||
case 5: /* slow blink */
|
||||
case 6: /* fast blink */
|
||||
/* We don't have blink, but we do have
|
||||
background intensity */
|
||||
attr |= BACKGROUND_INTENSITY;
|
||||
break;
|
||||
case 25: /* no blink */
|
||||
attr &= ~BACKGROUND_INTENSITY;
|
||||
break;
|
||||
case 7: /* negative */
|
||||
negative = 1;
|
||||
break;
|
||||
case 27: /* positive */
|
||||
negative = 0;
|
||||
break;
|
||||
case 8: /* conceal */
|
||||
case 28: /* reveal */
|
||||
/* Unsupported */
|
||||
break;
|
||||
case 30: /* Black */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
break;
|
||||
case 31: /* Red */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_RED;
|
||||
break;
|
||||
case 32: /* Green */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_GREEN;
|
||||
break;
|
||||
case 33: /* Yellow */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
break;
|
||||
case 34: /* Blue */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_BLUE;
|
||||
break;
|
||||
case 35: /* Magenta */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_RED | FOREGROUND_BLUE;
|
||||
break;
|
||||
case 36: /* Cyan */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
case 37: /* White */
|
||||
attr |= FOREGROUND_RED |
|
||||
FOREGROUND_GREEN |
|
||||
FOREGROUND_BLUE;
|
||||
break;
|
||||
case 38: /* Unknown */
|
||||
break;
|
||||
case 39: /* reset */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= (plain_attr & FOREGROUND_ALL);
|
||||
break;
|
||||
case 40: /* Black */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
break;
|
||||
case 41: /* Red */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_RED;
|
||||
break;
|
||||
case 42: /* Green */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_GREEN;
|
||||
break;
|
||||
case 43: /* Yellow */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_RED | BACKGROUND_GREEN;
|
||||
break;
|
||||
case 44: /* Blue */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_BLUE;
|
||||
break;
|
||||
case 45: /* Magenta */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_RED | BACKGROUND_BLUE;
|
||||
break;
|
||||
case 46: /* Cyan */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
|
||||
break;
|
||||
case 47: /* White */
|
||||
attr |= BACKGROUND_RED |
|
||||
BACKGROUND_GREEN |
|
||||
BACKGROUND_BLUE;
|
||||
break;
|
||||
case 48: /* Unknown */
|
||||
break;
|
||||
case 49: /* reset */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= (plain_attr & BACKGROUND_ALL);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported code */
|
||||
break;
|
||||
}
|
||||
str++;
|
||||
} while (*(str-1) == ';');
|
||||
|
||||
set_console_attr();
|
||||
break;
|
||||
case 1: /* bold */
|
||||
attr |= FOREGROUND_INTENSITY;
|
||||
case 'K':
|
||||
/* TODO */
|
||||
break;
|
||||
case 2: /* faint */
|
||||
case 22: /* normal */
|
||||
attr &= ~FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case 3: /* italic */
|
||||
/* Unsupported */
|
||||
break;
|
||||
case 4: /* underline */
|
||||
case 21: /* double underline */
|
||||
/* Wikipedia says this flag does nothing */
|
||||
/* Furthermore, mingw doesn't define this flag
|
||||
attr |= COMMON_LVB_UNDERSCORE; */
|
||||
break;
|
||||
case 24: /* no underline */
|
||||
/* attr &= ~COMMON_LVB_UNDERSCORE; */
|
||||
break;
|
||||
case 5: /* slow blink */
|
||||
case 6: /* fast blink */
|
||||
/* We don't have blink, but we do have background intensity */
|
||||
attr |= BACKGROUND_INTENSITY;
|
||||
break;
|
||||
case 25: /* no blink */
|
||||
attr &= ~BACKGROUND_INTENSITY;
|
||||
break;
|
||||
case 7: /* negative */
|
||||
negative = 1;
|
||||
break;
|
||||
case 27: /* positive */
|
||||
negative = 0;
|
||||
break;
|
||||
case 8: /* conceal */
|
||||
case 28: /* reveal */
|
||||
/* Unsupported */
|
||||
break;
|
||||
case 30: /* Black */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
break;
|
||||
case 31: /* Red */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_RED;
|
||||
break;
|
||||
case 32: /* Green */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_GREEN;
|
||||
break;
|
||||
case 33: /* Yellow */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
break;
|
||||
case 34: /* Blue */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_BLUE;
|
||||
break;
|
||||
case 35: /* Magenta */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_RED | FOREGROUND_BLUE;
|
||||
break;
|
||||
case 36: /* Cyan */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
case 37: /* White */
|
||||
attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
case 38: /* Unknown */
|
||||
break;
|
||||
case 39: /* reset */
|
||||
attr &= ~FOREGROUND_ALL;
|
||||
attr |= (plain_attr & FOREGROUND_ALL);
|
||||
break;
|
||||
case 40: /* Black */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
break;
|
||||
case 41: /* Red */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_RED;
|
||||
break;
|
||||
case 42: /* Green */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_GREEN;
|
||||
break;
|
||||
case 43: /* Yellow */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_RED | BACKGROUND_GREEN;
|
||||
break;
|
||||
case 44: /* Blue */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_BLUE;
|
||||
break;
|
||||
case 45: /* Magenta */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_RED | BACKGROUND_BLUE;
|
||||
break;
|
||||
case 46: /* Cyan */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
|
||||
break;
|
||||
case 47: /* White */
|
||||
attr |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
|
||||
break;
|
||||
case 48: /* Unknown */
|
||||
break;
|
||||
case 49: /* reset */
|
||||
attr &= ~BACKGROUND_ALL;
|
||||
attr |= (plain_attr & BACKGROUND_ALL);
|
||||
break;
|
||||
default:
|
||||
default:
|
||||
/* Unsupported code */
|
||||
break;
|
||||
}
|
||||
str++;
|
||||
} while (*(str-1) == ';');
|
||||
}
|
||||
|
||||
set_console_attr();
|
||||
break;
|
||||
case 'K':
|
||||
/* TODO */
|
||||
break;
|
||||
default:
|
||||
/* Unsupported code */
|
||||
break;
|
||||
}
|
||||
|
||||
return func + 1;
|
||||
return func + 1;
|
||||
}
|
||||
|
||||
static int ansi_emulate(const char *str, FILE *stream)
|
||||
{
|
||||
int rv = 0;
|
||||
const char *pos = str;
|
||||
int rv = 0;
|
||||
const char *pos = str;
|
||||
|
||||
while (*pos) {
|
||||
pos = strstr(str, "\033[");
|
||||
if (pos) {
|
||||
size_t len = pos - str;
|
||||
while (*pos) {
|
||||
pos = strstr(str, "\033[");
|
||||
if (pos) {
|
||||
size_t len = pos - str;
|
||||
|
||||
if (len) {
|
||||
size_t output_len = fwrite(str, 1, len, stream);
|
||||
rv += output_len;
|
||||
if (output_len < len)
|
||||
return rv;
|
||||
}
|
||||
if (len) {
|
||||
size_t out_len = fwrite(str, 1, len, stream);
|
||||
rv += out_len;
|
||||
if (out_len < len)
|
||||
return rv;
|
||||
}
|
||||
|
||||
str = pos + 2;
|
||||
rv += 2;
|
||||
str = pos + 2;
|
||||
rv += 2;
|
||||
|
||||
fflush(stream);
|
||||
fflush(stream);
|
||||
|
||||
pos = set_attr(str);
|
||||
rv += pos - str;
|
||||
str = pos;
|
||||
} else {
|
||||
rv += strlen(str);
|
||||
fputs(str, stream);
|
||||
return rv;
|
||||
pos = set_attr(str);
|
||||
rv += pos - str;
|
||||
str = pos;
|
||||
} else {
|
||||
rv += strlen(str);
|
||||
fputs(str, stream);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
int git_fputs(const char *str, FILE *stream)
|
||||
int winansi_fputs(const char *str, FILE *stream)
|
||||
{
|
||||
int rv;
|
||||
int rv;
|
||||
|
||||
init();
|
||||
if (!isatty(fileno(stream)))
|
||||
return fputs(str, stream);
|
||||
|
||||
if (!console)
|
||||
return fputs(str, stream);
|
||||
init();
|
||||
|
||||
if (!isatty(fileno(stream)))
|
||||
return fputs(str, stream);
|
||||
if (!console)
|
||||
return fputs(str, stream);
|
||||
|
||||
rv = ansi_emulate(str, stream);
|
||||
rv = ansi_emulate(str, stream);
|
||||
|
||||
if (rv >= 0)
|
||||
return 0;
|
||||
else
|
||||
return EOF;
|
||||
if (rv >= 0)
|
||||
return 0;
|
||||
else
|
||||
return EOF;
|
||||
}
|
||||
|
||||
int git_printf(const char *format, ...)
|
||||
static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
|
||||
{
|
||||
va_list list;
|
||||
int len, rv;
|
||||
char small_buf[256];
|
||||
char *buf = small_buf;
|
||||
va_list cp;
|
||||
|
||||
char small_buf[256];
|
||||
char *buf = small_buf;
|
||||
int len, rv;
|
||||
if (!isatty(fileno(stream)))
|
||||
goto abort;
|
||||
|
||||
init();
|
||||
init();
|
||||
|
||||
if (!console)
|
||||
goto abort;
|
||||
if (!console)
|
||||
goto abort;
|
||||
|
||||
if (!isatty(fileno(stdout)))
|
||||
goto abort;
|
||||
va_copy(cp, list);
|
||||
len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
|
||||
va_end(cp);
|
||||
|
||||
va_start(list, format);
|
||||
len = vsnprintf(small_buf, sizeof(small_buf), format, list);
|
||||
va_end(list);
|
||||
if (len > sizeof(small_buf) - 1) {
|
||||
buf = malloc(len + 1);
|
||||
if (!buf)
|
||||
goto abort;
|
||||
|
||||
if (len > sizeof(small_buf) - 1) {
|
||||
buf = malloc(len + 1);
|
||||
if (!buf)
|
||||
goto abort;
|
||||
len = vsnprintf(buf, len + 1, format, list);
|
||||
}
|
||||
|
||||
va_start(list, format);
|
||||
len = vsnprintf(buf, len + 1, format, list);
|
||||
va_end(list);
|
||||
}
|
||||
rv = ansi_emulate(buf, stream);
|
||||
|
||||
rv = ansi_emulate(buf, stdout);
|
||||
|
||||
if (buf != small_buf)
|
||||
free(buf);
|
||||
return rv;
|
||||
if (buf != small_buf)
|
||||
free(buf);
|
||||
return rv;
|
||||
|
||||
abort:
|
||||
va_start(list, format);
|
||||
rv = vprintf(format, list);
|
||||
va_end(list);
|
||||
return rv;
|
||||
rv = vfprintf(stream, format, list);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int winansi_fprintf(FILE *stream, const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
int rv;
|
||||
|
||||
va_start(list, format);
|
||||
rv = winansi_vfprintf(stream, format, list);
|
||||
va_end(list);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int winansi_printf(const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
int rv;
|
||||
|
||||
va_start(list, format);
|
||||
rv = winansi_vfprintf(stdout, format, list);
|
||||
va_end(list);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,11 @@
|
||||
# git@vger.kernel.org
|
||||
#
|
||||
|
||||
case "$COMP_WORDBREAKS" in
|
||||
*:*) : great ;;
|
||||
*) COMP_WORDBREAKS="$COMP_WORDBREAKS:"
|
||||
esac
|
||||
|
||||
__gitdir ()
|
||||
{
|
||||
if [ -z "$1" ]; then
|
||||
@@ -68,26 +73,26 @@ __git_ps1 ()
|
||||
if [ -n "$g" ]; then
|
||||
local r
|
||||
local b
|
||||
if [ -d "$g/../.dotest" ]
|
||||
if [ -d "$g/rebase" ]
|
||||
then
|
||||
if test -f "$g/../.dotest/rebasing"
|
||||
if test -f "$g/rebase/rebasing"
|
||||
then
|
||||
r="|REBASE"
|
||||
elif test -f "$g/../.dotest/applying"
|
||||
elif test -f "$g/rebase/applying"
|
||||
then
|
||||
r="|AM"
|
||||
else
|
||||
r="|AM/REBASE"
|
||||
fi
|
||||
b="$(git symbolic-ref HEAD 2>/dev/null)"
|
||||
elif [ -f "$g/.dotest-merge/interactive" ]
|
||||
elif [ -f "$g/rebase-merge/interactive" ]
|
||||
then
|
||||
r="|REBASE-i"
|
||||
b="$(cat "$g/.dotest-merge/head-name")"
|
||||
elif [ -d "$g/.dotest-merge" ]
|
||||
b="$(cat "$g/rebase-merge/head-name")"
|
||||
elif [ -d "$g/rebase-merge" ]
|
||||
then
|
||||
r="|REBASE-m"
|
||||
b="$(cat "$g/.dotest-merge/head-name")"
|
||||
b="$(cat "$g/rebase-merge/head-name")"
|
||||
elif [ -f "$g/MERGE_HEAD" ]
|
||||
then
|
||||
r="|MERGING"
|
||||
@@ -294,9 +299,23 @@ __git_complete_file ()
|
||||
ls="$ref"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$COMP_WORDBREAKS" in
|
||||
*:*) : great ;;
|
||||
*) pfx="$ref:$pfx" ;;
|
||||
esac
|
||||
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -P "$pfx" \
|
||||
-W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
|
||||
| sed '/^100... blob /s,^.* ,,
|
||||
| sed '/^100... blob /{
|
||||
s,^.* ,,
|
||||
s,$, ,
|
||||
}
|
||||
/^120000 blob /{
|
||||
s,^.* ,,
|
||||
s,$, ,
|
||||
}
|
||||
/^040000 tree /{
|
||||
s,^.* ,,
|
||||
s,$,/,
|
||||
@@ -468,8 +487,8 @@ __git_whitespacelist="nowarn warn error error-all strip"
|
||||
|
||||
_git_am ()
|
||||
{
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
if [ -d .dotest ]; then
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
|
||||
if [ -d "$dir"/rebase ]; then
|
||||
__gitcomp "--skip --resolved"
|
||||
return
|
||||
fi
|
||||
@@ -692,7 +711,12 @@ _git_fetch ()
|
||||
*)
|
||||
case "$cur" in
|
||||
*:*)
|
||||
__gitcomp "$(__git_refs)" "" "${cur#*:}"
|
||||
local pfx=""
|
||||
case "$COMP_WORDBREAKS" in
|
||||
*:*) : great ;;
|
||||
*) pfx="${cur%%:*}:" ;;
|
||||
esac
|
||||
__gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
|
||||
;;
|
||||
*)
|
||||
local remote
|
||||
@@ -868,7 +892,14 @@ _git_push ()
|
||||
git-push) remote="${COMP_WORDS[1]}" ;;
|
||||
git) remote="${COMP_WORDS[2]}" ;;
|
||||
esac
|
||||
__gitcomp "$(__git_refs "$remote")" "" "${cur#*:}"
|
||||
|
||||
local pfx=""
|
||||
case "$COMP_WORDBREAKS" in
|
||||
*:*) : great ;;
|
||||
*) pfx="${cur%%:*}:" ;;
|
||||
esac
|
||||
|
||||
__gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}"
|
||||
;;
|
||||
+*)
|
||||
__gitcomp "$(__git_refs)" + "${cur#+}"
|
||||
@@ -884,7 +915,7 @@ _git_push ()
|
||||
_git_rebase ()
|
||||
{
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
|
||||
if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then
|
||||
if [ -d "$dir"/rebase ] || [ -d "$dir"/rebase-merge ]; then
|
||||
__gitcomp "--continue --skip --abort"
|
||||
return
|
||||
fi
|
||||
@@ -905,6 +936,24 @@ _git_rebase ()
|
||||
__gitcomp "$(__git_refs)"
|
||||
}
|
||||
|
||||
_git_send_email ()
|
||||
{
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
case "$cur" in
|
||||
--*)
|
||||
__gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose
|
||||
--dry-run --envelope-sender --from --identity
|
||||
--in-reply-to --no-chain-reply-to --no-signed-off-by-cc
|
||||
--no-suppress-from --no-thread --quiet
|
||||
--signed-off-by-cc --smtp-pass --smtp-server
|
||||
--smtp-server-port --smtp-ssl --smtp-user --subject
|
||||
--suppress-cc --suppress-from --thread --to"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=()
|
||||
}
|
||||
|
||||
_git_config ()
|
||||
{
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
@@ -1376,6 +1425,7 @@ _git ()
|
||||
rebase) _git_rebase ;;
|
||||
remote) _git_remote ;;
|
||||
reset) _git_reset ;;
|
||||
send-email) _git_send_email ;;
|
||||
shortlog) _git_shortlog ;;
|
||||
show) _git_show ;;
|
||||
show-branch) _git_log ;;
|
||||
@@ -1409,64 +1459,11 @@ _gitk ()
|
||||
|
||||
complete -o default -o nospace -F _git git
|
||||
complete -o default -o nospace -F _gitk gitk
|
||||
complete -o default -o nospace -F _git_am git-am
|
||||
complete -o default -o nospace -F _git_apply git-apply
|
||||
complete -o default -o nospace -F _git_bisect git-bisect
|
||||
complete -o default -o nospace -F _git_branch git-branch
|
||||
complete -o default -o nospace -F _git_bundle git-bundle
|
||||
complete -o default -o nospace -F _git_checkout git-checkout
|
||||
complete -o default -o nospace -F _git_cherry git-cherry
|
||||
complete -o default -o nospace -F _git_cherry_pick git-cherry-pick
|
||||
complete -o default -o nospace -F _git_commit git-commit
|
||||
complete -o default -o nospace -F _git_describe git-describe
|
||||
complete -o default -o nospace -F _git_diff git-diff
|
||||
complete -o default -o nospace -F _git_fetch git-fetch
|
||||
complete -o default -o nospace -F _git_format_patch git-format-patch
|
||||
complete -o default -o nospace -F _git_gc git-gc
|
||||
complete -o default -o nospace -F _git_log git-log
|
||||
complete -o default -o nospace -F _git_ls_remote git-ls-remote
|
||||
complete -o default -o nospace -F _git_ls_tree git-ls-tree
|
||||
complete -o default -o nospace -F _git_merge git-merge
|
||||
complete -o default -o nospace -F _git_merge_base git-merge-base
|
||||
complete -o default -o nospace -F _git_name_rev git-name-rev
|
||||
complete -o default -o nospace -F _git_pull git-pull
|
||||
complete -o default -o nospace -F _git_push git-push
|
||||
complete -o default -o nospace -F _git_rebase git-rebase
|
||||
complete -o default -o nospace -F _git_config git-config
|
||||
complete -o default -o nospace -F _git_remote git-remote
|
||||
complete -o default -o nospace -F _git_reset git-reset
|
||||
complete -o default -o nospace -F _git_shortlog git-shortlog
|
||||
complete -o default -o nospace -F _git_show git-show
|
||||
complete -o default -o nospace -F _git_stash git-stash
|
||||
complete -o default -o nospace -F _git_submodule git-submodule
|
||||
complete -o default -o nospace -F _git_svn git-svn
|
||||
complete -o default -o nospace -F _git_log git-show-branch
|
||||
complete -o default -o nospace -F _git_tag git-tag
|
||||
complete -o default -o nospace -F _git_log git-whatchanged
|
||||
|
||||
# The following are necessary only for Cygwin, and only are needed
|
||||
# when the user has tab-completed the executable name and consequently
|
||||
# included the '.exe' suffix.
|
||||
#
|
||||
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
|
||||
complete -o default -o nospace -F _git_add git-add.exe
|
||||
complete -o default -o nospace -F _git_apply git-apply.exe
|
||||
complete -o default -o nospace -F _git git.exe
|
||||
complete -o default -o nospace -F _git_branch git-branch.exe
|
||||
complete -o default -o nospace -F _git_bundle git-bundle.exe
|
||||
complete -o default -o nospace -F _git_cherry git-cherry.exe
|
||||
complete -o default -o nospace -F _git_describe git-describe.exe
|
||||
complete -o default -o nospace -F _git_diff git-diff.exe
|
||||
complete -o default -o nospace -F _git_format_patch git-format-patch.exe
|
||||
complete -o default -o nospace -F _git_log git-log.exe
|
||||
complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
|
||||
complete -o default -o nospace -F _git_merge_base git-merge-base.exe
|
||||
complete -o default -o nospace -F _git_name_rev git-name-rev.exe
|
||||
complete -o default -o nospace -F _git_push git-push.exe
|
||||
complete -o default -o nospace -F _git_config git-config
|
||||
complete -o default -o nospace -F _git_shortlog git-shortlog.exe
|
||||
complete -o default -o nospace -F _git_show git-show.exe
|
||||
complete -o default -o nospace -F _git_log git-show-branch.exe
|
||||
complete -o default -o nospace -F _git_tag git-tag.exe
|
||||
complete -o default -o nospace -F _git_log git-whatchanged.exe
|
||||
fi
|
||||
|
||||
@@ -1252,8 +1252,8 @@ Return the list of files that haven't been handled."
|
||||
"\n")
|
||||
(when subject (insert subject "\n\n"))
|
||||
(cond (msg (insert msg "\n"))
|
||||
((file-readable-p ".dotest/msg")
|
||||
(insert-file-contents ".dotest/msg"))
|
||||
((file-readable-p ".git/rebase/msg")
|
||||
(insert-file-contents ".git/rebase/msg"))
|
||||
((file-readable-p ".git/MERGE_MSG")
|
||||
(insert-file-contents ".git/MERGE_MSG")))
|
||||
; delete empty lines at end
|
||||
@@ -1272,9 +1272,9 @@ Return the list of files that haven't been handled."
|
||||
(coding-system (git-get-commits-coding-system))
|
||||
author-name author-email subject date)
|
||||
(when (eq 0 (buffer-size buffer))
|
||||
(when (file-readable-p ".dotest/info")
|
||||
(when (file-readable-p ".git/rebase/info")
|
||||
(with-temp-buffer
|
||||
(insert-file-contents ".dotest/info")
|
||||
(insert-file-contents ".git/rebase/info")
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
|
||||
(setq author-name (match-string 1))
|
||||
|
||||
@@ -62,7 +62,7 @@ static void gather_stats(const char *buf, unsigned long size, struct text_stat *
|
||||
stats->printable++;
|
||||
}
|
||||
|
||||
// If file ends with EOF then don't count this EOF as non-printable
|
||||
/* If file ends with EOF then don't count this EOF as non-printable. */
|
||||
if (size >= 1 && buf[size-1] == '\032')
|
||||
stats->nonprintable--;
|
||||
}
|
||||
|
||||
@@ -406,9 +406,9 @@ sub list_and_choose {
|
||||
if ($choice =~ s/^-//) {
|
||||
$choose = 0;
|
||||
}
|
||||
# A range can be specified like 5-7
|
||||
if ($choice =~ /^(\d+)-(\d+)$/) {
|
||||
($bottom, $top) = ($1, $2);
|
||||
# A range can be specified like 5-7 or 5-.
|
||||
if ($choice =~ /^(\d+)-(\d*)$/) {
|
||||
($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff);
|
||||
}
|
||||
elsif ($choice =~ /^\d+$/) {
|
||||
$bottom = $top = $choice;
|
||||
|
||||
@@ -119,7 +119,7 @@ It does not apply to blobs recorded in its index."
|
||||
}
|
||||
|
||||
prec=4
|
||||
dotest=".dotest"
|
||||
dotest="$GIT_DIR/rebase"
|
||||
sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
|
||||
resolvemsg= resume=
|
||||
git_apply_opt=
|
||||
@@ -195,7 +195,7 @@ then
|
||||
false
|
||||
;;
|
||||
esac ||
|
||||
die "previous dotest directory $dotest still exists but mbox given."
|
||||
die "previous rebase directory $dotest still exists but mbox given."
|
||||
resume=yes
|
||||
else
|
||||
# Make sure we are not given --skip nor --resolved
|
||||
@@ -242,6 +242,7 @@ else
|
||||
: >"$dotest/rebasing"
|
||||
else
|
||||
: >"$dotest/applying"
|
||||
git update-ref ORIG_HEAD HEAD
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -324,7 +325,7 @@ do
|
||||
<"$dotest"/info >/dev/null &&
|
||||
go_next && continue
|
||||
|
||||
test -s $dotest/patch || {
|
||||
test -s "$dotest/patch" || {
|
||||
echo "Patch is empty. Was it split wrong?"
|
||||
stop_here $this
|
||||
}
|
||||
|
||||
@@ -157,6 +157,12 @@ extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
|
||||
extern int prefixcmp(const char *str, const char *prefix);
|
||||
extern time_t tm_to_time_t(const struct tm *tm);
|
||||
|
||||
static inline const char *skip_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
#ifdef NO_MMAP
|
||||
|
||||
#ifndef PROT_READ
|
||||
@@ -365,11 +371,4 @@ void git_qsort(void *base, size_t nmemb, size_t size,
|
||||
# define FORCE_DIR_SET_GID 0
|
||||
#endif
|
||||
|
||||
#ifdef WIN_ANSI
|
||||
extern int git_fputs(const char *str, FILE *stream);
|
||||
extern int git_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
|
||||
#define fputs git_fputs
|
||||
#define printf(...) git_printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1995,9 +1995,13 @@ if {[is_enabled multicommit]} {
|
||||
}
|
||||
}
|
||||
|
||||
.mbar.repository add command -label [mc Quit] \
|
||||
-command do_quit \
|
||||
-accelerator $M1T-Q
|
||||
if {[is_MacOSX]} {
|
||||
proc ::tk::mac::Quit {args} { do_quit }
|
||||
} else {
|
||||
.mbar.repository add command -label [mc Quit] \
|
||||
-command do_quit \
|
||||
-accelerator $M1T-Q
|
||||
}
|
||||
|
||||
# -- Edit Menu
|
||||
#
|
||||
|
||||
@@ -257,6 +257,7 @@ proc _reset_wait {fd} {
|
||||
|
||||
catch {file delete [gitdir MERGE_HEAD]}
|
||||
catch {file delete [gitdir rr-cache MERGE_RR]}
|
||||
catch {file delete [gitdir MERGE_RR]}
|
||||
catch {file delete [gitdir SQUASH_MSG]}
|
||||
catch {file delete [gitdir MERGE_MSG]}
|
||||
catch {file delete [gitdir GITGUI_MSG]}
|
||||
|
||||
@@ -53,7 +53,7 @@ if ! [ -d "$QUILT_PATCHES" ] ; then
|
||||
fi
|
||||
|
||||
# Temporary directories
|
||||
tmp_dir=.dotest
|
||||
tmp_dir="$GIT_DIR"/rebase
|
||||
tmp_msg="$tmp_dir/msg"
|
||||
tmp_patch="$tmp_dir/patch"
|
||||
tmp_info="$tmp_dir/info"
|
||||
|
||||
@@ -31,7 +31,7 @@ skip skip current patch and continue rebasing process
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
|
||||
DOTEST="$GIT_DIR/.dotest-merge"
|
||||
DOTEST="$GIT_DIR/rebase-merge"
|
||||
TODO="$DOTEST"/git-rebase-todo
|
||||
DONE="$DOTEST"/done
|
||||
MSG="$DOTEST"/message
|
||||
@@ -574,6 +574,7 @@ EOF
|
||||
has_action "$TODO" ||
|
||||
die_abort "Nothing to do"
|
||||
|
||||
git update-ref ORIG_HEAD $HEAD
|
||||
output git checkout $ONTO && do_rest
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -14,7 +14,7 @@ It is possible that a merge failure will prevent this process from being
|
||||
completely automatic. You will have to resolve any such merge failure
|
||||
and run git rebase --continue. Another option is to bypass the commit
|
||||
that caused the merge failure with git rebase --skip. To restore the
|
||||
original <branch> and remove the .dotest working files, use the command
|
||||
original <branch> and remove the .git/rebase working files, use the command
|
||||
git rebase --abort instead.
|
||||
|
||||
Note that if <branch> is not specified on the command line, the
|
||||
@@ -42,7 +42,7 @@ To restore the original branch and stop rebasing run \"git rebase --abort\".
|
||||
unset newbase
|
||||
strategy=recursive
|
||||
do_merge=
|
||||
dotest=$GIT_DIR/.dotest-merge
|
||||
dotest="$GIT_DIR"/rebase-merge
|
||||
prec=4
|
||||
verbose=
|
||||
git_am_opt=
|
||||
@@ -150,7 +150,7 @@ while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
--continue)
|
||||
test -d "$dotest" -o -d .dotest ||
|
||||
test -d "$dotest" -o -d "$GIT_DIR"/rebase ||
|
||||
die "No rebase in progress?"
|
||||
|
||||
git diff-files --quiet --ignore-submodules || {
|
||||
@@ -173,15 +173,15 @@ do
|
||||
finish_rb_merge
|
||||
exit
|
||||
fi
|
||||
head_name=$(cat .dotest/head-name) &&
|
||||
onto=$(cat .dotest/onto) &&
|
||||
orig_head=$(cat .dotest/orig-head) &&
|
||||
head_name=$(cat "$GIT_DIR"/rebase/head-name) &&
|
||||
onto=$(cat "$GIT_DIR"/rebase/onto) &&
|
||||
orig_head=$(cat "$GIT_DIR"/rebase/orig-head) &&
|
||||
git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
|
||||
move_to_original_branch
|
||||
exit
|
||||
;;
|
||||
--skip)
|
||||
test -d "$dotest" -o -d .dotest ||
|
||||
test -d "$dotest" -o -d "$GIT_DIR"/rebase ||
|
||||
die "No rebase in progress?"
|
||||
|
||||
git reset --hard HEAD || exit $?
|
||||
@@ -201,15 +201,15 @@ do
|
||||
finish_rb_merge
|
||||
exit
|
||||
fi
|
||||
head_name=$(cat .dotest/head-name) &&
|
||||
onto=$(cat .dotest/onto) &&
|
||||
orig_head=$(cat .dotest/orig-head) &&
|
||||
head_name=$(cat "$GIT_DIR"/rebase/head-name) &&
|
||||
onto=$(cat "$GIT_DIR"/rebase/onto) &&
|
||||
orig_head=$(cat "$GIT_DIR"/rebase/orig-head) &&
|
||||
git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
|
||||
move_to_original_branch
|
||||
exit
|
||||
;;
|
||||
--abort)
|
||||
test -d "$dotest" -o -d .dotest ||
|
||||
test -d "$dotest" -o -d "$GIT_DIR"/rebase ||
|
||||
die "No rebase in progress?"
|
||||
|
||||
git rerere clear
|
||||
@@ -217,7 +217,7 @@ do
|
||||
then
|
||||
move_to_original_branch
|
||||
else
|
||||
dotest=.dotest
|
||||
dotest="$GIT_DIR"/rebase
|
||||
move_to_original_branch
|
||||
fi
|
||||
git reset --hard $(cat "$dotest/orig-head")
|
||||
@@ -265,24 +265,24 @@ do
|
||||
shift
|
||||
done
|
||||
|
||||
# Make sure we do not have .dotest
|
||||
# Make sure we do not have $GIT_DIR/rebase
|
||||
if test -z "$do_merge"
|
||||
then
|
||||
if mkdir .dotest
|
||||
if mkdir "$GIT_DIR"/rebase
|
||||
then
|
||||
rmdir .dotest
|
||||
rmdir "$GIT_DIR"/rebase
|
||||
else
|
||||
echo >&2 '
|
||||
It seems that I cannot create a .dotest directory, and I wonder if you
|
||||
It seems that I cannot create a '"$GIT_DIR"'/rebase directory, and I wonder if you
|
||||
are in the middle of patch application or another rebase. If that is not
|
||||
the case, please rm -fr .dotest and run me again. I am stopping in case
|
||||
the case, please rm -fr '"$GIT_DIR"'/rebase and run me again. I am stopping in case
|
||||
you still have something valuable there.'
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if test -d "$dotest"
|
||||
then
|
||||
die "previous dotest directory $dotest still exists." \
|
||||
die "previous rebase directory $dotest still exists." \
|
||||
'try git-rebase < --continue | --abort >'
|
||||
fi
|
||||
fi
|
||||
@@ -378,7 +378,7 @@ fi
|
||||
echo "First, rewinding head to replay your work on top of it..."
|
||||
git checkout "$onto^0" >/dev/null 2>&1 ||
|
||||
die "could not detach HEAD"
|
||||
# git reset --hard "$onto^0"
|
||||
git update-ref ORIG_HEAD $branch
|
||||
|
||||
# If the $onto is a proper descendant of the tip of the branch, then
|
||||
# we just fast forwarded.
|
||||
@@ -396,10 +396,10 @@ then
|
||||
git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
|
||||
move_to_original_branch
|
||||
ret=$?
|
||||
test 0 != $ret -a -d .dotest &&
|
||||
echo $head_name > .dotest/head-name &&
|
||||
echo $onto > .dotest/onto &&
|
||||
echo $orig_head > .dotest/orig-head
|
||||
test 0 != $ret -a -d "$GIT_DIR"/rebase &&
|
||||
echo $head_name > "$GIT_DIR"/rebase/head-name &&
|
||||
echo $onto > "$GIT_DIR"/rebase/onto &&
|
||||
echo $orig_head > "$GIT_DIR"/rebase/orig-head
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Copyright (c) 2007 Lars Hjemli
|
||||
|
||||
USAGE="[--quiet] [--cached] \
|
||||
[add <repo> [-b branch]|status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \
|
||||
[add <repo> [-b branch] <path>]|[status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \
|
||||
[--] [<path>...]"
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
@@ -27,18 +27,6 @@ say()
|
||||
fi
|
||||
}
|
||||
|
||||
# NEEDSWORK: identical function exists in get_repo_base in clone.sh
|
||||
get_repo_base() {
|
||||
(
|
||||
cd "`/bin/pwd`" &&
|
||||
cd "$1" || cd "$1.git" &&
|
||||
{
|
||||
cd .git
|
||||
pwd
|
||||
}
|
||||
) 2>/dev/null
|
||||
}
|
||||
|
||||
# Resolve relative url by appending to parent's url
|
||||
resolve_relative_url ()
|
||||
{
|
||||
@@ -115,7 +103,7 @@ module_clone()
|
||||
#
|
||||
# Add a new submodule to the working tree, .gitmodules and the index
|
||||
#
|
||||
# $@ = repo [path]
|
||||
# $@ = repo path
|
||||
#
|
||||
# optional branch is stored in global branch variable
|
||||
#
|
||||
@@ -150,16 +138,27 @@ cmd_add()
|
||||
repo=$1
|
||||
path=$2
|
||||
|
||||
if test -z "$repo"; then
|
||||
if test -z "$repo" -o -z "$path"; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# Guess path from repo if not specified or strip trailing slashes
|
||||
if test -z "$path"; then
|
||||
path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
|
||||
else
|
||||
path=$(echo "$path" | sed -e 's|/*$||')
|
||||
fi
|
||||
# assure repo is absolute or relative to parent
|
||||
case "$repo" in
|
||||
./*|../*)
|
||||
# dereference source url relative to parent's url
|
||||
realrepo=$(resolve_relative_url "$repo") || exit
|
||||
;;
|
||||
*:*|/*)
|
||||
# absolute url
|
||||
realrepo=$repo
|
||||
;;
|
||||
*)
|
||||
die "repo URL: '$repo' must be absolute or begin with ./|../"
|
||||
;;
|
||||
esac
|
||||
|
||||
# strip trailing slashes from path
|
||||
path=$(echo "$path" | sed -e 's|/*$||')
|
||||
|
||||
git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
|
||||
die "'$path' already exists in the index"
|
||||
@@ -173,21 +172,17 @@ cmd_add()
|
||||
else
|
||||
die "'$path' already exists and is not a valid git repo"
|
||||
fi
|
||||
else
|
||||
|
||||
case "$repo" in
|
||||
./*|../*)
|
||||
# dereference source url relative to parent's url
|
||||
realrepo=$(resolve_relative_url "$repo") || exit
|
||||
;;
|
||||
url=$(resolve_relative_url "$repo") || exit
|
||||
;;
|
||||
*)
|
||||
# Turn the source into an absolute path if
|
||||
# it is local
|
||||
if base=$(get_repo_base "$repo"); then
|
||||
repo="$base"
|
||||
fi
|
||||
realrepo=$repo
|
||||
url="$repo"
|
||||
;;
|
||||
esac
|
||||
git config submodule."$path".url "$url"
|
||||
else
|
||||
|
||||
module_clone "$path" "$realrepo" || exit
|
||||
(unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
|
||||
|
||||
41
git-svn.perl
41
git-svn.perl
@@ -537,13 +537,13 @@ sub cmd_find_rev {
|
||||
my $head = shift;
|
||||
$head ||= 'HEAD';
|
||||
my @refs;
|
||||
my (undef, undef, undef, $gs) = working_head_info($head, \@refs);
|
||||
my (undef, undef, $uuid, $gs) = working_head_info($head, \@refs);
|
||||
unless ($gs) {
|
||||
die "Unable to determine upstream SVN information from ",
|
||||
"$head history\n";
|
||||
}
|
||||
my $desired_revision = substr($revision_or_hash, 1);
|
||||
$result = $gs->rev_map_get($desired_revision);
|
||||
$result = $gs->rev_map_get($desired_revision, $uuid);
|
||||
} else {
|
||||
my (undef, $rev, undef) = cmt_metadata($revision_or_hash);
|
||||
$result = $rev;
|
||||
@@ -1162,7 +1162,7 @@ sub working_head_info {
|
||||
if (defined $url && defined $rev) {
|
||||
next if $max{$url} and $max{$url} < $rev;
|
||||
if (my $gs = Git::SVN->find_by_url($url)) {
|
||||
my $c = $gs->rev_map_get($rev);
|
||||
my $c = $gs->rev_map_get($rev, $uuid);
|
||||
if ($c && $c eq $hash) {
|
||||
close $fh; # break the pipe
|
||||
return ($url, $rev, $uuid, $gs);
|
||||
@@ -1416,11 +1416,17 @@ sub fetch_all {
|
||||
|
||||
sub read_all_remotes {
|
||||
my $r = {};
|
||||
my $use_svm_props = eval { command_oneline(qw/config --bool
|
||||
svn.useSvmProps/) };
|
||||
$use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
|
||||
foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
|
||||
if (m!^(.+)\.fetch=\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!) {
|
||||
my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
|
||||
$local_ref =~ s{^/}{};
|
||||
$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
|
||||
$r->{$remote}->{svm} = {} if $use_svm_props;
|
||||
} elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
|
||||
$r->{$1}->{svm} = {};
|
||||
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
|
||||
$r->{$1}->{url} = $2;
|
||||
} elsif (m!^(.+)\.(branches|tags)=
|
||||
@@ -1437,6 +1443,23 @@ sub read_all_remotes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map {
|
||||
if (defined $r->{$_}->{svm}) {
|
||||
my $svm;
|
||||
eval {
|
||||
my $section = "svn-remote.$_";
|
||||
$svm = {
|
||||
source => tmp_config('--get',
|
||||
"$section.svm-source"),
|
||||
replace => tmp_config('--get',
|
||||
"$section.svm-replace"),
|
||||
}
|
||||
};
|
||||
$r->{$_}->{svm} = $svm;
|
||||
}
|
||||
} keys %$r;
|
||||
|
||||
$r;
|
||||
}
|
||||
|
||||
@@ -1563,13 +1586,21 @@ sub find_by_url { # repos_root and, path are optional
|
||||
}
|
||||
my $p = $path;
|
||||
my $rwr = rewrite_root({repo_id => $repo_id});
|
||||
my $svm = $remotes->{$repo_id}->{svm}
|
||||
if defined $remotes->{$repo_id}->{svm};
|
||||
unless (defined $p) {
|
||||
$p = $full_url;
|
||||
my $z = $u;
|
||||
my $prefix = '';
|
||||
if ($rwr) {
|
||||
$z = $rwr;
|
||||
} elsif (defined $svm) {
|
||||
$z = $svm->{source};
|
||||
$prefix = $svm->{replace};
|
||||
$prefix =~ s#^\Q$u\E(?:/|$)##;
|
||||
$prefix =~ s#/$##;
|
||||
}
|
||||
$p =~ s#^\Q$z\E(?:/|$)## or next;
|
||||
$p =~ s#^\Q$z\E(?:/|$)#$prefix# or next;
|
||||
}
|
||||
foreach my $f (keys %$fetch) {
|
||||
next if $f ne $p;
|
||||
@@ -4619,7 +4650,7 @@ sub migrate_from_v1 {
|
||||
mkpath([$svn_dir]);
|
||||
print STDERR "Data from a previous version of git-svn exists, but\n\t",
|
||||
"$svn_dir\n\t(required for this version ",
|
||||
"($::VERSION) of git-svn) does not. exist\n";
|
||||
"($::VERSION) of git-svn) does not exist.\n";
|
||||
my ($fh, $ctx) = command_output_pipe(qw/rev-parse --symbolic --all/);
|
||||
while (<$fh>) {
|
||||
my $x = $_;
|
||||
|
||||
54
git.c
54
git.c
@@ -127,59 +127,6 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
|
||||
return handled;
|
||||
}
|
||||
|
||||
static int split_cmdline(char *cmdline, const char ***argv)
|
||||
{
|
||||
int src, dst, count = 0, size = 16;
|
||||
char quoted = 0;
|
||||
|
||||
*argv = xmalloc(sizeof(char*) * size);
|
||||
|
||||
/* split alias_string */
|
||||
(*argv)[count++] = cmdline;
|
||||
for (src = dst = 0; cmdline[src];) {
|
||||
char c = cmdline[src];
|
||||
if (!quoted && isspace(c)) {
|
||||
cmdline[dst++] = 0;
|
||||
while (cmdline[++src]
|
||||
&& isspace(cmdline[src]))
|
||||
; /* skip */
|
||||
if (count >= size) {
|
||||
size += 16;
|
||||
*argv = xrealloc(*argv, sizeof(char*) * size);
|
||||
}
|
||||
(*argv)[count++] = cmdline + dst;
|
||||
} else if(!quoted && (c == '\'' || c == '"')) {
|
||||
quoted = c;
|
||||
src++;
|
||||
} else if (c == quoted) {
|
||||
quoted = 0;
|
||||
src++;
|
||||
} else {
|
||||
if (c == '\\' && quoted != '\'') {
|
||||
src++;
|
||||
c = cmdline[src];
|
||||
if (!c) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("cmdline ends with \\");
|
||||
}
|
||||
}
|
||||
cmdline[dst++] = c;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
cmdline[dst] = 0;
|
||||
|
||||
if (quoted) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("unclosed quote");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int handle_alias(int *argcp, const char ***argv)
|
||||
{
|
||||
int envchanged = 0, ret = 0, saved_errno = errno;
|
||||
@@ -366,6 +313,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "ls-remote", cmd_ls_remote },
|
||||
{ "mailinfo", cmd_mailinfo },
|
||||
{ "mailsplit", cmd_mailsplit },
|
||||
{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-base", cmd_merge_base, RUN_SETUP },
|
||||
{ "merge-file", cmd_merge_file },
|
||||
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
|
||||
|
||||
@@ -5,23 +5,6 @@
|
||||
#define OPT_SHORT 1
|
||||
#define OPT_UNSET 2
|
||||
|
||||
static inline const char *get_arg(struct parse_opt_ctx_t *p)
|
||||
{
|
||||
if (p->opt) {
|
||||
const char *res = p->opt;
|
||||
p->opt = NULL;
|
||||
return res;
|
||||
}
|
||||
p->argc--;
|
||||
return *++p->argv;
|
||||
}
|
||||
|
||||
static inline const char *skip_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
static int opterror(const struct option *opt, const char *reason, int flags)
|
||||
{
|
||||
if (flags & OPT_SHORT)
|
||||
@@ -31,8 +14,24 @@ static int opterror(const struct option *opt, const char *reason, int flags)
|
||||
return error("option `%s' %s", opt->long_name, reason);
|
||||
}
|
||||
|
||||
static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
|
||||
int flags, const char **arg)
|
||||
{
|
||||
if (p->opt) {
|
||||
*arg = p->opt;
|
||||
p->opt = NULL;
|
||||
} else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) {
|
||||
*arg = (const char *)opt->defval;
|
||||
} else if (p->argc) {
|
||||
p->argc--;
|
||||
*arg = *++p->argv;
|
||||
} else
|
||||
return opterror(opt, "requires a value", flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_value(struct parse_opt_ctx_t *p,
|
||||
const struct option *opt, int flags)
|
||||
const struct option *opt, int flags)
|
||||
{
|
||||
const char *s, *arg;
|
||||
const int unset = flags & OPT_UNSET;
|
||||
@@ -58,7 +57,6 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
}
|
||||
}
|
||||
|
||||
arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
|
||||
switch (opt->type) {
|
||||
case OPTION_BIT:
|
||||
if (unset)
|
||||
@@ -80,17 +78,12 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
return 0;
|
||||
|
||||
case OPTION_STRING:
|
||||
if (unset) {
|
||||
if (unset)
|
||||
*(const char **)opt->value = NULL;
|
||||
return 0;
|
||||
}
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
|
||||
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
*(const char **)opt->value = (const char *)opt->defval;
|
||||
return 0;
|
||||
}
|
||||
if (!arg)
|
||||
return opterror(opt, "requires a value", flags);
|
||||
*(const char **)opt->value = get_arg(p);
|
||||
else
|
||||
return get_arg(p, opt, flags, (const char **)opt->value);
|
||||
return 0;
|
||||
|
||||
case OPTION_CALLBACK:
|
||||
@@ -100,9 +93,9 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (!arg)
|
||||
return opterror(opt, "requires a value", flags);
|
||||
return (*opt->callback)(opt, get_arg(p), 0) ? (-1) : 0;
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
|
||||
|
||||
case OPTION_INTEGER:
|
||||
if (unset) {
|
||||
@@ -113,9 +106,9 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
*(int *)opt->value = opt->defval;
|
||||
return 0;
|
||||
}
|
||||
if (!arg)
|
||||
return opterror(opt, "requires a value", flags);
|
||||
*(int *)opt->value = strtol(get_arg(p), (char **)&s, 10);
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
*(int *)opt->value = strtol(arg, (char **)&s, 10);
|
||||
if (*s)
|
||||
return opterror(opt, "expects a numerical value", flags);
|
||||
return 0;
|
||||
|
||||
@@ -28,6 +28,7 @@ enum parse_opt_option_flags {
|
||||
PARSE_OPT_NOARG = 2,
|
||||
PARSE_OPT_NONEG = 4,
|
||||
PARSE_OPT_HIDDEN = 8,
|
||||
PARSE_OPT_LASTARG_DEFAULT = 16,
|
||||
};
|
||||
|
||||
struct option;
|
||||
|
||||
27
pretty.c
27
pretty.c
@@ -3,6 +3,8 @@
|
||||
#include "utf8.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "path-list.h"
|
||||
#include "mailmap.h"
|
||||
|
||||
static char *user_format;
|
||||
|
||||
@@ -288,6 +290,25 @@ static char *logmsg_reencode(const struct commit *commit,
|
||||
return out;
|
||||
}
|
||||
|
||||
static int mailmap_name(struct strbuf *sb, const char *email)
|
||||
{
|
||||
static struct path_list *mail_map;
|
||||
char buffer[1024];
|
||||
|
||||
if (!mail_map) {
|
||||
mail_map = xcalloc(1, sizeof(*mail_map));
|
||||
read_mailmap(mail_map, ".mailmap", NULL);
|
||||
}
|
||||
|
||||
if (!mail_map->nr)
|
||||
return -1;
|
||||
|
||||
if (!map_email(mail_map, email, buffer, sizeof(buffer)))
|
||||
return -1;
|
||||
strbuf_addstr(sb, buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t format_person_part(struct strbuf *sb, char part,
|
||||
const char *msg, int len)
|
||||
{
|
||||
@@ -309,10 +330,12 @@ static size_t format_person_part(struct strbuf *sb, char part,
|
||||
if (end >= len - 2)
|
||||
goto skip;
|
||||
|
||||
if (part == 'n') { /* name */
|
||||
if (part == 'n' || part == 'N') { /* name */
|
||||
while (end > 0 && isspace(msg[end - 1]))
|
||||
end--;
|
||||
strbuf_add(sb, msg, end);
|
||||
if (part != 'N' || !msg[end] || !msg[end + 1] ||
|
||||
mailmap_name(sb, msg + end + 2) < 0)
|
||||
strbuf_add(sb, msg, end);
|
||||
return placeholder_len;
|
||||
}
|
||||
start = ++end; /* save email start position */
|
||||
|
||||
31
read-cache.c
31
read-cache.c
@@ -1410,3 +1410,34 @@ int write_index(const struct index_state *istate, int newfd)
|
||||
}
|
||||
return ce_flush(&c, newfd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the index file that is potentially unmerged into given
|
||||
* index_state, dropping any unmerged entries. Returns true is
|
||||
* the index is unmerged. Callers who want to refuse to work
|
||||
* from an unmerged state can call this and check its return value,
|
||||
* instead of calling read_cache().
|
||||
*/
|
||||
int read_index_unmerged(struct index_state *istate)
|
||||
{
|
||||
int i;
|
||||
struct cache_entry **dst;
|
||||
struct cache_entry *last = NULL;
|
||||
|
||||
read_index(istate);
|
||||
dst = istate->cache;
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
struct cache_entry *ce = istate->cache[i];
|
||||
if (ce_stage(ce)) {
|
||||
remove_name_hash(ce);
|
||||
if (last && !strcmp(ce->name, last->name))
|
||||
continue;
|
||||
cache_tree_invalidate_path(istate->cache_tree, ce->name);
|
||||
last = ce;
|
||||
continue;
|
||||
}
|
||||
*dst++ = ce;
|
||||
}
|
||||
istate->cache_nr = dst - istate->cache;
|
||||
return !!last;
|
||||
}
|
||||
|
||||
360
rerere.c
Normal file
360
rerere.c
Normal file
@@ -0,0 +1,360 @@
|
||||
#include "cache.h"
|
||||
#include "path-list.h"
|
||||
#include "rerere.h"
|
||||
#include "xdiff/xdiff.h"
|
||||
#include "xdiff-interface.h"
|
||||
|
||||
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
|
||||
static int rerere_enabled = -1;
|
||||
|
||||
/* automatically update cleanly resolved paths to the index */
|
||||
static int rerere_autoupdate;
|
||||
|
||||
static char *merge_rr_path;
|
||||
|
||||
static const char *rr_path(const char *name, const char *file)
|
||||
{
|
||||
return git_path("rr-cache/%s/%s", name, file);
|
||||
}
|
||||
|
||||
static int has_resolution(const char *name)
|
||||
{
|
||||
struct stat st;
|
||||
return !stat(rr_path(name, "postimage"), &st);
|
||||
}
|
||||
|
||||
static void read_rr(struct path_list *rr)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
char buf[PATH_MAX];
|
||||
FILE *in = fopen(merge_rr_path, "r");
|
||||
if (!in)
|
||||
return;
|
||||
while (fread(buf, 40, 1, in) == 1) {
|
||||
int i;
|
||||
char *name;
|
||||
if (get_sha1_hex(buf, sha1))
|
||||
die("corrupt MERGE_RR");
|
||||
buf[40] = '\0';
|
||||
name = xstrdup(buf);
|
||||
if (fgetc(in) != '\t')
|
||||
die("corrupt MERGE_RR");
|
||||
for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
|
||||
; /* do nothing */
|
||||
if (i == sizeof(buf))
|
||||
die("filename too long");
|
||||
path_list_insert(buf, rr)->util = name;
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
static struct lock_file write_lock;
|
||||
|
||||
static int write_rr(struct path_list *rr, int out_fd)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < rr->nr; i++) {
|
||||
const char *path;
|
||||
int length;
|
||||
if (!rr->items[i].util)
|
||||
continue;
|
||||
path = rr->items[i].path;
|
||||
length = strlen(path) + 1;
|
||||
if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
|
||||
write_in_full(out_fd, "\t", 1) != 1 ||
|
||||
write_in_full(out_fd, path, length) != length)
|
||||
die("unable to write rerere record");
|
||||
}
|
||||
if (commit_lock_file(&write_lock) != 0)
|
||||
die("unable to write rerere record");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_file(const char *path,
|
||||
unsigned char *sha1, const char *output)
|
||||
{
|
||||
SHA_CTX ctx;
|
||||
char buf[1024];
|
||||
int hunk = 0, hunk_no = 0;
|
||||
struct strbuf one, two;
|
||||
FILE *f = fopen(path, "r");
|
||||
FILE *out = NULL;
|
||||
|
||||
if (!f)
|
||||
return error("Could not open %s", path);
|
||||
|
||||
if (output) {
|
||||
out = fopen(output, "w");
|
||||
if (!out) {
|
||||
fclose(f);
|
||||
return error("Could not write %s", output);
|
||||
}
|
||||
}
|
||||
|
||||
if (sha1)
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
strbuf_init(&one, 0);
|
||||
strbuf_init(&two, 0);
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
if (!prefixcmp(buf, "<<<<<<< ")) {
|
||||
if (hunk)
|
||||
goto bad;
|
||||
hunk = 1;
|
||||
} else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
|
||||
if (hunk != 1)
|
||||
goto bad;
|
||||
hunk = 2;
|
||||
} else if (!prefixcmp(buf, ">>>>>>> ")) {
|
||||
if (hunk != 2)
|
||||
goto bad;
|
||||
if (strbuf_cmp(&one, &two) > 0)
|
||||
strbuf_swap(&one, &two);
|
||||
hunk_no++;
|
||||
hunk = 0;
|
||||
if (out) {
|
||||
fputs("<<<<<<<\n", out);
|
||||
fwrite(one.buf, one.len, 1, out);
|
||||
fputs("=======\n", out);
|
||||
fwrite(two.buf, two.len, 1, out);
|
||||
fputs(">>>>>>>\n", out);
|
||||
}
|
||||
if (sha1) {
|
||||
SHA1_Update(&ctx, one.buf ? one.buf : "",
|
||||
one.len + 1);
|
||||
SHA1_Update(&ctx, two.buf ? two.buf : "",
|
||||
two.len + 1);
|
||||
}
|
||||
strbuf_reset(&one);
|
||||
strbuf_reset(&two);
|
||||
} else if (hunk == 1)
|
||||
strbuf_addstr(&one, buf);
|
||||
else if (hunk == 2)
|
||||
strbuf_addstr(&two, buf);
|
||||
else if (out)
|
||||
fputs(buf, out);
|
||||
continue;
|
||||
bad:
|
||||
hunk = 99; /* force error exit */
|
||||
break;
|
||||
}
|
||||
strbuf_release(&one);
|
||||
strbuf_release(&two);
|
||||
|
||||
fclose(f);
|
||||
if (out)
|
||||
fclose(out);
|
||||
if (sha1)
|
||||
SHA1_Final(sha1, &ctx);
|
||||
if (hunk) {
|
||||
if (output)
|
||||
unlink(output);
|
||||
return error("Could not parse conflict hunks in %s", path);
|
||||
}
|
||||
return hunk_no;
|
||||
}
|
||||
|
||||
static int find_conflict(struct path_list *conflict)
|
||||
{
|
||||
int i;
|
||||
if (read_cache() < 0)
|
||||
return error("Could not read index");
|
||||
for (i = 0; i+1 < active_nr; i++) {
|
||||
struct cache_entry *e2 = active_cache[i];
|
||||
struct cache_entry *e3 = active_cache[i+1];
|
||||
if (ce_stage(e2) == 2 &&
|
||||
ce_stage(e3) == 3 &&
|
||||
ce_same_name(e2, e3) &&
|
||||
S_ISREG(e2->ce_mode) &&
|
||||
S_ISREG(e3->ce_mode)) {
|
||||
path_list_insert((const char *)e2->name, conflict);
|
||||
i++; /* skip over both #2 and #3 */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge(const char *name, const char *path)
|
||||
{
|
||||
int ret;
|
||||
mmfile_t cur, base, other;
|
||||
mmbuffer_t result = {NULL, 0};
|
||||
xpparam_t xpp = {XDF_NEED_MINIMAL};
|
||||
|
||||
if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
|
||||
return 1;
|
||||
|
||||
if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
|
||||
read_mmfile(&base, rr_path(name, "preimage")) ||
|
||||
read_mmfile(&other, rr_path(name, "postimage")))
|
||||
return 1;
|
||||
ret = xdl_merge(&base, &cur, "", &other, "",
|
||||
&xpp, XDL_MERGE_ZEALOUS, &result);
|
||||
if (!ret) {
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f)
|
||||
return error("Could not write to %s", path);
|
||||
fwrite(result.ptr, result.size, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
free(cur.ptr);
|
||||
free(base.ptr);
|
||||
free(other.ptr);
|
||||
free(result.ptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct lock_file index_lock;
|
||||
|
||||
static int update_paths(struct path_list *update)
|
||||
{
|
||||
int i;
|
||||
int fd = hold_locked_index(&index_lock, 0);
|
||||
int status = 0;
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < update->nr; i++) {
|
||||
struct path_list_item *item = &update->items[i];
|
||||
if (add_file_to_cache(item->path, ADD_CACHE_IGNORE_ERRORS))
|
||||
status = -1;
|
||||
}
|
||||
|
||||
if (!status && active_cache_changed) {
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
commit_locked_index(&index_lock))
|
||||
die("Unable to write new index file");
|
||||
} else if (fd >= 0)
|
||||
rollback_lock_file(&index_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int do_plain_rerere(struct path_list *rr, int fd)
|
||||
{
|
||||
struct path_list conflict = { NULL, 0, 0, 1 };
|
||||
struct path_list update = { NULL, 0, 0, 1 };
|
||||
int i;
|
||||
|
||||
find_conflict(&conflict);
|
||||
|
||||
/*
|
||||
* MERGE_RR records paths with conflicts immediately after merge
|
||||
* failed. Some of the conflicted paths might have been hand resolved
|
||||
* in the working tree since then, but the initial run would catch all
|
||||
* and register their preimages.
|
||||
*/
|
||||
|
||||
for (i = 0; i < conflict.nr; i++) {
|
||||
const char *path = conflict.items[i].path;
|
||||
if (!path_list_has_path(rr, path)) {
|
||||
unsigned char sha1[20];
|
||||
char *hex;
|
||||
int ret;
|
||||
ret = handle_file(path, sha1, NULL);
|
||||
if (ret < 1)
|
||||
continue;
|
||||
hex = xstrdup(sha1_to_hex(sha1));
|
||||
path_list_insert(path, rr)->util = hex;
|
||||
if (mkdir(git_path("rr-cache/%s", hex), 0755))
|
||||
continue;;
|
||||
handle_file(path, NULL, rr_path(hex, "preimage"));
|
||||
fprintf(stderr, "Recorded preimage for '%s'\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now some of the paths that had conflicts earlier might have been
|
||||
* hand resolved. Others may be similar to a conflict already that
|
||||
* was resolved before.
|
||||
*/
|
||||
|
||||
for (i = 0; i < rr->nr; i++) {
|
||||
int ret;
|
||||
const char *path = rr->items[i].path;
|
||||
const char *name = (const char *)rr->items[i].util;
|
||||
|
||||
if (has_resolution(name)) {
|
||||
if (!merge(name, path)) {
|
||||
fprintf(stderr, "Resolved '%s' using "
|
||||
"previous resolution.\n", path);
|
||||
if (rerere_autoupdate)
|
||||
path_list_insert(path, &update);
|
||||
goto mark_resolved;
|
||||
}
|
||||
}
|
||||
|
||||
/* Let's see if we have resolved it. */
|
||||
ret = handle_file(path, NULL, NULL);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "Recorded resolution for '%s'.\n", path);
|
||||
copy_file(rr_path(name, "postimage"), path, 0666);
|
||||
mark_resolved:
|
||||
rr->items[i].util = NULL;
|
||||
}
|
||||
|
||||
if (update.nr)
|
||||
update_paths(&update);
|
||||
|
||||
return write_rr(rr, fd);
|
||||
}
|
||||
|
||||
static int git_rerere_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "rerere.enabled"))
|
||||
rerere_enabled = git_config_bool(var, value);
|
||||
else if (!strcmp(var, "rerere.autoupdate"))
|
||||
rerere_autoupdate = git_config_bool(var, value);
|
||||
else
|
||||
return git_default_config(var, value, cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_rerere_enabled(void)
|
||||
{
|
||||
struct stat st;
|
||||
const char *rr_cache;
|
||||
int rr_cache_exists;
|
||||
|
||||
if (!rerere_enabled)
|
||||
return 0;
|
||||
|
||||
rr_cache = git_path("rr-cache");
|
||||
rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
|
||||
if (rerere_enabled < 0)
|
||||
return rr_cache_exists;
|
||||
|
||||
if (!rr_cache_exists &&
|
||||
(mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
|
||||
die("Could not create directory %s", rr_cache);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int setup_rerere(struct path_list *merge_rr)
|
||||
{
|
||||
int fd;
|
||||
|
||||
git_config(git_rerere_config, NULL);
|
||||
if (!is_rerere_enabled())
|
||||
return -1;
|
||||
|
||||
merge_rr_path = xstrdup(git_path("MERGE_RR"));
|
||||
fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
|
||||
read_rr(merge_rr);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int rerere(void)
|
||||
{
|
||||
struct path_list merge_rr = { NULL, 0, 0, 1 };
|
||||
int fd;
|
||||
|
||||
fd = setup_rerere(&merge_rr);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
return do_plain_rerere(&merge_rr, fd);
|
||||
}
|
||||
9
rerere.h
Normal file
9
rerere.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef RERERE_H
|
||||
#define RERERE_H
|
||||
|
||||
#include "path-list.h"
|
||||
|
||||
extern int setup_rerere(struct path_list *);
|
||||
extern int rerere(void);
|
||||
|
||||
#endif
|
||||
30
revision.c
30
revision.c
@@ -413,10 +413,26 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
|
||||
commit->object.flags |= TREESAME;
|
||||
}
|
||||
|
||||
static int add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)
|
||||
static void insert_by_date_cached(struct commit *p, struct commit_list **head,
|
||||
struct commit_list *cached_base, struct commit_list **cache)
|
||||
{
|
||||
struct commit_list *new_entry;
|
||||
|
||||
if (cached_base && p->date < cached_base->item->date)
|
||||
new_entry = insert_by_date(p, &cached_base->next);
|
||||
else
|
||||
new_entry = insert_by_date(p, head);
|
||||
|
||||
if (cache && (!*cache || p->date < (*cache)->item->date))
|
||||
*cache = new_entry;
|
||||
}
|
||||
|
||||
static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
|
||||
struct commit_list **list, struct commit_list **cache_ptr)
|
||||
{
|
||||
struct commit_list *parent = commit->parents;
|
||||
unsigned left_flag;
|
||||
struct commit_list *cached_base = cache_ptr ? *cache_ptr : NULL;
|
||||
|
||||
if (commit->object.flags & ADDED)
|
||||
return 0;
|
||||
@@ -446,7 +462,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
|
||||
if (p->object.flags & SEEN)
|
||||
continue;
|
||||
p->object.flags |= SEEN;
|
||||
insert_by_date(p, list);
|
||||
insert_by_date_cached(p, list, cached_base, cache_ptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -471,7 +487,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
|
||||
p->object.flags |= left_flag;
|
||||
if (!(p->object.flags & SEEN)) {
|
||||
p->object.flags |= SEEN;
|
||||
insert_by_date(p, list);
|
||||
insert_by_date_cached(p, list, cached_base, cache_ptr);
|
||||
}
|
||||
if(revs->first_parent_only)
|
||||
break;
|
||||
@@ -612,7 +628,7 @@ static int limit_list(struct rev_info *revs)
|
||||
|
||||
if (revs->max_age != -1 && (commit->date < revs->max_age))
|
||||
obj->flags |= UNINTERESTING;
|
||||
if (add_parents_to_list(revs, commit, &list) < 0)
|
||||
if (add_parents_to_list(revs, commit, &list, NULL) < 0)
|
||||
return -1;
|
||||
if (obj->flags & UNINTERESTING) {
|
||||
mark_parents_uninteresting(commit);
|
||||
@@ -1415,10 +1431,12 @@ enum rewrite_result {
|
||||
|
||||
static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
|
||||
{
|
||||
struct commit_list *cache = NULL;
|
||||
|
||||
for (;;) {
|
||||
struct commit *p = *pp;
|
||||
if (!revs->limited)
|
||||
if (add_parents_to_list(revs, p, &revs->commits) < 0)
|
||||
if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
|
||||
return rewrite_one_error;
|
||||
if (p->parents && p->parents->next)
|
||||
return rewrite_one_ok;
|
||||
@@ -1542,7 +1560,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
|
||||
if (revs->max_age != -1 &&
|
||||
(commit->date < revs->max_age))
|
||||
continue;
|
||||
if (add_parents_to_list(revs, commit, &revs->commits) < 0)
|
||||
if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
30
sha1_file.c
30
sha1_file.c
@@ -1006,6 +1006,18 @@ static void mark_bad_packed_object(struct packed_git *p,
|
||||
p->num_bad_objects++;
|
||||
}
|
||||
|
||||
static int has_packed_and_bad(const unsigned char *sha1)
|
||||
{
|
||||
struct packed_git *p;
|
||||
unsigned i;
|
||||
|
||||
for (p = packed_git; p; p = p->next)
|
||||
for (i = 0; i < p->num_bad_objects; i++)
|
||||
if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
|
||||
{
|
||||
unsigned char real_sha1[20];
|
||||
@@ -1647,7 +1659,7 @@ static void *unpack_delta_entry(struct packed_git *p,
|
||||
sha1_to_hex(base_sha1), (uintmax_t)base_offset,
|
||||
p->pack_name);
|
||||
mark_bad_packed_object(p, base_sha1);
|
||||
base = read_sha1_file(base_sha1, type, &base_size);
|
||||
base = read_object(base_sha1, type, &base_size);
|
||||
if (!base)
|
||||
return NULL;
|
||||
}
|
||||
@@ -1945,7 +1957,7 @@ static void *read_packed_sha1(const unsigned char *sha1,
|
||||
error("failed to read object %s at offset %"PRIuMAX" from %s",
|
||||
sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name);
|
||||
mark_bad_packed_object(e.p, sha1);
|
||||
data = read_sha1_file(sha1, type, size);
|
||||
data = read_object(sha1, type, size);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -2010,8 +2022,8 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
|
||||
unsigned long *size)
|
||||
void *read_object(const unsigned char *sha1, enum object_type *type,
|
||||
unsigned long *size)
|
||||
{
|
||||
unsigned long mapsize;
|
||||
void *map, *buf;
|
||||
@@ -2037,6 +2049,16 @@ void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
|
||||
return read_packed_sha1(sha1, type, size);
|
||||
}
|
||||
|
||||
void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
|
||||
unsigned long *size)
|
||||
{
|
||||
void *data = read_object(sha1, type, size);
|
||||
/* legacy behavior is to die on corrupted objects */
|
||||
if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1)))
|
||||
die("object %s is corrupted", sha1_to_hex(sha1));
|
||||
return data;
|
||||
}
|
||||
|
||||
void *read_object_with_reference(const unsigned char *sha1,
|
||||
const char *required_type_name,
|
||||
unsigned long *size,
|
||||
|
||||
@@ -11,6 +11,7 @@ struct shortlog {
|
||||
int wrap;
|
||||
int in1;
|
||||
int in2;
|
||||
int user_format;
|
||||
|
||||
char *common_repo_prefix;
|
||||
int email;
|
||||
|
||||
@@ -50,12 +50,12 @@ test_debug \
|
||||
|
||||
test_expect_success \
|
||||
'rebase topic branch against new master and check git-am did not get halted' \
|
||||
'git-rebase master && test ! -d .dotest'
|
||||
'git-rebase master && test ! -d .git/rebase'
|
||||
|
||||
test_expect_success \
|
||||
'rebase --merge topic branch that was partially merged upstream' \
|
||||
'git-checkout -f my-topic-branch-merge &&
|
||||
git-rebase --merge master-merge &&
|
||||
test ! -d .git/.dotest-merge'
|
||||
test ! -d .git/rebase-merge'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -159,19 +159,19 @@ test_expect_success 'stop on conflicting pick' '
|
||||
git tag new-branch1 &&
|
||||
test_must_fail git rebase -i master &&
|
||||
test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
|
||||
test_cmp expect .git/.dotest-merge/patch &&
|
||||
test_cmp expect .git/rebase-merge/patch &&
|
||||
test_cmp expect2 file1 &&
|
||||
test "$(git-diff --name-status |
|
||||
sed -n -e "/^U/s/^U[^a-z]*//p")" = file1 &&
|
||||
test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
|
||||
test 0 = $(grep -c "^[^#]" < .git/.dotest-merge/git-rebase-todo)
|
||||
test 4 = $(grep -v "^#" < .git/rebase-merge/done | wc -l) &&
|
||||
test 0 = $(grep -c "^[^#]" < .git/rebase-merge/git-rebase-todo)
|
||||
'
|
||||
|
||||
test_expect_success 'abort' '
|
||||
git rebase --abort &&
|
||||
test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
|
||||
test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
|
||||
! test -d .git/.dotest-merge
|
||||
! test -d .git/rebase-merge
|
||||
'
|
||||
|
||||
test_expect_success 'retain authorship' '
|
||||
|
||||
@@ -74,7 +74,7 @@ testrebase() {
|
||||
'
|
||||
}
|
||||
|
||||
testrebase "" .dotest
|
||||
testrebase " --merge" .git/.dotest-merge
|
||||
testrebase "" .git/rebase
|
||||
testrebase " --merge" .git/rebase-merge
|
||||
|
||||
test_done
|
||||
|
||||
@@ -9,15 +9,15 @@ test_description='git apply test patches with multiple fragments.
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
cmp () {
|
||||
diff -w "$@"
|
||||
}
|
||||
|
||||
cp ../t4109/patch1.patch .
|
||||
cp ../t4109/patch2.patch .
|
||||
cp ../t4109/patch3.patch .
|
||||
cp ../t4109/patch4.patch .
|
||||
|
||||
cmp () {
|
||||
diff -w "$@"
|
||||
}
|
||||
|
||||
test_expect_success "S = git apply (1)" \
|
||||
'git apply patch1.patch patch2.patch'
|
||||
mv main.c main.c.git
|
||||
|
||||
@@ -102,7 +102,7 @@ test_expect_success 'am applies patch correctly' '
|
||||
git checkout first &&
|
||||
test_tick &&
|
||||
git am <patch1 &&
|
||||
! test -d .dotest &&
|
||||
! test -d .git/rebase &&
|
||||
test -z "$(git diff second)" &&
|
||||
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
|
||||
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
|
||||
@@ -123,7 +123,7 @@ test_expect_success 'am changes committer and keeps author' '
|
||||
test_tick &&
|
||||
git checkout first &&
|
||||
git am patch2 &&
|
||||
! test -d .dotest &&
|
||||
! test -d .git/rebase &&
|
||||
test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
|
||||
test -z "$(git diff master..HEAD)" &&
|
||||
test -z "$(git diff master^..HEAD^)" &&
|
||||
@@ -163,7 +163,7 @@ test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
|
||||
test_expect_success 'am --keep really keeps the subject' '
|
||||
git checkout HEAD^ &&
|
||||
git am --keep patch4 &&
|
||||
! test -d .dotest &&
|
||||
! test -d .git/rebase &&
|
||||
git-cat-file commit HEAD |
|
||||
grep -q -F "Re: Re: Re: [PATCH 1/5 v2] third"
|
||||
'
|
||||
@@ -176,19 +176,19 @@ test_expect_success 'am -3 falls back to 3-way merge' '
|
||||
test_tick &&
|
||||
git commit -m "copied stuff" &&
|
||||
git am -3 lorem-move.patch &&
|
||||
! test -d .dotest &&
|
||||
! test -d .git/rebase &&
|
||||
test -z "$(git diff lorem)"
|
||||
'
|
||||
|
||||
test_expect_success 'am pauses on conflict' '
|
||||
git checkout lorem2^^ &&
|
||||
! git am lorem-move.patch &&
|
||||
test -d .dotest
|
||||
test -d .git/rebase
|
||||
'
|
||||
|
||||
test_expect_success 'am --skip works' '
|
||||
git am --skip &&
|
||||
! test -d .dotest &&
|
||||
! test -d .git/rebase &&
|
||||
test -z "$(git diff lorem2^^ -- file)" &&
|
||||
test goodbye = "$(cat another)"
|
||||
'
|
||||
@@ -196,31 +196,31 @@ test_expect_success 'am --skip works' '
|
||||
test_expect_success 'am --resolved works' '
|
||||
git checkout lorem2^^ &&
|
||||
! git am lorem-move.patch &&
|
||||
test -d .dotest &&
|
||||
test -d .git/rebase &&
|
||||
echo resolved >>file &&
|
||||
git add file &&
|
||||
git am --resolved &&
|
||||
! test -d .dotest &&
|
||||
! test -d .git/rebase &&
|
||||
test goodbye = "$(cat another)"
|
||||
'
|
||||
|
||||
test_expect_success 'am takes patches from a Pine mailbox' '
|
||||
git checkout first &&
|
||||
cat pine patch1 | git am &&
|
||||
! test -d .dotest &&
|
||||
! test -d .git/rebase &&
|
||||
test -z "$(git diff master^..HEAD)"
|
||||
'
|
||||
|
||||
test_expect_success 'am fails on mail without patch' '
|
||||
! git am <failmail &&
|
||||
rm -r .dotest/
|
||||
rm -r .git/rebase/
|
||||
'
|
||||
|
||||
test_expect_success 'am fails on empty patch' '
|
||||
echo "---" >>failmail &&
|
||||
! git am <failmail &&
|
||||
git am --skip &&
|
||||
! test -d .dotest
|
||||
! test -d .git/rebase
|
||||
'
|
||||
|
||||
test_expect_success 'am works from stdin in subdirectory' '
|
||||
|
||||
@@ -9,6 +9,8 @@ test_description='git rerere
|
||||
. ./test-lib.sh
|
||||
|
||||
cat > a1 << EOF
|
||||
Some title
|
||||
==========
|
||||
Whether 'tis nobler in the mind to suffer
|
||||
The slings and arrows of outrageous fortune,
|
||||
Or to take arms against a sea of troubles,
|
||||
@@ -24,6 +26,8 @@ git commit -q -a -m initial
|
||||
|
||||
git checkout -b first
|
||||
cat >> a1 << EOF
|
||||
Some title
|
||||
==========
|
||||
To die, to sleep;
|
||||
To sleep: perchance to dream: ay, there's the rub;
|
||||
For in that sleep of death what dreams may come
|
||||
@@ -35,7 +39,7 @@ git commit -q -a -m first
|
||||
|
||||
git checkout -b second master
|
||||
git show first:a1 |
|
||||
sed -e 's/To die, t/To die! T/' > a1
|
||||
sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1
|
||||
echo "* END *" >>a1
|
||||
git commit -q -a -m second
|
||||
|
||||
@@ -53,16 +57,16 @@ test_expect_success 'conflicting merge' '
|
||||
! git merge first
|
||||
'
|
||||
|
||||
sha1=$(sed -e 's/ .*//' .git/rr-cache/MERGE_RR)
|
||||
sha1=$(sed -e 's/ .*//' .git/MERGE_RR)
|
||||
rr=.git/rr-cache/$sha1
|
||||
test_expect_success 'recorded preimage' "grep ======= $rr/preimage"
|
||||
test_expect_success 'recorded preimage' "grep ^=======$ $rr/preimage"
|
||||
|
||||
test_expect_success 'rerere.enabled works, too' '
|
||||
rm -rf .git/rr-cache &&
|
||||
git config rerere.enabled true &&
|
||||
git reset --hard &&
|
||||
! git merge first &&
|
||||
grep ======= $rr/preimage
|
||||
grep ^=======$ $rr/preimage
|
||||
'
|
||||
|
||||
test_expect_success 'no postimage or thisimage yet' \
|
||||
@@ -71,7 +75,7 @@ test_expect_success 'no postimage or thisimage yet' \
|
||||
test_expect_success 'preimage has right number of lines' '
|
||||
|
||||
cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
|
||||
test $cnt = 9
|
||||
test $cnt = 13
|
||||
|
||||
'
|
||||
|
||||
@@ -80,13 +84,23 @@ git show first:a1 > a1
|
||||
cat > expect << EOF
|
||||
--- a/a1
|
||||
+++ b/a1
|
||||
@@ -6,17 +6,9 @@
|
||||
@@ -1,4 +1,4 @@
|
||||
-Some Title
|
||||
+Some title
|
||||
==========
|
||||
Whether 'tis nobler in the mind to suffer
|
||||
The slings and arrows of outrageous fortune,
|
||||
@@ -8,21 +8,11 @@
|
||||
The heart-ache and the thousand natural shocks
|
||||
That flesh is heir to, 'tis a consummation
|
||||
Devoutly to be wish'd.
|
||||
-<<<<<<<
|
||||
-Some Title
|
||||
-==========
|
||||
-To die! To sleep;
|
||||
-=======
|
||||
Some title
|
||||
==========
|
||||
To die, to sleep;
|
||||
->>>>>>>
|
||||
To sleep: perchance to dream: ay, there's the rub;
|
||||
@@ -124,12 +138,12 @@ test_expect_success 'another conflicting merge' '
|
||||
'
|
||||
|
||||
git show first:a1 | sed 's/To die: t/To die! T/' > expect
|
||||
test_expect_success 'rerere kicked in' "! grep ======= a1"
|
||||
test_expect_success 'rerere kicked in' "! grep ^=======$ a1"
|
||||
|
||||
test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'
|
||||
|
||||
rm $rr/postimage
|
||||
echo "$sha1 a1" | perl -pe 'y/\012/\000/' > .git/rr-cache/MERGE_RR
|
||||
echo "$sha1 a1" | perl -pe 'y/\012/\000/' > .git/MERGE_RR
|
||||
|
||||
test_expect_success 'rerere clear' 'git rerere clear'
|
||||
|
||||
@@ -176,7 +190,7 @@ test_expect_success 'file2 added differently in two branches' '
|
||||
git add file2 &&
|
||||
git commit -m version2 &&
|
||||
! git merge fourth &&
|
||||
sha1=$(sed -e "s/ .*//" .git/rr-cache/MERGE_RR) &&
|
||||
sha1=$(sed -e "s/ .*//" .git/MERGE_RR) &&
|
||||
rr=.git/rr-cache/$sha1 &&
|
||||
echo Cello > file2 &&
|
||||
git add file2 &&
|
||||
|
||||
@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \
|
||||
'git mailsplit -o. ../t5100/sample.mbox >last &&
|
||||
last=`cat last` &&
|
||||
echo total is $last &&
|
||||
test `cat last` = 9'
|
||||
test `cat last` = 10'
|
||||
|
||||
for mail in `echo 00*`
|
||||
do
|
||||
|
||||
35
t/t5100/0010
Normal file
35
t/t5100/0010
Normal file
@@ -0,0 +1,35 @@
|
||||
From b9704a518e21158433baa2cc2d591fea687967f6 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= <lukass@etek.chalmers.se>
|
||||
Date: Thu, 10 Jul 2008 23:41:33 +0200
|
||||
Subject: Re: discussion that lead to this patch
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
[PATCH] git-mailinfo: Fix getting the subject from the body
|
||||
|
||||
"Subject: " isn't in the static array "header", and thus
|
||||
memcmp("Subject: ", header[i], 7) will never match.
|
||||
|
||||
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
|
||||
Signed-off-by: Junio C Hamano <gitster@pobox.com>
|
||||
---
|
||||
builtin-mailinfo.c | 2 +-
|
||||
1 files changed, 1 insertions(+), 1 deletions(-)
|
||||
|
||||
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
|
||||
index 962aa34..2d1520f 100644
|
||||
--- a/builtin-mailinfo.c
|
||||
+++ b/builtin-mailinfo.c
|
||||
@@ -334,7 +334,7 @@ static int check_header(char *line, unsigned linesize, char **hdr_data, int over
|
||||
return 1;
|
||||
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
|
||||
for (i = 0; header[i]; i++) {
|
||||
- if (!memcmp("Subject: ", header[i], 9)) {
|
||||
+ if (!memcmp("Subject", header[i], 7)) {
|
||||
if (! handle_header(line, hdr_data[i], 0)) {
|
||||
return 1;
|
||||
}
|
||||
--
|
||||
1.5.6.2.455.g1efb2
|
||||
|
||||
5
t/t5100/info0010
Normal file
5
t/t5100/info0010
Normal file
@@ -0,0 +1,5 @@
|
||||
Author: Lukas Sandström
|
||||
Email: lukass@etek.chalmers.se
|
||||
Subject: git-mailinfo: Fix getting the subject from the body
|
||||
Date: Thu, 10 Jul 2008 23:41:33 +0200
|
||||
|
||||
5
t/t5100/msg0010
Normal file
5
t/t5100/msg0010
Normal file
@@ -0,0 +1,5 @@
|
||||
"Subject: " isn't in the static array "header", and thus
|
||||
memcmp("Subject: ", header[i], 7) will never match.
|
||||
|
||||
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
|
||||
Signed-off-by: Junio C Hamano <gitster@pobox.com>
|
||||
20
t/t5100/patch0010
Normal file
20
t/t5100/patch0010
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
builtin-mailinfo.c | 2 +-
|
||||
1 files changed, 1 insertions(+), 1 deletions(-)
|
||||
|
||||
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
|
||||
index 962aa34..2d1520f 100644
|
||||
--- a/builtin-mailinfo.c
|
||||
+++ b/builtin-mailinfo.c
|
||||
@@ -334,7 +334,7 @@ static int check_header(char *line, unsigned linesize, char **hdr_data, int over
|
||||
return 1;
|
||||
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
|
||||
for (i = 0; header[i]; i++) {
|
||||
- if (!memcmp("Subject: ", header[i], 9)) {
|
||||
+ if (!memcmp("Subject", header[i], 7)) {
|
||||
if (! handle_header(line, hdr_data[i], 0)) {
|
||||
return 1;
|
||||
}
|
||||
--
|
||||
1.5.6.2.455.g1efb2
|
||||
|
||||
@@ -430,3 +430,38 @@ index b426a14..97756ec 100644
|
||||
=20
|
||||
=20
|
||||
2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
|
||||
From b9704a518e21158433baa2cc2d591fea687967f6 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= <lukass@etek.chalmers.se>
|
||||
Date: Thu, 10 Jul 2008 23:41:33 +0200
|
||||
Subject: Re: discussion that lead to this patch
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
[PATCH] git-mailinfo: Fix getting the subject from the body
|
||||
|
||||
"Subject: " isn't in the static array "header", and thus
|
||||
memcmp("Subject: ", header[i], 7) will never match.
|
||||
|
||||
Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
|
||||
Signed-off-by: Junio C Hamano <gitster@pobox.com>
|
||||
---
|
||||
builtin-mailinfo.c | 2 +-
|
||||
1 files changed, 1 insertions(+), 1 deletions(-)
|
||||
|
||||
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
|
||||
index 962aa34..2d1520f 100644
|
||||
--- a/builtin-mailinfo.c
|
||||
+++ b/builtin-mailinfo.c
|
||||
@@ -334,7 +334,7 @@ static int check_header(char *line, unsigned linesize, char **hdr_data, int over
|
||||
return 1;
|
||||
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
|
||||
for (i = 0; header[i]; i++) {
|
||||
- if (!memcmp("Subject: ", header[i], 9)) {
|
||||
+ if (!memcmp("Subject", header[i], 7)) {
|
||||
if (! handle_header(line, hdr_data[i], 0)) {
|
||||
return 1;
|
||||
}
|
||||
--
|
||||
1.5.6.2.455.g1efb2
|
||||
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
test_description='pack index with 64-bit offsets and object CRC'
|
||||
. ./test-lib.sh
|
||||
|
||||
zeros () {
|
||||
while :; do
|
||||
echo -n xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
done | tr x '\0'
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'setup' \
|
||||
'rm -rf .git
|
||||
@@ -168,7 +174,7 @@ test_expect_success \
|
||||
git-index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
|
||||
git verify-pack ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
chmod +w ".git/objects/pack/pack-${pack1}.idx" &&
|
||||
dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
|
||||
zeros | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
|
||||
bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + 0)) &&
|
||||
( while read obj
|
||||
do git cat-file -p $obj >/dev/null || exit 1
|
||||
|
||||
@@ -41,11 +41,17 @@ create_new_pack() {
|
||||
git verify-pack -v ${pack}.pack
|
||||
}
|
||||
|
||||
zeros () {
|
||||
while :; do
|
||||
echo -n xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
done | tr x '\0'
|
||||
}
|
||||
|
||||
do_corrupt_object() {
|
||||
ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
|
||||
ofs=$(($ofs + $2)) &&
|
||||
chmod +w ${pack}.pack &&
|
||||
dd if=/dev/zero of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
|
||||
zeros | dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
|
||||
test_must_fail git verify-pack ${pack}.pack
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ test_expect_success 'setup' '
|
||||
|
||||
: > super-file &&
|
||||
git add super-file &&
|
||||
git submodule add . sub &&
|
||||
git submodule add "$(pwd)" sub &&
|
||||
git symbolic-ref HEAD refs/heads/super &&
|
||||
test_tick &&
|
||||
git commit -m super-initial &&
|
||||
|
||||
60
t/t6011-rev-list-with-bad-commit.sh
Executable file
60
t/t6011-rev-list-with-bad-commit.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git rev-list should notice bad commits'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
# Note:
|
||||
# - compression level is set to zero to make "corruptions" easier to perform
|
||||
# - reflog is disabled to avoid extra references which would twart the test
|
||||
|
||||
test_expect_success 'setup' \
|
||||
'
|
||||
git init &&
|
||||
git config core.compression 0 &&
|
||||
git config core.logallrefupdates false &&
|
||||
echo "foo" > foo &&
|
||||
git add foo &&
|
||||
git commit -m "first commit" &&
|
||||
echo "bar" > bar &&
|
||||
git add bar &&
|
||||
git commit -m "second commit" &&
|
||||
echo "baz" > baz &&
|
||||
git add baz &&
|
||||
git commit -m "third commit" &&
|
||||
echo "foo again" >> foo &&
|
||||
git add foo &&
|
||||
git commit -m "fourth commit" &&
|
||||
git repack -a -f -d
|
||||
'
|
||||
|
||||
test_expect_success 'verify number of revisions' \
|
||||
'
|
||||
revs=$(git rev-list --all | wc -l) &&
|
||||
test $revs -eq 4 &&
|
||||
first_commit=$(git rev-parse HEAD~3)
|
||||
'
|
||||
|
||||
test_expect_success 'corrupt second commit object' \
|
||||
'
|
||||
perl -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack &&
|
||||
test_must_fail git fsck --full
|
||||
'
|
||||
|
||||
test_expect_success 'rev-list should fail' \
|
||||
'
|
||||
test_must_fail git rev-list --all > /dev/null
|
||||
'
|
||||
|
||||
test_expect_success 'git repack _MUST_ fail' \
|
||||
'
|
||||
test_must_fail git repack -a -f -d
|
||||
'
|
||||
|
||||
test_expect_success 'first commit is still available' \
|
||||
'
|
||||
git log $first_commit
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -89,4 +89,8 @@ EOF
|
||||
|
||||
test_expect_success 'Criss-cross merge result' 'cmp file file-expect'
|
||||
|
||||
test_expect_success 'Criss-cross merge fails (-s resolve)' \
|
||||
'git reset --hard A^ &&
|
||||
test_must_fail git merge -s resolve -m "final merge" B'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -465,11 +465,51 @@ test_expect_success 'merge log message' '
|
||||
git merge --no-log c2 &&
|
||||
git show -s --pretty=format:%b HEAD >msg.act &&
|
||||
verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
|
||||
|
||||
git merge --log c3 &&
|
||||
git show -s --pretty=format:%b HEAD >msg.act &&
|
||||
verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
|
||||
|
||||
git reset --hard HEAD^ &&
|
||||
git config merge.log yes &&
|
||||
git merge c3 &&
|
||||
git show -s --pretty=format:%b HEAD >msg.act &&
|
||||
verify_diff msg.log msg.act "[OOPS] bad merge log message"
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
|
||||
git reset --hard c1 &&
|
||||
git config branch.master.mergeoptions "" &&
|
||||
test_tick &&
|
||||
git merge c0 c2 c0 c1 &&
|
||||
verify_merge file result.1-5 &&
|
||||
verify_parents $c1 $c2
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
|
||||
git reset --hard c1 &&
|
||||
git config branch.master.mergeoptions "" &&
|
||||
test_tick &&
|
||||
git merge c0 c2 c0 c1 &&
|
||||
verify_merge file result.1-5 &&
|
||||
verify_parents $c1 $c2
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_expect_success 'merge c1 with c1 and c2' '
|
||||
git reset --hard c1 &&
|
||||
git config branch.master.mergeoptions "" &&
|
||||
test_tick &&
|
||||
git merge c1 c2 &&
|
||||
verify_merge file result.1-5 &&
|
||||
verify_parents $c1 $c2
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_done
|
||||
|
||||
129
t/t7601-merge-pull-config.sh
Executable file
129
t/t7601-merge-pull-config.sh
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing pull.* configuration parsing.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 >c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 >c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 >c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2 &&
|
||||
git reset --hard c0 &&
|
||||
echo c3 >c3.c &&
|
||||
git add c3.c &&
|
||||
git commit -m c3 &&
|
||||
git tag c3
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2' '
|
||||
git reset --hard c1 &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test ! -f c2.c &&
|
||||
test ! -f c3.c &&
|
||||
git merge c2 &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
|
||||
git reset --hard c1 &&
|
||||
git config pull.twohead ours &&
|
||||
git merge c2 &&
|
||||
test -f c1.c &&
|
||||
! test -f c2.c
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '
|
||||
git reset --hard c1 &&
|
||||
git config pull.octopus "recursive" &&
|
||||
test_must_fail git merge c2 c3 &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD)"
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octopus)' '
|
||||
git reset --hard c1 &&
|
||||
git config pull.octopus "recursive octopus" &&
|
||||
git merge c2 c3 &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
|
||||
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
|
||||
test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
|
||||
git diff --exit-code &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c &&
|
||||
test -f c3.c
|
||||
'
|
||||
|
||||
conflict_count()
|
||||
{
|
||||
{
|
||||
git diff-files --name-only
|
||||
git ls-files --unmerged
|
||||
} | wc -l
|
||||
}
|
||||
|
||||
# c4 - c5
|
||||
# \ c6
|
||||
#
|
||||
# There are two conflicts here:
|
||||
#
|
||||
# 1) Because foo.c is renamed to bar.c, recursive will handle this,
|
||||
# resolve won't.
|
||||
#
|
||||
# 2) One in conflict.c and that will always fail.
|
||||
|
||||
test_expect_success 'setup conflicted merge' '
|
||||
git reset --hard c0 &&
|
||||
echo A >conflict.c &&
|
||||
git add conflict.c &&
|
||||
echo contents >foo.c &&
|
||||
git add foo.c &&
|
||||
git commit -m c4 &&
|
||||
git tag c4 &&
|
||||
echo B >conflict.c &&
|
||||
git add conflict.c &&
|
||||
git mv foo.c bar.c &&
|
||||
git commit -m c5 &&
|
||||
git tag c5 &&
|
||||
git reset --hard c4 &&
|
||||
echo C >conflict.c &&
|
||||
git add conflict.c &&
|
||||
echo secondline >> foo.c &&
|
||||
git add foo.c &&
|
||||
git commit -m c6 &&
|
||||
git tag c6
|
||||
'
|
||||
|
||||
# First do the merge with resolve and recursive then verify that
|
||||
# recusive is choosen.
|
||||
|
||||
test_expect_success 'merge picks up the best result' '
|
||||
git config pull.twohead "recursive resolve" &&
|
||||
git reset --hard c5 &&
|
||||
git merge -s resolve c6
|
||||
resolve_count=$(conflict_count) &&
|
||||
git reset --hard c5 &&
|
||||
git merge -s recursive c6
|
||||
recursive_count=$(conflict_count) &&
|
||||
git reset --hard c5 &&
|
||||
git merge c6
|
||||
auto_count=$(conflict_count) &&
|
||||
test $auto_count = $recursive_count &&
|
||||
test $auto_count != $resolve_count
|
||||
'
|
||||
|
||||
test_done
|
||||
52
t/t7602-merge-octopus-many.sh
Executable file
52
t/t7602-merge-octopus-many.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing octopus merge with more than 25 refs.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
i=1 &&
|
||||
while test $i -le 30
|
||||
do
|
||||
git reset --hard c0 &&
|
||||
echo c$i > c$i.c &&
|
||||
git add c$i.c &&
|
||||
git commit -m c$i &&
|
||||
git tag c$i &&
|
||||
i=`expr $i + 1` || return 1
|
||||
done
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
|
||||
git reset --hard c1 &&
|
||||
i=2 &&
|
||||
refs="" &&
|
||||
while test $i -le 30
|
||||
do
|
||||
refs="$refs c$i"
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
git merge $refs &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
i=1 &&
|
||||
while test $i -le 30
|
||||
do
|
||||
test "$(git rev-parse c$i)" = "$(git rev-parse HEAD^$i)" &&
|
||||
i=`expr $i + 1` || return 1
|
||||
done &&
|
||||
git diff --exit-code &&
|
||||
i=1 &&
|
||||
while test $i -le 30
|
||||
do
|
||||
test -f c$i.c &&
|
||||
i=`expr $i + 1` || return 1
|
||||
done
|
||||
'
|
||||
|
||||
test_done
|
||||
63
t/t7603-merge-reduce-heads.sh
Executable file
63
t/t7603-merge-reduce-heads.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing octopus merge when reducing parents to independent branches.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
# 0 - 1
|
||||
# \ 2
|
||||
# \ 3
|
||||
# \ 4 - 5
|
||||
#
|
||||
# So 1, 2, 3 and 5 should be kept, 4 should be avoided.
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 > c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2 &&
|
||||
git reset --hard c0 &&
|
||||
echo c3 > c3.c &&
|
||||
git add c3.c &&
|
||||
git commit -m c3 &&
|
||||
git tag c3 &&
|
||||
git reset --hard c0 &&
|
||||
echo c4 > c4.c &&
|
||||
git add c4.c &&
|
||||
git commit -m c4 &&
|
||||
git tag c4 &&
|
||||
echo c5 > c5.c &&
|
||||
git add c5.c &&
|
||||
git commit -m c5 &&
|
||||
git tag c5
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2, c3, c4, c5' '
|
||||
git reset --hard c1 &&
|
||||
git merge c2 c3 c4 c5 &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
|
||||
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
|
||||
test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
|
||||
test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
|
||||
git diff --exit-code &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c &&
|
||||
test -f c3.c &&
|
||||
test -f c4.c &&
|
||||
test -f c5.c
|
||||
'
|
||||
|
||||
test_done
|
||||
37
t/t7604-merge-custom-message.sh
Executable file
37
t/t7604-merge-custom-message.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing merge when using a custom message for the merge commit.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 > c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
custom message
|
||||
|
||||
Merge commit 'c2'
|
||||
EOF
|
||||
test_expect_success 'merge c2 with a custom message' '
|
||||
git reset --hard c1 &&
|
||||
git merge -m "custom message" c2 &&
|
||||
git cat-file commit HEAD | sed -e "1,/^$/d" > actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
||||
46
t/t7605-merge-resolve.sh
Executable file
46
t/t7605-merge-resolve.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing the resolve strategy.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 > c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2 &&
|
||||
git reset --hard c0 &&
|
||||
echo c3 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c3 &&
|
||||
git tag c3
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 to c2' '
|
||||
git reset --hard c1 &&
|
||||
git merge -s resolve c2 &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
|
||||
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
|
||||
git diff --exit-code &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c
|
||||
'
|
||||
|
||||
test_expect_success 'merge c2 to c3 (fails)' '
|
||||
git reset --hard c2 &&
|
||||
test_must_fail git merge -s resolve c3
|
||||
'
|
||||
test_done
|
||||
@@ -87,7 +87,7 @@ test_expect_success 'multiple dcommit from git-svn will not clobber svn' "
|
||||
"
|
||||
|
||||
|
||||
test_expect_success 'check that rebase really failed' 'test -d .dotest'
|
||||
test_expect_success 'check that rebase really failed' 'test -d .git/rebase'
|
||||
|
||||
test_expect_success 'resolve, continue the rebase and dcommit' "
|
||||
echo clobber and I really mean it > file &&
|
||||
|
||||
@@ -49,4 +49,13 @@ test_expect_success 'verify metadata for /dir' "
|
||||
grep '^git-svn-id: $dir_url@1 $uuid$'
|
||||
"
|
||||
|
||||
test_expect_success 'find commit based on SVN revision number' "
|
||||
git-svn find-rev r12 |
|
||||
grep `git rev-parse HEAD`
|
||||
"
|
||||
|
||||
test_expect_success 'empty rebase' "
|
||||
git-svn rebase
|
||||
"
|
||||
|
||||
test_done
|
||||
|
||||
@@ -18,7 +18,7 @@ fi
|
||||
|
||||
cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
|
||||
case "$cvsps_version" in
|
||||
2.1)
|
||||
2.1 | 2.2*)
|
||||
;;
|
||||
'')
|
||||
say 'skipping cvsimport tests, cvsps not found'
|
||||
@@ -26,7 +26,7 @@ case "$cvsps_version" in
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
say 'skipping cvsimport tests, cvsps too old'
|
||||
say 'skipping cvsimport tests, unsupported cvsps version'
|
||||
test_done
|
||||
exit
|
||||
;;
|
||||
|
||||
@@ -10,8 +10,6 @@ RM ?= rm -f
|
||||
prefix ?= $(HOME)
|
||||
template_instdir ?= $(prefix)/share/git-core/templates
|
||||
# DESTDIR=
|
||||
# set NOEXECTEMPL to non-empty to change the names of hook scripts
|
||||
# so that the tools will not find them
|
||||
|
||||
# Shell quote (do not use $(call) to accommodate ancient setups);
|
||||
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
|
||||
@@ -34,7 +32,6 @@ boilerplates.made : $(bpsrc)
|
||||
$(INSTALL) -d -m 755 blt/$$dir && \
|
||||
case "$$boilerplate" in \
|
||||
*--) ;; \
|
||||
hooks--*) cp -p $$boilerplate blt/$${dst}$(NOEXECTEMPL) ;; \
|
||||
*) cp -p $$boilerplate blt/$$dst ;; \
|
||||
esac || exit; \
|
||||
done && \
|
||||
|
||||
Reference in New Issue
Block a user