mirror of
https://github.com/git/git.git
synced 2026-03-13 18:33:25 +01:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
@@ -4,8 +4,16 @@ GIT v1.5.6.3 Release Notes
|
||||
Fixes since v1.5.6.2
|
||||
--------------------
|
||||
|
||||
* Setting core.sharerepository to traditional "true" value was supposed to make
|
||||
the repository group writable but should not affect permission for others.
|
||||
However, since 1.5.6, it was broken to drop permission for others when umask is
|
||||
022, making the repository unreadable by others.
|
||||
|
||||
* Setting GIT_TRACE will report spawning of external process via run_command().
|
||||
|
||||
* Using an object with very deep delta chain pinned memory needed for extracting
|
||||
intermediate base objects unnecessarily long, leading to excess memory usage.
|
||||
|
||||
* Bash completion script did not notice '--' marker on the command
|
||||
line and tried the relatively slow "ref completion" even when
|
||||
completing arguments after one.
|
||||
@@ -14,6 +22,12 @@ Fixes since v1.5.6.2
|
||||
tree file for it confused "racy-git avoidance" logic into thinking
|
||||
that the path is now unchanged.
|
||||
|
||||
* The section that describes attributes related to git-archive were placed
|
||||
in a wrong place in the gitattributes(5) manual page.
|
||||
|
||||
* "git am" was not helpful to the users when it detected that the committer
|
||||
information is not set up properly yet.
|
||||
|
||||
* "git clone" had a leftover debugging fprintf().
|
||||
|
||||
* "git clone -q" was not quiet enough as it used to and gave object count
|
||||
@@ -23,8 +37,10 @@ Fixes since v1.5.6.2
|
||||
good thing if the remote side is well packed but otherwise not,
|
||||
especially for a project that is not really big.
|
||||
|
||||
* The section that describes attributes related to git-archive were placed
|
||||
in a wrong place in the gitattributes(5) manual page.
|
||||
* "git daemon" used to call syslog() from a signal handler, which
|
||||
could raise signals of its own but generally is not reentrant. This
|
||||
was fixed by restructuring the code to report syslog() after the handler
|
||||
returns.
|
||||
|
||||
* When "git push" tries to remove a remote ref, and corresponding
|
||||
tracking ref is missing, we used to report error (i.e. failure to
|
||||
@@ -34,9 +50,3 @@ Fixes since v1.5.6.2
|
||||
MIME multipart mail correctly.
|
||||
|
||||
Contains other various documentation fixes.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.6.2-23-ge965647
|
||||
echo O=$(git describe maint)
|
||||
git shortlog --no-merges $O..maint
|
||||
|
||||
@@ -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:
|
||||
@@ -236,6 +237,7 @@ patch::
|
||||
k - leave this hunk undecided, see previous undecided hunk
|
||||
K - leave this hunk undecided, see previous hunk
|
||||
s - split the current hunk into smaller hunks
|
||||
e - manually edit the current hunk
|
||||
? - print help
|
||||
+
|
||||
After deciding the fate for all hunks, if there is any hunk
|
||||
|
||||
@@ -12,8 +12,8 @@ SYNOPSIS
|
||||
'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
|
||||
[--3way] [--interactive] [--binary]
|
||||
[--whitespace=<option>] [-C<n>] [-p<n>]
|
||||
<mbox>|<Maildir>...
|
||||
'git am' [--skip | --resolved]
|
||||
[<mbox> | <Maildir>...]
|
||||
'git am' (--skip | --resolved)
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -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>.
|
||||
|
||||
@@ -8,7 +8,7 @@ git-mailinfo - Extracts patch and authorship from a single e-mail message
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git mailinfo' [-k] [-u | --encoding=<encoding>] <msg> <patch>
|
||||
'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] <msg> <patch>
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@@ -46,6 +46,9 @@ conversion, even with this flag.
|
||||
from what is specified by i18n.commitencoding, this flag
|
||||
can be used to override it.
|
||||
|
||||
-n::
|
||||
Disable all charset re-coding of the metadata.
|
||||
|
||||
<msg>::
|
||||
The commit log message extracted from e-mail, usually
|
||||
except the title line which comes from e-mail Subject.
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -8,8 +8,11 @@ git-stash - Stash the changes in a dirty working directory away
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git stash' (list | show [<stash>] | apply [<stash>] | clear | drop [<stash>] | pop [<stash>])
|
||||
'git stash' list
|
||||
'git stash' (show | apply | drop | pop ) [<stash>]
|
||||
'git stash' branch <branchname> [<stash>]
|
||||
'git stash' [save [<message>]]
|
||||
'git stash' clear
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -36,12 +39,15 @@ is also possible).
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
save [<message>]::
|
||||
save [--keep-index] [<message>]::
|
||||
|
||||
Save your local modifications to a new 'stash', and run `git reset
|
||||
--hard` to revert them. This is the default action when no
|
||||
subcommand is given. The <message> part is optional and gives
|
||||
the description along with the stashed state.
|
||||
+
|
||||
If the `--keep-index` option is used, all changes already added to the
|
||||
index are left intact.
|
||||
|
||||
list [<options>]::
|
||||
|
||||
@@ -81,6 +87,20 @@ tree's changes, but also the index's ones. However, this can fail, when you
|
||||
have conflicts (which are stored in the index, where you therefore can no
|
||||
longer apply the changes as they were originally).
|
||||
|
||||
branch <branchname> [<stash>]::
|
||||
|
||||
Creates and checks out a new branch named `<branchname>` starting from
|
||||
the commit at which the `<stash>` was originally created, applies the
|
||||
changes recorded in `<stash>` to the new working tree and index, then
|
||||
drops the `<stash>` if that completes successfully. When no `<stash>`
|
||||
is given, applies the latest one.
|
||||
+
|
||||
This is useful if the branch on which you ran `git stash save` has
|
||||
changed enough that `git stash apply` fails due to conflicts. Since
|
||||
the stash is applied on top of the commit that was HEAD at the time
|
||||
`git stash` was run, it restores the originally stashed state with
|
||||
no conflicts.
|
||||
|
||||
clear::
|
||||
Remove all the stashed states. Note that those states will then
|
||||
be subject to pruning, and may be difficult or impossible to recover.
|
||||
@@ -169,6 +189,24 @@ $ git stash apply
|
||||
... continue hacking ...
|
||||
----------------------------------------------------------------
|
||||
|
||||
Testing partial commits::
|
||||
|
||||
You can use `git stash save --keep-index` when you want to make two or
|
||||
more commits out of the changes in the work tree, and you want to test
|
||||
each change before committing:
|
||||
+
|
||||
----------------------------------------------------------------
|
||||
... hack hack hack ...
|
||||
$ git add --patch foo # add just first part to the index
|
||||
$ git stash save --keep-index # save all other changes to the stash
|
||||
$ edit/build/test first part
|
||||
$ git commit foo -m 'First part' # commit fully tested change
|
||||
$ git stash pop # prepare to work on all other changes
|
||||
... repeat above five steps until one commit remains ...
|
||||
$ edit/build/test remaining parts
|
||||
$ git commit foo -m 'Remaining parts'
|
||||
----------------------------------------------------------------
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkgit:git-checkout[1],
|
||||
|
||||
@@ -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
|
||||
-----
|
||||
|
||||
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
|
||||
branch of the `git.git` repository.
|
||||
Documentation for older releases are available here:
|
||||
|
||||
* link:v1.5.6.2/git.html[documentation for release 1.5.6.2]
|
||||
* link:v1.5.6.3/git.html[documentation for release 1.5.6.3]
|
||||
|
||||
* release notes for
|
||||
link:RelNotes-1.5.6.3.txt[1.5.6.3].
|
||||
link:RelNotes-1.5.6.2.txt[1.5.6.2].
|
||||
link:RelNotes-1.5.6.1.txt[1.5.6.1].
|
||||
link:RelNotes-1.5.6.txt[1.5.6].
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,6 +45,10 @@ endif::git-rev-list[]
|
||||
|
||||
Print the parents of the commit.
|
||||
|
||||
--children::
|
||||
|
||||
Print the children of the commit.
|
||||
|
||||
ifdef::git-rev-list[]
|
||||
--timestamp::
|
||||
Print the raw commit timestamp.
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
4
Makefile
4
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
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
519
builtin-blame.c
519
builtin-blame.c
@@ -18,24 +18,16 @@
|
||||
#include "cache-tree.h"
|
||||
#include "path-list.h"
|
||||
#include "mailmap.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static char blame_usage[] =
|
||||
"git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
|
||||
" -c Use the same output mode as git-annotate (Default: off)\n"
|
||||
" -b Show blank SHA-1 for boundary commits (Default: off)\n"
|
||||
" -l Show long commit SHA1 (Default: off)\n"
|
||||
" --root Do not treat root commits as boundaries (Default: off)\n"
|
||||
" -t Show raw timestamp (Default: off)\n"
|
||||
" -f, --show-name Show original filename (Default: auto)\n"
|
||||
" -n, --show-number Show original linenumber (Default: off)\n"
|
||||
" -s Suppress author name and timestamp (Default: off)\n"
|
||||
" -p, --porcelain Show in a format designed for machine consumption\n"
|
||||
" -w Ignore whitespace differences\n"
|
||||
" -L n,m Process only line range n,m, counting from 1\n"
|
||||
" -M, -C Find line movements within and across files\n"
|
||||
" --incremental Show blame entries as we find them, incrementally\n"
|
||||
" --contents file Use <file>'s contents as the final image\n"
|
||||
" -S revs-file Use revisions from revs-file instead of calling git-rev-list\n";
|
||||
static char blame_usage[] = "git-blame [options] [rev-opts] [rev] [--] file";
|
||||
|
||||
static const char *blame_opt_usage[] = {
|
||||
blame_usage,
|
||||
"",
|
||||
"[rev-opts] are documented in git-rev-list(1)",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int longest_file;
|
||||
static int longest_author;
|
||||
@@ -43,6 +35,7 @@ static int max_orig_digits;
|
||||
static int max_digits;
|
||||
static int max_score_digits;
|
||||
static int show_root;
|
||||
static int reverse;
|
||||
static int blank_boundary;
|
||||
static int incremental;
|
||||
static int cmd_is_annotate;
|
||||
@@ -91,7 +84,7 @@ struct origin {
|
||||
* Given an origin, prepare mmfile_t structure to be used by the
|
||||
* diff machinery
|
||||
*/
|
||||
static char *fill_origin_blob(struct origin *o, mmfile_t *file)
|
||||
static void fill_origin_blob(struct origin *o, mmfile_t *file)
|
||||
{
|
||||
if (!o->file.ptr) {
|
||||
enum object_type type;
|
||||
@@ -106,7 +99,6 @@ static char *fill_origin_blob(struct origin *o, mmfile_t *file)
|
||||
}
|
||||
else
|
||||
*file = o->file;
|
||||
return file->ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -178,7 +170,7 @@ struct blame_entry {
|
||||
struct scoreboard {
|
||||
/* the final commit (i.e. where we started digging from) */
|
||||
struct commit *final;
|
||||
|
||||
struct rev_info *revs;
|
||||
const char *path;
|
||||
|
||||
/*
|
||||
@@ -1192,18 +1184,48 @@ static void pass_whole_blame(struct scoreboard *sb,
|
||||
}
|
||||
}
|
||||
|
||||
#define MAXPARENT 16
|
||||
/*
|
||||
* We pass blame from the current commit to its parents. We keep saying
|
||||
* "parent" (and "porigin"), but what we mean is to find scapegoat to
|
||||
* exonerate ourselves.
|
||||
*/
|
||||
static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
|
||||
{
|
||||
if (!reverse)
|
||||
return commit->parents;
|
||||
return lookup_decoration(&revs->children, &commit->object);
|
||||
}
|
||||
|
||||
static int num_scapegoats(struct rev_info *revs, struct commit *commit)
|
||||
{
|
||||
int cnt;
|
||||
struct commit_list *l = first_scapegoat(revs, commit);
|
||||
for (cnt = 0; l; l = l->next)
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#define MAXSG 16
|
||||
|
||||
static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
{
|
||||
int i, pass;
|
||||
struct rev_info *revs = sb->revs;
|
||||
int i, pass, num_sg;
|
||||
struct commit *commit = origin->commit;
|
||||
struct commit_list *parent;
|
||||
struct origin *parent_origin[MAXPARENT], *porigin;
|
||||
struct commit_list *sg;
|
||||
struct origin *sg_buf[MAXSG];
|
||||
struct origin *porigin, **sg_origin = sg_buf;
|
||||
|
||||
memset(parent_origin, 0, sizeof(parent_origin));
|
||||
num_sg = num_scapegoats(revs, commit);
|
||||
if (!num_sg)
|
||||
goto finish;
|
||||
else if (num_sg < ARRAY_SIZE(sg_buf))
|
||||
memset(sg_buf, 0, sizeof(sg_buf));
|
||||
else
|
||||
sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
|
||||
|
||||
/* The first pass looks for unrenamed path to optimize for
|
||||
/*
|
||||
* The first pass looks for unrenamed path to optimize for
|
||||
* common cases, then we look for renames in the second pass.
|
||||
*/
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
@@ -1211,13 +1233,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
struct commit *, struct origin *);
|
||||
find = pass ? find_rename : find_origin;
|
||||
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct commit *p = parent->item;
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct commit *p = sg->item;
|
||||
int j, same;
|
||||
|
||||
if (parent_origin[i])
|
||||
if (sg_origin[i])
|
||||
continue;
|
||||
if (parse_commit(p))
|
||||
continue;
|
||||
@@ -1230,24 +1252,24 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
goto finish;
|
||||
}
|
||||
for (j = same = 0; j < i; j++)
|
||||
if (parent_origin[j] &&
|
||||
!hashcmp(parent_origin[j]->blob_sha1,
|
||||
if (sg_origin[j] &&
|
||||
!hashcmp(sg_origin[j]->blob_sha1,
|
||||
porigin->blob_sha1)) {
|
||||
same = 1;
|
||||
break;
|
||||
}
|
||||
if (!same)
|
||||
parent_origin[i] = porigin;
|
||||
sg_origin[i] = porigin;
|
||||
else
|
||||
origin_decref(porigin);
|
||||
}
|
||||
}
|
||||
|
||||
num_commits++;
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct origin *porigin = parent_origin[i];
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct origin *porigin = sg_origin[i];
|
||||
if (!porigin)
|
||||
continue;
|
||||
if (pass_blame_to_parent(sb, origin, porigin))
|
||||
@@ -1258,10 +1280,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
* Optionally find moves in parents' files.
|
||||
*/
|
||||
if (opt & PICKAXE_BLAME_MOVE)
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct origin *porigin = parent_origin[i];
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct origin *porigin = sg_origin[i];
|
||||
if (!porigin)
|
||||
continue;
|
||||
if (find_move_in_parent(sb, origin, porigin))
|
||||
@@ -1272,23 +1294,25 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
* Optionally find copies from parents' files.
|
||||
*/
|
||||
if (opt & PICKAXE_BLAME_COPY)
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct origin *porigin = parent_origin[i];
|
||||
if (find_copy_in_parent(sb, origin, parent->item,
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct origin *porigin = sg_origin[i];
|
||||
if (find_copy_in_parent(sb, origin, sg->item,
|
||||
porigin, opt))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
finish:
|
||||
for (i = 0; i < MAXPARENT; i++) {
|
||||
if (parent_origin[i]) {
|
||||
drop_origin_blob(parent_origin[i]);
|
||||
origin_decref(parent_origin[i]);
|
||||
for (i = 0; i < num_sg; i++) {
|
||||
if (sg_origin[i]) {
|
||||
drop_origin_blob(sg_origin[i]);
|
||||
origin_decref(sg_origin[i]);
|
||||
}
|
||||
}
|
||||
drop_origin_blob(origin);
|
||||
if (sg_buf != sg_origin)
|
||||
free(sg_origin);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1487,8 +1511,10 @@ static void found_guilty_entry(struct blame_entry *ent)
|
||||
* is still unknown, pick one blame_entry, and allow its current
|
||||
* suspect to pass blames to its parents.
|
||||
*/
|
||||
static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
|
||||
static void assign_blame(struct scoreboard *sb, int opt)
|
||||
{
|
||||
struct rev_info *revs = sb->revs;
|
||||
|
||||
while (1) {
|
||||
struct blame_entry *ent;
|
||||
struct commit *commit;
|
||||
@@ -1509,8 +1535,9 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
|
||||
commit = suspect->commit;
|
||||
if (!commit->object.parsed)
|
||||
parse_commit(commit);
|
||||
if (!(commit->object.flags & UNINTERESTING) &&
|
||||
!(revs->max_age != -1 && commit->date < revs->max_age))
|
||||
if (reverse ||
|
||||
(!(commit->object.flags & UNINTERESTING) &&
|
||||
!(revs->max_age != -1 && commit->date < revs->max_age)))
|
||||
pass_blame(sb, suspect, opt);
|
||||
else {
|
||||
commit->object.flags |= UNINTERESTING;
|
||||
@@ -2006,6 +2033,10 @@ static int git_blame_config(const char *var, const char *value, void *cb)
|
||||
return git_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare a dummy commit that represents the work tree (or staged) item.
|
||||
* Note that annotating work tree item never works in the reverse.
|
||||
*/
|
||||
static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
|
||||
{
|
||||
struct commit *commit;
|
||||
@@ -2122,6 +2153,108 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
return commit;
|
||||
}
|
||||
|
||||
static const char *prepare_final(struct scoreboard *sb)
|
||||
{
|
||||
int i;
|
||||
const char *final_commit_name = NULL;
|
||||
struct rev_info *revs = sb->revs;
|
||||
|
||||
/*
|
||||
* There must be one and only one positive commit in the
|
||||
* revs->pending array.
|
||||
*/
|
||||
for (i = 0; i < revs->pending.nr; i++) {
|
||||
struct object *obj = revs->pending.objects[i].item;
|
||||
if (obj->flags & UNINTERESTING)
|
||||
continue;
|
||||
while (obj->type == OBJ_TAG)
|
||||
obj = deref_tag(obj, NULL, 0);
|
||||
if (obj->type != OBJ_COMMIT)
|
||||
die("Non commit %s?", revs->pending.objects[i].name);
|
||||
if (sb->final)
|
||||
die("More than one commit to dig from %s and %s?",
|
||||
revs->pending.objects[i].name,
|
||||
final_commit_name);
|
||||
sb->final = (struct commit *) obj;
|
||||
final_commit_name = revs->pending.objects[i].name;
|
||||
}
|
||||
return final_commit_name;
|
||||
}
|
||||
|
||||
static const char *prepare_initial(struct scoreboard *sb)
|
||||
{
|
||||
int i;
|
||||
const char *final_commit_name = NULL;
|
||||
struct rev_info *revs = sb->revs;
|
||||
|
||||
/*
|
||||
* There must be one and only one negative commit, and it must be
|
||||
* the boundary.
|
||||
*/
|
||||
for (i = 0; i < revs->pending.nr; i++) {
|
||||
struct object *obj = revs->pending.objects[i].item;
|
||||
if (!(obj->flags & UNINTERESTING))
|
||||
continue;
|
||||
while (obj->type == OBJ_TAG)
|
||||
obj = deref_tag(obj, NULL, 0);
|
||||
if (obj->type != OBJ_COMMIT)
|
||||
die("Non commit %s?", revs->pending.objects[i].name);
|
||||
if (sb->final)
|
||||
die("More than one commit to dig down to %s and %s?",
|
||||
revs->pending.objects[i].name,
|
||||
final_commit_name);
|
||||
sb->final = (struct commit *) obj;
|
||||
final_commit_name = revs->pending.objects[i].name;
|
||||
}
|
||||
if (!final_commit_name)
|
||||
die("No commit to dig down to?");
|
||||
return final_commit_name;
|
||||
}
|
||||
|
||||
static int blame_copy_callback(const struct option *option, const char *arg, int unset)
|
||||
{
|
||||
int *opt = option->value;
|
||||
|
||||
/*
|
||||
* -C enables copy from removed files;
|
||||
* -C -C enables copy from existing files, but only
|
||||
* when blaming a new file;
|
||||
* -C -C -C enables copy from existing files for
|
||||
* everybody
|
||||
*/
|
||||
if (*opt & PICKAXE_BLAME_COPY_HARDER)
|
||||
*opt |= PICKAXE_BLAME_COPY_HARDEST;
|
||||
if (*opt & PICKAXE_BLAME_COPY)
|
||||
*opt |= PICKAXE_BLAME_COPY_HARDER;
|
||||
*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
|
||||
|
||||
if (arg)
|
||||
blame_copy_score = parse_score(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blame_move_callback(const struct option *option, const char *arg, int unset)
|
||||
{
|
||||
int *opt = option->value;
|
||||
|
||||
*opt |= PICKAXE_BLAME_MOVE;
|
||||
|
||||
if (arg)
|
||||
blame_move_score = parse_score(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
|
||||
{
|
||||
const char **bottomtop = option->value;
|
||||
if (!arg)
|
||||
return -1;
|
||||
if (*bottomtop)
|
||||
die("More than one '-L n,m' option given");
|
||||
*bottomtop = arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct rev_info revs;
|
||||
@@ -2129,102 +2262,66 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
struct scoreboard sb;
|
||||
struct origin *o;
|
||||
struct blame_entry *ent;
|
||||
int i, seen_dashdash, unk, opt;
|
||||
long bottom, top, lno;
|
||||
int output_option = 0;
|
||||
int show_stats = 0;
|
||||
const char *revs_file = NULL;
|
||||
long dashdash_pos, bottom, top, lno;
|
||||
const char *final_commit_name = NULL;
|
||||
enum object_type type;
|
||||
const char *bottomtop = NULL;
|
||||
const char *contents_from = NULL;
|
||||
|
||||
static const char *bottomtop = NULL;
|
||||
static int output_option = 0, opt = 0;
|
||||
static int show_stats = 0;
|
||||
static const char *revs_file = NULL;
|
||||
static const char *contents_from = NULL;
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN(0, "incremental", &incremental, "Show blame entries as we find them, incrementally"),
|
||||
OPT_BOOLEAN('b', NULL, &blank_boundary, "Show blank SHA-1 for boundary commits (Default: off)"),
|
||||
OPT_BOOLEAN(0, "root", &show_root, "Do not treat root commits as boundaries (Default: off)"),
|
||||
OPT_BOOLEAN(0, "show-stats", &show_stats, "Show work cost statistics"),
|
||||
OPT_BIT(0, "score-debug", &output_option, "Show output score for blame entries", OUTPUT_SHOW_SCORE),
|
||||
OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
|
||||
OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
|
||||
OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
|
||||
OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
|
||||
OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
|
||||
OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
|
||||
OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
|
||||
OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
|
||||
OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
|
||||
OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
|
||||
{ OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
|
||||
{ OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
|
||||
OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
struct parse_opt_ctx_t ctx;
|
||||
|
||||
cmd_is_annotate = !strcmp(argv[0], "annotate");
|
||||
|
||||
git_config(git_blame_config, NULL);
|
||||
init_revisions(&revs, NULL);
|
||||
save_commit_buffer = 0;
|
||||
dashdash_pos = 0;
|
||||
|
||||
opt = 0;
|
||||
seen_dashdash = 0;
|
||||
for (unk = i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (*arg != '-')
|
||||
break;
|
||||
else if (!strcmp("-b", arg))
|
||||
blank_boundary = 1;
|
||||
else if (!strcmp("--root", arg))
|
||||
show_root = 1;
|
||||
else if (!strcmp(arg, "--show-stats"))
|
||||
show_stats = 1;
|
||||
else if (!strcmp("-c", arg))
|
||||
output_option |= OUTPUT_ANNOTATE_COMPAT;
|
||||
else if (!strcmp("-t", arg))
|
||||
output_option |= OUTPUT_RAW_TIMESTAMP;
|
||||
else if (!strcmp("-l", arg))
|
||||
output_option |= OUTPUT_LONG_OBJECT_NAME;
|
||||
else if (!strcmp("-s", arg))
|
||||
output_option |= OUTPUT_NO_AUTHOR;
|
||||
else if (!strcmp("-w", arg))
|
||||
xdl_opts |= XDF_IGNORE_WHITESPACE;
|
||||
else if (!strcmp("-S", arg) && ++i < argc)
|
||||
revs_file = argv[i];
|
||||
else if (!prefixcmp(arg, "-M")) {
|
||||
opt |= PICKAXE_BLAME_MOVE;
|
||||
blame_move_score = parse_score(arg+2);
|
||||
parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
|
||||
PARSE_OPT_KEEP_ARGV0);
|
||||
for (;;) {
|
||||
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
|
||||
case PARSE_OPT_HELP:
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
if (ctx.argv[0])
|
||||
dashdash_pos = ctx.cpidx;
|
||||
goto parse_done;
|
||||
}
|
||||
else if (!prefixcmp(arg, "-C")) {
|
||||
/*
|
||||
* -C enables copy from removed files;
|
||||
* -C -C enables copy from existing files, but only
|
||||
* when blaming a new file;
|
||||
* -C -C -C enables copy from existing files for
|
||||
* everybody
|
||||
*/
|
||||
if (opt & PICKAXE_BLAME_COPY_HARDER)
|
||||
opt |= PICKAXE_BLAME_COPY_HARDEST;
|
||||
if (opt & PICKAXE_BLAME_COPY)
|
||||
opt |= PICKAXE_BLAME_COPY_HARDER;
|
||||
opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
|
||||
blame_copy_score = parse_score(arg+2);
|
||||
|
||||
if (!strcmp(ctx.argv[0], "--reverse")) {
|
||||
ctx.argv[0] = "--children";
|
||||
reverse = 1;
|
||||
}
|
||||
else if (!prefixcmp(arg, "-L")) {
|
||||
if (!arg[2]) {
|
||||
if (++i >= argc)
|
||||
usage(blame_usage);
|
||||
arg = argv[i];
|
||||
}
|
||||
else
|
||||
arg += 2;
|
||||
if (bottomtop)
|
||||
die("More than one '-L n,m' option given");
|
||||
bottomtop = arg;
|
||||
}
|
||||
else if (!strcmp("--contents", arg)) {
|
||||
if (++i >= argc)
|
||||
usage(blame_usage);
|
||||
contents_from = argv[i];
|
||||
}
|
||||
else if (!strcmp("--incremental", arg))
|
||||
incremental = 1;
|
||||
else if (!strcmp("--score-debug", arg))
|
||||
output_option |= OUTPUT_SHOW_SCORE;
|
||||
else if (!strcmp("-f", arg) ||
|
||||
!strcmp("--show-name", arg))
|
||||
output_option |= OUTPUT_SHOW_NAME;
|
||||
else if (!strcmp("-n", arg) ||
|
||||
!strcmp("--show-number", arg))
|
||||
output_option |= OUTPUT_SHOW_NUMBER;
|
||||
else if (!strcmp("-p", arg) ||
|
||||
!strcmp("--porcelain", arg))
|
||||
output_option |= OUTPUT_PORCELAIN;
|
||||
else if (!strcmp("--", arg)) {
|
||||
seen_dashdash = 1;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
argv[unk++] = arg;
|
||||
parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
|
||||
}
|
||||
parse_done:
|
||||
argc = parse_options_end(&ctx);
|
||||
|
||||
if (!blame_move_score)
|
||||
blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
|
||||
@@ -2238,115 +2335,59 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
*
|
||||
* The remaining are:
|
||||
*
|
||||
* (1) if seen_dashdash, its either
|
||||
* "-options -- <path>" or
|
||||
* "-options -- <path> <rev>".
|
||||
* but the latter is allowed only if there is no
|
||||
* options that we passed to revision machinery.
|
||||
* (1) if dashdash_pos != 0, its either
|
||||
* "blame [revisions] -- <path>" or
|
||||
* "blame -- <path> <rev>"
|
||||
*
|
||||
* (2) otherwise, we may have "--" somewhere later and
|
||||
* might be looking at the first one of multiple 'rev'
|
||||
* parameters (e.g. " master ^next ^maint -- path").
|
||||
* See if there is a dashdash first, and give the
|
||||
* arguments before that to revision machinery.
|
||||
* After that there must be one 'path'.
|
||||
* (2) otherwise, its one of the two:
|
||||
* "blame [revisions] <path>"
|
||||
* "blame <path> <rev>"
|
||||
*
|
||||
* (3) otherwise, its one of the three:
|
||||
* "-options <path> <rev>"
|
||||
* "-options <rev> <path>"
|
||||
* "-options <path>"
|
||||
* but again the first one is allowed only if
|
||||
* there is no options that we passed to revision
|
||||
* machinery.
|
||||
* Note that we must strip out <path> from the arguments: we do not
|
||||
* want the path pruning but we may want "bottom" processing.
|
||||
*/
|
||||
|
||||
if (seen_dashdash) {
|
||||
/* (1) */
|
||||
if (argc <= i)
|
||||
usage(blame_usage);
|
||||
path = add_prefix(prefix, argv[i]);
|
||||
if (i + 1 == argc - 1) {
|
||||
if (unk != 1)
|
||||
usage(blame_usage);
|
||||
argv[unk++] = argv[i + 1];
|
||||
if (dashdash_pos) {
|
||||
switch (argc - dashdash_pos - 1) {
|
||||
case 2: /* (1b) */
|
||||
if (argc != 4)
|
||||
usage_with_options(blame_opt_usage, options);
|
||||
/* reorder for the new way: <rev> -- <path> */
|
||||
argv[1] = argv[3];
|
||||
argv[3] = argv[2];
|
||||
argv[2] = "--";
|
||||
/* FALLTHROUGH */
|
||||
case 1: /* (1a) */
|
||||
path = add_prefix(prefix, argv[--argc]);
|
||||
argv[argc] = NULL;
|
||||
break;
|
||||
default:
|
||||
usage_with_options(blame_opt_usage, options);
|
||||
}
|
||||
else if (i + 1 != argc)
|
||||
/* garbage at end */
|
||||
usage(blame_usage);
|
||||
}
|
||||
else {
|
||||
int j;
|
||||
for (j = i; !seen_dashdash && j < argc; j++)
|
||||
if (!strcmp(argv[j], "--"))
|
||||
seen_dashdash = j;
|
||||
if (seen_dashdash) {
|
||||
/* (2) */
|
||||
if (seen_dashdash + 1 != argc - 1)
|
||||
usage(blame_usage);
|
||||
path = add_prefix(prefix, argv[seen_dashdash + 1]);
|
||||
for (j = i; j < seen_dashdash; j++)
|
||||
argv[unk++] = argv[j];
|
||||
} else {
|
||||
if (argc < 2)
|
||||
usage_with_options(blame_opt_usage, options);
|
||||
path = add_prefix(prefix, argv[argc - 1]);
|
||||
if (argc == 3 && !has_path_in_work_tree(path)) { /* (2b) */
|
||||
path = add_prefix(prefix, argv[1]);
|
||||
argv[1] = argv[2];
|
||||
}
|
||||
else {
|
||||
/* (3) */
|
||||
if (argc <= i)
|
||||
usage(blame_usage);
|
||||
path = add_prefix(prefix, argv[i]);
|
||||
if (i + 1 == argc - 1) {
|
||||
final_commit_name = argv[i + 1];
|
||||
argv[argc - 1] = "--";
|
||||
|
||||
/* if (unk == 1) we could be getting
|
||||
* old-style
|
||||
*/
|
||||
if (unk == 1 && !has_path_in_work_tree(path)) {
|
||||
path = add_prefix(prefix, argv[i + 1]);
|
||||
final_commit_name = argv[i];
|
||||
}
|
||||
}
|
||||
else if (i != argc - 1)
|
||||
usage(blame_usage); /* garbage at end */
|
||||
|
||||
setup_work_tree();
|
||||
if (!has_path_in_work_tree(path))
|
||||
die("cannot stat path %s: %s",
|
||||
path, strerror(errno));
|
||||
}
|
||||
setup_work_tree();
|
||||
if (!has_path_in_work_tree(path))
|
||||
die("cannot stat path %s: %s", path, strerror(errno));
|
||||
}
|
||||
|
||||
if (final_commit_name)
|
||||
argv[unk++] = final_commit_name;
|
||||
|
||||
/*
|
||||
* Now we got rev and path. We do not want the path pruning
|
||||
* but we may want "bottom" processing.
|
||||
*/
|
||||
argv[unk++] = "--"; /* terminate the rev name */
|
||||
argv[unk] = NULL;
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(unk, argv, &revs, NULL);
|
||||
setup_revisions(argc, argv, &revs, NULL);
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
|
||||
/*
|
||||
* There must be one and only one positive commit in the
|
||||
* revs->pending array.
|
||||
*/
|
||||
for (i = 0; i < revs.pending.nr; i++) {
|
||||
struct object *obj = revs.pending.objects[i].item;
|
||||
if (obj->flags & UNINTERESTING)
|
||||
continue;
|
||||
while (obj->type == OBJ_TAG)
|
||||
obj = deref_tag(obj, NULL, 0);
|
||||
if (obj->type != OBJ_COMMIT)
|
||||
die("Non commit %s?",
|
||||
revs.pending.objects[i].name);
|
||||
if (sb.final)
|
||||
die("More than one commit to dig from %s and %s?",
|
||||
revs.pending.objects[i].name,
|
||||
final_commit_name);
|
||||
sb.final = (struct commit *) obj;
|
||||
final_commit_name = revs.pending.objects[i].name;
|
||||
}
|
||||
sb.revs = &revs;
|
||||
if (!reverse)
|
||||
final_commit_name = prepare_final(&sb);
|
||||
else if (contents_from)
|
||||
die("--contents and --children do not blend well.");
|
||||
else
|
||||
final_commit_name = prepare_initial(&sb);
|
||||
|
||||
if (!sb.final) {
|
||||
/*
|
||||
@@ -2425,7 +2466,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
if (!incremental)
|
||||
setup_pager();
|
||||
|
||||
assign_blame(&sb, &revs, opt);
|
||||
assign_blame(&sb, opt);
|
||||
|
||||
if (incremental)
|
||||
return 0;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -282,6 +289,21 @@ static int ref_cmp(const void *r1, const void *r2)
|
||||
return strcmp(c1->name, c2->name);
|
||||
}
|
||||
|
||||
static void fill_tracking_info(char *stat, const char *branch_name)
|
||||
{
|
||||
int ours, theirs;
|
||||
struct branch *branch = branch_get(branch_name);
|
||||
|
||||
if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
|
||||
return;
|
||||
if (!ours)
|
||||
sprintf(stat, "[behind %d] ", theirs);
|
||||
else if (!theirs)
|
||||
sprintf(stat, "[ahead %d] ", ours);
|
||||
else
|
||||
sprintf(stat, "[ahead %d, behind %d] ", ours, theirs);
|
||||
}
|
||||
|
||||
static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
int abbrev, int current)
|
||||
{
|
||||
@@ -310,8 +332,10 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
if (verbose) {
|
||||
struct strbuf subject;
|
||||
const char *sub = " **** invalid ref ****";
|
||||
char stat[128];
|
||||
|
||||
strbuf_init(&subject, 0);
|
||||
stat[0] = '\0';
|
||||
|
||||
commit = lookup_commit(item->sha1);
|
||||
if (commit && !parse_commit(commit)) {
|
||||
@@ -319,10 +343,15 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
&subject, 0, NULL, NULL, 0, 0);
|
||||
sub = subject.buf;
|
||||
}
|
||||
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
|
||||
|
||||
if (item->kind == REF_LOCAL_BRANCH)
|
||||
fill_tracking_info(stat, item->name);
|
||||
|
||||
printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
|
||||
maxwidth, item->name,
|
||||
branch_get_color(COLOR_BRANCH_RESET),
|
||||
find_unique_abbrev(item->sha1, abbrev), sub);
|
||||
find_unique_abbrev(item->sha1, abbrev),
|
||||
stat, sub);
|
||||
strbuf_release(&subject);
|
||||
} else {
|
||||
printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
|
||||
@@ -421,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;
|
||||
@@ -438,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),
|
||||
|
||||
@@ -457,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(),
|
||||
};
|
||||
|
||||
@@ -467,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)
|
||||
@@ -482,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);
|
||||
|
||||
@@ -305,97 +305,15 @@ static int merge_working_tree(struct checkout_opts *opts,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
|
||||
static void report_tracking(struct branch_info *new)
|
||||
{
|
||||
/*
|
||||
* We have switched to a new branch; is it building on
|
||||
* top of another branch, and if so does that other branch
|
||||
* have changes we do not have yet?
|
||||
*/
|
||||
char *base;
|
||||
unsigned char sha1[20];
|
||||
struct commit *ours, *theirs;
|
||||
char symmetric[84];
|
||||
struct rev_info revs;
|
||||
const char *rev_argv[10];
|
||||
int rev_argc;
|
||||
int num_ours, num_theirs;
|
||||
const char *remote_msg;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
struct branch *branch = branch_get(new->name);
|
||||
|
||||
/*
|
||||
* Nothing to report unless we are marked to build on top of
|
||||
* somebody else.
|
||||
*/
|
||||
if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
||||
if (!format_tracking_info(branch, &sb))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If what we used to build on no longer exists, there is
|
||||
* nothing to report.
|
||||
*/
|
||||
base = branch->merge[0]->dst;
|
||||
if (!resolve_ref(base, sha1, 1, NULL))
|
||||
return;
|
||||
|
||||
theirs = lookup_commit(sha1);
|
||||
ours = new->commit;
|
||||
if (!hashcmp(sha1, ours->object.sha1))
|
||||
return; /* we are the same */
|
||||
|
||||
/* Run "rev-list --left-right ours...theirs" internally... */
|
||||
rev_argc = 0;
|
||||
rev_argv[rev_argc++] = NULL;
|
||||
rev_argv[rev_argc++] = "--left-right";
|
||||
rev_argv[rev_argc++] = symmetric;
|
||||
rev_argv[rev_argc++] = "--";
|
||||
rev_argv[rev_argc] = NULL;
|
||||
|
||||
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
||||
strcpy(symmetric + 40, "...");
|
||||
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
||||
prepare_revision_walk(&revs);
|
||||
|
||||
/* ... and count the commits on each side. */
|
||||
num_ours = 0;
|
||||
num_theirs = 0;
|
||||
while (1) {
|
||||
struct commit *c = get_revision(&revs);
|
||||
if (!c)
|
||||
break;
|
||||
if (c->object.flags & SYMMETRIC_LEFT)
|
||||
num_ours++;
|
||||
else
|
||||
num_theirs++;
|
||||
}
|
||||
|
||||
if (!prefixcmp(base, "refs/remotes/")) {
|
||||
remote_msg = " remote";
|
||||
base += strlen("refs/remotes/");
|
||||
} else {
|
||||
remote_msg = "";
|
||||
}
|
||||
|
||||
if (!num_theirs)
|
||||
printf("Your branch is ahead of the tracked%s branch '%s' "
|
||||
"by %d commit%s.\n",
|
||||
remote_msg, base,
|
||||
num_ours, (num_ours == 1) ? "" : "s");
|
||||
else if (!num_ours)
|
||||
printf("Your branch is behind the tracked%s branch '%s' "
|
||||
"by %d commit%s,\n"
|
||||
"and can be fast-forwarded.\n",
|
||||
remote_msg, base,
|
||||
num_theirs, (num_theirs == 1) ? "" : "s");
|
||||
else
|
||||
printf("Your branch and the tracked%s branch '%s' "
|
||||
"have diverged,\nand respectively "
|
||||
"have %d and %d different commit(s) each.\n",
|
||||
remote_msg, base,
|
||||
num_ours, num_theirs);
|
||||
fputs(sb.buf, stdout);
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
@@ -441,7 +359,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
|
||||
remove_branch_state();
|
||||
strbuf_release(&msg);
|
||||
if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
|
||||
report_tracking(new, opts);
|
||||
report_tracking(new);
|
||||
}
|
||||
|
||||
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -960,7 +962,7 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
|
||||
}
|
||||
|
||||
static const char mailinfo_usage[] =
|
||||
"git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
|
||||
"git-mailinfo [-k] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
|
||||
|
||||
int cmd_mailinfo(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "path-list.h"
|
||||
|
||||
static const char git_mailsplit_usage[] =
|
||||
"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>|<Maildir>...";
|
||||
"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
|
||||
|
||||
static int is_from_line(const char *line, int len)
|
||||
{
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -37,6 +37,7 @@ static const char rev_list_usage[] =
|
||||
" --reverse\n"
|
||||
" formatting output:\n"
|
||||
" --parents\n"
|
||||
" --children\n"
|
||||
" --objects | --objects-edge\n"
|
||||
" --unpacked\n"
|
||||
" --header | --pretty\n"
|
||||
@@ -90,6 +91,15 @@ static void show_commit(struct commit *commit)
|
||||
parents = parents->next;
|
||||
}
|
||||
}
|
||||
if (revs.children.name) {
|
||||
struct commit_list *children;
|
||||
|
||||
children = lookup_decoration(&revs.children, &commit->object);
|
||||
while (children) {
|
||||
printf(" %s", sha1_to_hex(children->item->object.sha1));
|
||||
children = children->next;
|
||||
}
|
||||
}
|
||||
show_decorations(commit);
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
putchar(' ');
|
||||
|
||||
@@ -206,6 +206,7 @@ static int merge_recursive(const char *base_sha1,
|
||||
{
|
||||
char buffer[256];
|
||||
const char *argv[6];
|
||||
int i = 0;
|
||||
|
||||
sprintf(buffer, "GITHEAD_%s", head_sha1);
|
||||
setenv(buffer, head_name, 1);
|
||||
@@ -218,12 +219,13 @@ static int merge_recursive(const char *base_sha1,
|
||||
* and $prev on top of us (when reverting), or the change between
|
||||
* $prev and $commit on top of us (when cherry-picking or replaying).
|
||||
*/
|
||||
argv[0] = "merge-recursive";
|
||||
argv[1] = base_sha1;
|
||||
argv[2] = "--";
|
||||
argv[3] = head_sha1;
|
||||
argv[4] = next_sha1;
|
||||
argv[5] = NULL;
|
||||
argv[i++] = "merge-recursive";
|
||||
if (base_sha1)
|
||||
argv[i++] = base_sha1;
|
||||
argv[i++] = "--";
|
||||
argv[i++] = head_sha1;
|
||||
argv[i++] = next_sha1;
|
||||
argv[i++] = NULL;
|
||||
|
||||
return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
|
||||
}
|
||||
@@ -297,9 +299,12 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
discard_cache();
|
||||
}
|
||||
|
||||
if (!commit->parents)
|
||||
die ("Cannot %s a root commit", me);
|
||||
if (commit->parents->next) {
|
||||
if (!commit->parents) {
|
||||
if (action == REVERT)
|
||||
die ("Cannot revert a root commit");
|
||||
parent = NULL;
|
||||
}
|
||||
else if (commit->parents->next) {
|
||||
/* Reverting or cherry-picking a merge commit */
|
||||
int cnt;
|
||||
struct commit_list *p;
|
||||
@@ -368,7 +373,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (merge_recursive(sha1_to_hex(base->object.sha1),
|
||||
if (merge_recursive(base == NULL ?
|
||||
NULL : sha1_to_hex(base->object.sha1),
|
||||
sha1_to_hex(head), "HEAD",
|
||||
sha1_to_hex(next->object.sha1), oneline) ||
|
||||
write_cache_as_tree(head, 0, NULL)) {
|
||||
|
||||
@@ -7,9 +7,14 @@
|
||||
#include "utf8.h"
|
||||
#include "mailmap.h"
|
||||
#include "shortlog.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char shortlog_usage[] =
|
||||
"git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]";
|
||||
static char const * const shortlog_usage[] = {
|
||||
"git-shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
|
||||
"",
|
||||
"[rev-opts] are documented in git-rev-list(1)",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int compare_by_number(const void *a1, const void *a2)
|
||||
{
|
||||
@@ -149,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);
|
||||
@@ -164,21 +178,19 @@ static void get_from_rev(struct rev_info *rev, struct shortlog *log)
|
||||
shortlog_add_commit(log, commit);
|
||||
}
|
||||
|
||||
static int parse_uint(char const **arg, int comma)
|
||||
static int parse_uint(char const **arg, int comma, int defval)
|
||||
{
|
||||
unsigned long ul;
|
||||
int ret;
|
||||
char *endp;
|
||||
|
||||
ul = strtoul(*arg, &endp, 10);
|
||||
if (endp != *arg && *endp && *endp != comma)
|
||||
if (*endp && *endp != comma)
|
||||
return -1;
|
||||
ret = (int) ul;
|
||||
if (ret != ul)
|
||||
if (ul > INT_MAX)
|
||||
return -1;
|
||||
*arg = endp;
|
||||
if (**arg)
|
||||
(*arg)++;
|
||||
ret = *arg == endp ? defval : (int)ul;
|
||||
*arg = *endp ? endp + 1 : endp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -187,30 +199,30 @@ static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
|
||||
#define DEFAULT_INDENT1 6
|
||||
#define DEFAULT_INDENT2 9
|
||||
|
||||
static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
|
||||
static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
arg += 2; /* skip -w */
|
||||
struct shortlog *log = opt->value;
|
||||
|
||||
*wrap = parse_uint(&arg, ',');
|
||||
if (*wrap < 0)
|
||||
die(wrap_arg_usage);
|
||||
*in1 = parse_uint(&arg, ',');
|
||||
if (*in1 < 0)
|
||||
die(wrap_arg_usage);
|
||||
*in2 = parse_uint(&arg, '\0');
|
||||
if (*in2 < 0)
|
||||
die(wrap_arg_usage);
|
||||
log->wrap_lines = !unset;
|
||||
if (unset)
|
||||
return 0;
|
||||
if (!arg) {
|
||||
log->wrap = DEFAULT_WRAPLEN;
|
||||
log->in1 = DEFAULT_INDENT1;
|
||||
log->in2 = DEFAULT_INDENT2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!*wrap)
|
||||
*wrap = DEFAULT_WRAPLEN;
|
||||
if (!*in1)
|
||||
*in1 = DEFAULT_INDENT1;
|
||||
if (!*in2)
|
||||
*in2 = DEFAULT_INDENT2;
|
||||
if (*wrap &&
|
||||
((*in1 && *wrap <= *in1) ||
|
||||
(*in2 && *wrap <= *in2)))
|
||||
die(wrap_arg_usage);
|
||||
log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
|
||||
log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
|
||||
log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
|
||||
if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
|
||||
return error(wrap_arg_usage);
|
||||
if (log->wrap &&
|
||||
((log->in1 && log->wrap <= log->in1) ||
|
||||
(log->in2 && log->wrap <= log->in2)))
|
||||
return error(wrap_arg_usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shortlog_init(struct shortlog *log)
|
||||
@@ -227,38 +239,48 @@ void shortlog_init(struct shortlog *log)
|
||||
|
||||
int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct shortlog log;
|
||||
struct rev_info rev;
|
||||
static struct shortlog log;
|
||||
static struct rev_info rev;
|
||||
int nongit;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
|
||||
"sort output according to the number of commits per author"),
|
||||
OPT_BOOLEAN('s', "summary", &log.summary,
|
||||
"Suppress commit descriptions, only provides commit count"),
|
||||
OPT_BOOLEAN('e', "email", &log.email,
|
||||
"Show the email address of each author"),
|
||||
{ OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]",
|
||||
"Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args },
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
struct parse_opt_ctx_t ctx;
|
||||
|
||||
prefix = setup_git_directory_gently(&nongit);
|
||||
shortlog_init(&log);
|
||||
|
||||
/* since -n is a shadowed rev argument, parse our args first */
|
||||
while (argc > 1) {
|
||||
if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
|
||||
log.sort_by_number = 1;
|
||||
else if (!strcmp(argv[1], "-s") ||
|
||||
!strcmp(argv[1], "--summary"))
|
||||
log.summary = 1;
|
||||
else if (!strcmp(argv[1], "-e") ||
|
||||
!strcmp(argv[1], "--email"))
|
||||
log.email = 1;
|
||||
else if (!prefixcmp(argv[1], "-w")) {
|
||||
log.wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
|
||||
}
|
||||
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
|
||||
usage(shortlog_usage);
|
||||
else
|
||||
break;
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
init_revisions(&rev, prefix);
|
||||
argc = setup_revisions(argc, argv, &rev, NULL);
|
||||
if (argc > 1)
|
||||
die ("unrecognized argument: %s", argv[1]);
|
||||
parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
|
||||
PARSE_OPT_KEEP_ARGV0);
|
||||
|
||||
for (;;) {
|
||||
switch (parse_options_step(&ctx, options, shortlog_usage)) {
|
||||
case PARSE_OPT_HELP:
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
goto parse_done;
|
||||
}
|
||||
parse_revision_opt(&rev, &ctx, options, shortlog_usage);
|
||||
}
|
||||
parse_done:
|
||||
argc = parse_options_end(&ctx);
|
||||
|
||||
if (setup_revisions(argc, argv, &rev, NULL) != 1) {
|
||||
error("unrecognized argument: %s", argv[1]);
|
||||
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))
|
||||
|
||||
@@ -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,6 +242,15 @@ static int do_sign(struct strbuf *buffer)
|
||||
if (finish_command(&gpg) || !len || len < 0)
|
||||
return error("gpg failed to sign the tag");
|
||||
|
||||
/* 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);
|
||||
|
||||
13
bundle.c
13
bundle.c
@@ -178,6 +178,7 @@ int create_bundle(struct bundle_header *header, const char *path,
|
||||
int i, ref_count = 0;
|
||||
char buffer[1024];
|
||||
struct rev_info revs;
|
||||
int read_from_stdin = 0;
|
||||
struct child_process rls;
|
||||
FILE *rls_fout;
|
||||
|
||||
@@ -227,8 +228,16 @@ int create_bundle(struct bundle_header *header, const char *path,
|
||||
|
||||
/* write references */
|
||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||
if (argc > 1)
|
||||
return error("unrecognized argument: %s'", argv[1]);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--stdin")) {
|
||||
if (read_from_stdin++)
|
||||
die("--stdin given twice?");
|
||||
read_revisions_from_stdin(&revs);
|
||||
continue;
|
||||
}
|
||||
return error("unrecognized argument: %s'", argv[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < revs.pending.nr; i++) {
|
||||
struct object_array_entry *e = revs.pending.objects + i;
|
||||
|
||||
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 */
|
||||
|
||||
@@ -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"
|
||||
@@ -114,9 +119,20 @@ __git_ps1 ()
|
||||
fi
|
||||
}
|
||||
|
||||
__gitcomp_1 ()
|
||||
{
|
||||
local c IFS=' '$'\t'$'\n'
|
||||
for c in $1; do
|
||||
case "$c$2" in
|
||||
--*=*) printf %s$'\n' "$c$2" ;;
|
||||
*.) printf %s$'\n' "$c$2" ;;
|
||||
*) printf %s$'\n' "$c$2 " ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
__gitcomp ()
|
||||
{
|
||||
local all c s=$'\n' IFS=' '$'\t'$'\n'
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
if [ $# -gt 2 ]; then
|
||||
cur="$3"
|
||||
@@ -124,21 +140,14 @@ __gitcomp ()
|
||||
case "$cur" in
|
||||
--*=)
|
||||
COMPREPLY=()
|
||||
return
|
||||
;;
|
||||
*)
|
||||
for c in $1; do
|
||||
case "$c$4" in
|
||||
--*=*) all="$all$c$4$s" ;;
|
||||
*.) all="$all$c$4$s" ;;
|
||||
*) all="$all$c$4 $s" ;;
|
||||
esac
|
||||
done
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -P "$2" \
|
||||
-W "$(__gitcomp_1 "$1" "$4")" \
|
||||
-- "$cur"))
|
||||
;;
|
||||
esac
|
||||
IFS=$s
|
||||
COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur"))
|
||||
return
|
||||
}
|
||||
|
||||
__git_heads ()
|
||||
@@ -290,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,$,/,
|
||||
@@ -320,9 +343,6 @@ __git_complete_revlist ()
|
||||
cur="${cur#*..}"
|
||||
__gitcomp "$(__git_refs)" "$pfx" "$cur"
|
||||
;;
|
||||
*.)
|
||||
__gitcomp "$cur."
|
||||
;;
|
||||
*)
|
||||
__gitcomp "$(__git_refs)"
|
||||
;;
|
||||
@@ -467,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
|
||||
@@ -691,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
|
||||
@@ -867,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#+}"
|
||||
@@ -883,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
|
||||
@@ -904,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]}"
|
||||
@@ -1163,8 +1213,19 @@ _git_show ()
|
||||
_git_stash ()
|
||||
{
|
||||
local subcommands='save list show apply clear drop pop create'
|
||||
if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
|
||||
local subcommand="$(__git_find_subcommand "$subcommands")"
|
||||
if [ -z "$subcommand" ]; then
|
||||
__gitcomp "$subcommands"
|
||||
else
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
case "$subcommand,$cur" in
|
||||
save,--*)
|
||||
__gitcomp "--keep-index"
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1364,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 ;;
|
||||
@@ -1397,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))
|
||||
|
||||
@@ -61,6 +61,10 @@ static void gather_stats(const char *buf, unsigned long size, struct text_stat *
|
||||
else
|
||||
stats->printable++;
|
||||
}
|
||||
|
||||
/* If file ends with EOF then don't count this EOF as non-printable. */
|
||||
if (size >= 1 && buf[size-1] == '\032')
|
||||
stats->nonprintable--;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -18,6 +18,18 @@ my ($fraginfo_color) =
|
||||
$diff_use_color ? (
|
||||
$repo->get_color('color.diff.frag', 'cyan'),
|
||||
) : ();
|
||||
my ($diff_plain_color) =
|
||||
$diff_use_color ? (
|
||||
$repo->get_color('color.diff.plain', ''),
|
||||
) : ();
|
||||
my ($diff_old_color) =
|
||||
$diff_use_color ? (
|
||||
$repo->get_color('color.diff.old', 'red'),
|
||||
) : ();
|
||||
my ($diff_new_color) =
|
||||
$diff_use_color ? (
|
||||
$repo->get_color('color.diff.new', 'green'),
|
||||
) : ();
|
||||
|
||||
my $normal_color = $repo->get_color("", "reset");
|
||||
|
||||
@@ -42,7 +54,7 @@ sub colored {
|
||||
my $patch_mode;
|
||||
|
||||
sub run_cmd_pipe {
|
||||
if ($^O eq 'MSWin32') {
|
||||
if ($^O eq 'MSWin32' || $^O eq 'msys') {
|
||||
my @invalid = grep {m/[":*]/} @_;
|
||||
die "$^O does not support: @invalid\n" if @invalid;
|
||||
my @args = map { m/ /o ? "\"$_\"": $_ } @_;
|
||||
@@ -394,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;
|
||||
@@ -682,92 +694,104 @@ sub split_hunk {
|
||||
return @split;
|
||||
}
|
||||
|
||||
sub find_last_o_ctx {
|
||||
my ($it) = @_;
|
||||
my $text = $it->{TEXT};
|
||||
my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);
|
||||
my $i = @{$text};
|
||||
my $last_o_ctx = $o_ofs + $o_cnt;
|
||||
while (0 < --$i) {
|
||||
my $line = $text->[$i];
|
||||
if ($line =~ /^ /) {
|
||||
$last_o_ctx--;
|
||||
next;
|
||||
}
|
||||
last;
|
||||
}
|
||||
return $last_o_ctx;
|
||||
|
||||
sub color_diff {
|
||||
return map {
|
||||
colored((/^@/ ? $fraginfo_color :
|
||||
/^\+/ ? $diff_new_color :
|
||||
/^-/ ? $diff_old_color :
|
||||
$diff_plain_color),
|
||||
$_);
|
||||
} @_;
|
||||
}
|
||||
|
||||
sub merge_hunk {
|
||||
my ($prev, $this) = @_;
|
||||
my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
|
||||
parse_hunk_header($prev->{TEXT}[0]);
|
||||
my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
|
||||
parse_hunk_header($this->{TEXT}[0]);
|
||||
sub edit_hunk_manually {
|
||||
my ($oldtext) = @_;
|
||||
|
||||
my (@line, $i, $ofs, $o_cnt, $n_cnt);
|
||||
$ofs = $o0_ofs;
|
||||
$o_cnt = $n_cnt = 0;
|
||||
for ($i = 1; $i < @{$prev->{TEXT}}; $i++) {
|
||||
my $line = $prev->{TEXT}[$i];
|
||||
if ($line =~ /^\+/) {
|
||||
$n_cnt++;
|
||||
push @line, $line;
|
||||
next;
|
||||
}
|
||||
my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
|
||||
my $fh;
|
||||
open $fh, '>', $hunkfile
|
||||
or die "failed to open hunk edit file for writing: " . $!;
|
||||
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
|
||||
print $fh @$oldtext;
|
||||
print $fh <<EOF;
|
||||
# ---
|
||||
# To remove '-' lines, make them ' ' lines (context).
|
||||
# To remove '+' lines, delete them.
|
||||
# Lines starting with # will be removed.
|
||||
#
|
||||
# If the patch applies cleanly, the edited hunk will immediately be
|
||||
# marked for staging. If it does not apply cleanly, you will be given
|
||||
# an opportunity to edit again. If all lines of the hunk are removed,
|
||||
# then the edit is aborted and the hunk is left unchanged.
|
||||
EOF
|
||||
close $fh;
|
||||
|
||||
last if ($o1_ofs <= $ofs);
|
||||
my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor")
|
||||
|| $ENV{VISUAL} || $ENV{EDITOR} || "vi";
|
||||
system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
|
||||
|
||||
$o_cnt++;
|
||||
$ofs++;
|
||||
if ($line =~ /^ /) {
|
||||
$n_cnt++;
|
||||
}
|
||||
push @line, $line;
|
||||
open $fh, '<', $hunkfile
|
||||
or die "failed to open hunk edit file for reading: " . $!;
|
||||
my @newtext = grep { !/^#/ } <$fh>;
|
||||
close $fh;
|
||||
unlink $hunkfile;
|
||||
|
||||
# Abort if nothing remains
|
||||
if (!grep { /\S/ } @newtext) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
for ($i = 1; $i < @{$this->{TEXT}}; $i++) {
|
||||
my $line = $this->{TEXT}[$i];
|
||||
if ($line =~ /^\+/) {
|
||||
$n_cnt++;
|
||||
push @line, $line;
|
||||
next;
|
||||
}
|
||||
$ofs++;
|
||||
$o_cnt++;
|
||||
if ($line =~ /^ /) {
|
||||
$n_cnt++;
|
||||
}
|
||||
push @line, $line;
|
||||
# Reinsert the first hunk header if the user accidentally deleted it
|
||||
if ($newtext[0] !~ /^@/) {
|
||||
unshift @newtext, $oldtext->[0];
|
||||
}
|
||||
my $head = ("@@ -$o0_ofs" .
|
||||
(($o_cnt != 1) ? ",$o_cnt" : '') .
|
||||
" +$n0_ofs" .
|
||||
(($n_cnt != 1) ? ",$n_cnt" : '') .
|
||||
" @@\n");
|
||||
@{$prev->{TEXT}} = ($head, @line);
|
||||
return \@newtext;
|
||||
}
|
||||
|
||||
sub coalesce_overlapping_hunks {
|
||||
my (@in) = @_;
|
||||
my @out = ();
|
||||
sub diff_applies {
|
||||
my $fh;
|
||||
open $fh, '| git apply --recount --cached --check';
|
||||
for my $h (@_) {
|
||||
print $fh @{$h->{TEXT}};
|
||||
}
|
||||
return close $fh;
|
||||
}
|
||||
|
||||
my ($last_o_ctx);
|
||||
sub prompt_yesno {
|
||||
my ($prompt) = @_;
|
||||
while (1) {
|
||||
print colored $prompt_color, $prompt;
|
||||
my $line = <STDIN>;
|
||||
return 0 if $line =~ /^n/i;
|
||||
return 1 if $line =~ /^y/i;
|
||||
}
|
||||
}
|
||||
|
||||
for (grep { $_->{USE} } @in) {
|
||||
my $text = $_->{TEXT};
|
||||
my ($o_ofs) = parse_hunk_header($text->[0]);
|
||||
if (defined $last_o_ctx &&
|
||||
$o_ofs <= $last_o_ctx) {
|
||||
merge_hunk($out[-1], $_);
|
||||
sub edit_hunk_loop {
|
||||
my ($head, $hunk, $ix) = @_;
|
||||
my $text = $hunk->[$ix]->{TEXT};
|
||||
|
||||
while (1) {
|
||||
$text = edit_hunk_manually($text);
|
||||
if (!defined $text) {
|
||||
return undef;
|
||||
}
|
||||
my $newhunk = { TEXT => $text, USE => 1 };
|
||||
if (diff_applies($head,
|
||||
@{$hunk}[0..$ix-1],
|
||||
$newhunk,
|
||||
@{$hunk}[$ix+1..$#{$hunk}])) {
|
||||
$newhunk->{DISPLAY} = [color_diff(@{$text})];
|
||||
return $newhunk;
|
||||
}
|
||||
else {
|
||||
push @out, $_;
|
||||
prompt_yesno(
|
||||
'Your edited hunk does not apply. Edit again '
|
||||
. '(saying "no" discards!) [y/n]? '
|
||||
) or return undef;
|
||||
}
|
||||
$last_o_ctx = find_last_o_ctx($out[-1]);
|
||||
}
|
||||
return @out;
|
||||
}
|
||||
|
||||
sub help_patch_cmd {
|
||||
@@ -781,6 +805,7 @@ J - leave this hunk undecided, see next hunk
|
||||
k - leave this hunk undecided, see previous undecided hunk
|
||||
K - leave this hunk undecided, see previous hunk
|
||||
s - split the current hunk into smaller hunks
|
||||
e - manually edit the current hunk
|
||||
? - print help
|
||||
EOF
|
||||
}
|
||||
@@ -885,6 +910,7 @@ sub patch_update_file {
|
||||
if (hunk_splittable($hunk[$ix]{TEXT})) {
|
||||
$other .= '/s';
|
||||
}
|
||||
$other .= '/e';
|
||||
for (@{$hunk[$ix]{DISPLAY}}) {
|
||||
print;
|
||||
}
|
||||
@@ -949,6 +975,12 @@ sub patch_update_file {
|
||||
$num = scalar @hunk;
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^e/) {
|
||||
my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
|
||||
if (defined $newhunk) {
|
||||
splice @hunk, $ix, 1, $newhunk;
|
||||
}
|
||||
}
|
||||
else {
|
||||
help_patch_cmd($other);
|
||||
next;
|
||||
@@ -962,47 +994,21 @@ sub patch_update_file {
|
||||
}
|
||||
}
|
||||
|
||||
@hunk = coalesce_overlapping_hunks(@hunk);
|
||||
|
||||
my $n_lofs = 0;
|
||||
my @result = ();
|
||||
if ($mode->{USE}) {
|
||||
push @result, @{$mode->{TEXT}};
|
||||
}
|
||||
for (@hunk) {
|
||||
my $text = $_->{TEXT};
|
||||
my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
|
||||
parse_hunk_header($text->[0]);
|
||||
|
||||
if (!$_->{USE}) {
|
||||
# We would have added ($n_cnt - $o_cnt) lines
|
||||
# to the postimage if we were to use this hunk,
|
||||
# but we didn't. So the line number that the next
|
||||
# hunk starts at would be shifted by that much.
|
||||
$n_lofs -= ($n_cnt - $o_cnt);
|
||||
next;
|
||||
}
|
||||
else {
|
||||
if ($n_lofs) {
|
||||
$n_ofs += $n_lofs;
|
||||
$text->[0] = ("@@ -$o_ofs" .
|
||||
(($o_cnt != 1)
|
||||
? ",$o_cnt" : '') .
|
||||
" +$n_ofs" .
|
||||
(($n_cnt != 1)
|
||||
? ",$n_cnt" : '') .
|
||||
" @@\n");
|
||||
}
|
||||
for (@$text) {
|
||||
push @result, $_;
|
||||
}
|
||||
if ($_->{USE}) {
|
||||
push @result, @{$_->{TEXT}};
|
||||
}
|
||||
}
|
||||
|
||||
if (@result) {
|
||||
my $fh;
|
||||
|
||||
open $fh, '| git apply --cached';
|
||||
open $fh, '| git apply --cached --recount';
|
||||
for (@{$head->{TEXT}}, @result) {
|
||||
print $fh $_;
|
||||
}
|
||||
|
||||
12
git-am.sh
12
git-am.sh
@@ -5,7 +5,7 @@
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_SPEC="\
|
||||
git-am [options] <mbox>|<Maildir>...
|
||||
git-am [options] [<mbox>|<Maildir>...]
|
||||
git-am [options] --resolved
|
||||
git-am [options] --skip
|
||||
--
|
||||
@@ -30,7 +30,8 @@ set_reflog_action am
|
||||
require_work_tree
|
||||
cd_to_toplevel
|
||||
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
git var GIT_COMMITTER_IDENT >/dev/null ||
|
||||
die "You need to set your committer info first"
|
||||
|
||||
stop_here () {
|
||||
echo "$1" >"$dotest/next"
|
||||
@@ -118,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=
|
||||
@@ -194,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
|
||||
@@ -241,6 +242,7 @@ else
|
||||
: >"$dotest/rebasing"
|
||||
else
|
||||
: >"$dotest/applying"
|
||||
git update-ref ORIG_HEAD HEAD
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -323,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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -10,14 +10,28 @@
|
||||
# The original idea comes from Eric W. Biederman, in
|
||||
# http://article.gmane.org/gmane.comp.version-control.git/22407
|
||||
|
||||
USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
|
||||
[--onto <branch>] <upstream> [<branch>])'
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_SPEC="\
|
||||
git-rebase [-i] [options] [--] <upstream> [<branch>]
|
||||
git-rebase [-i] (--continue | --abort | --skip)
|
||||
--
|
||||
Available options are
|
||||
v,verbose display a diffstat of what changed upstream
|
||||
onto= rebase onto given branch instead of upstream
|
||||
p,preserve-merges try to recreate merges instead of ignoring them
|
||||
s,strategy= use the given merge strategy
|
||||
m,merge always used (no-op)
|
||||
i,interactive always used (no-op)
|
||||
Actions:
|
||||
continue continue rebasing process
|
||||
abort abort rebasing process and restore original branch
|
||||
skip skip current patch and continue rebasing process
|
||||
"
|
||||
|
||||
OPTIONS_SPEC=
|
||||
. 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
|
||||
@@ -25,10 +39,8 @@ SQUASH_MSG="$DOTEST"/message-squash
|
||||
REWRITTEN="$DOTEST"/rewritten
|
||||
PRESERVE_MERGES=
|
||||
STRATEGY=
|
||||
ONTO=
|
||||
VERBOSE=
|
||||
test -d "$REWRITTEN" && PRESERVE_MERGES=t
|
||||
test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
|
||||
test -f "$DOTEST"/verbose && VERBOSE=t
|
||||
|
||||
GIT_CHERRY_PICK_HELP=" After resolving the conflicts,
|
||||
mark the corrected paths with 'git add <paths>', and
|
||||
@@ -366,10 +378,27 @@ do_rest () {
|
||||
done
|
||||
}
|
||||
|
||||
# check if no other options are set
|
||||
is_standalone () {
|
||||
test $# -eq 2 -a "$2" = '--' &&
|
||||
test -z "$ONTO" &&
|
||||
test -z "$PRESERVE_MERGES" &&
|
||||
test -z "$STRATEGY" &&
|
||||
test -z "$VERBOSE"
|
||||
}
|
||||
|
||||
get_saved_options () {
|
||||
test -d "$REWRITTEN" && PRESERVE_MERGES=t
|
||||
test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
|
||||
test -f "$DOTEST"/verbose && VERBOSE=t
|
||||
}
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
--continue)
|
||||
is_standalone "$@" || usage
|
||||
get_saved_options
|
||||
comment_for_reflog continue
|
||||
|
||||
test -d "$DOTEST" || die "No interactive rebase running"
|
||||
@@ -402,6 +431,8 @@ do
|
||||
do_rest
|
||||
;;
|
||||
--abort)
|
||||
is_standalone "$@" || usage
|
||||
get_saved_options
|
||||
comment_for_reflog abort
|
||||
|
||||
git rerere clear
|
||||
@@ -419,6 +450,8 @@ do
|
||||
exit
|
||||
;;
|
||||
--skip)
|
||||
is_standalone "$@" || usage
|
||||
get_saved_options
|
||||
comment_for_reflog skip
|
||||
|
||||
git rerere clear
|
||||
@@ -426,7 +459,7 @@ do
|
||||
|
||||
output git reset --hard && do_rest
|
||||
;;
|
||||
-s|--strategy)
|
||||
-s)
|
||||
case "$#,$1" in
|
||||
*,*=*)
|
||||
STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
|
||||
@@ -437,25 +470,26 @@ do
|
||||
shift ;;
|
||||
esac
|
||||
;;
|
||||
-m|--merge)
|
||||
-m)
|
||||
# we use merge anyway
|
||||
;;
|
||||
-C*)
|
||||
die "Interactive rebase uses merge, so $1 does not make sense"
|
||||
;;
|
||||
-v|--verbose)
|
||||
-v)
|
||||
VERBOSE=t
|
||||
;;
|
||||
-p|--preserve-merges)
|
||||
-p)
|
||||
PRESERVE_MERGES=t
|
||||
;;
|
||||
-i|--interactive)
|
||||
-i)
|
||||
# yeah, we know
|
||||
;;
|
||||
''|-h)
|
||||
usage
|
||||
--onto)
|
||||
shift
|
||||
ONTO=$(git rev-parse --verify "$1") ||
|
||||
die "Does not point to a valid commit: $1"
|
||||
;;
|
||||
*)
|
||||
--)
|
||||
shift
|
||||
test $# -eq 1 -o $# -eq 2 || usage
|
||||
test -d "$DOTEST" &&
|
||||
die "Interactive rebase already started"
|
||||
|
||||
@@ -464,15 +498,6 @@ do
|
||||
|
||||
comment_for_reflog start
|
||||
|
||||
ONTO=
|
||||
case "$1" in
|
||||
--onto)
|
||||
ONTO=$(git rev-parse --verify "$2") ||
|
||||
die "Does not point to a valid commit: $2"
|
||||
shift; shift
|
||||
;;
|
||||
esac
|
||||
|
||||
require_clean_work_tree
|
||||
|
||||
UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
|
||||
@@ -549,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
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ merge_base=`git merge-base $baserev $headrev` ||
|
||||
die "fatal: No commits in common between $base and $head"
|
||||
|
||||
url=$(get_remote_url "$url")
|
||||
branch=$(git peek-remote "$url" \
|
||||
branch=$(git ls-remote "$url" \
|
||||
| sed -n -e "/^$headrev refs.heads./{
|
||||
s/^.* refs.heads.//
|
||||
p
|
||||
|
||||
43
git-stash.sh
43
git-stash.sh
@@ -86,6 +86,13 @@ create_stash () {
|
||||
}
|
||||
|
||||
save_stash () {
|
||||
keep_index=
|
||||
case "$1" in
|
||||
--keep-index)
|
||||
keep_index=t
|
||||
shift
|
||||
esac
|
||||
|
||||
stash_msg="$1"
|
||||
|
||||
if no_changes
|
||||
@@ -104,6 +111,13 @@ save_stash () {
|
||||
git update-ref -m "$stash_msg" $ref_stash $w_commit ||
|
||||
die "Cannot save the current status"
|
||||
printf 'Saved working directory and index state "%s"\n' "$stash_msg"
|
||||
|
||||
git reset --hard
|
||||
|
||||
if test -n "$keep_index" && test -n $i_tree
|
||||
then
|
||||
git read-tree --reset -u $i_tree
|
||||
fi
|
||||
}
|
||||
|
||||
have_stash () {
|
||||
@@ -153,7 +167,8 @@ apply_stash () {
|
||||
die "$*: no valid stashed state found"
|
||||
|
||||
unstashed_index_tree=
|
||||
if test -n "$unstash_index" && test "$b_tree" != "$i_tree"
|
||||
if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
|
||||
test "$c_tree" != "$i_tree"
|
||||
then
|
||||
git diff-tree --binary $s^2^..$s^2 | git apply --cached
|
||||
test $? -ne 0 &&
|
||||
@@ -218,6 +233,23 @@ drop_stash () {
|
||||
git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
|
||||
}
|
||||
|
||||
apply_to_branch () {
|
||||
have_stash || die 'Nothing to apply'
|
||||
|
||||
test -n "$1" || die 'No branch name specified'
|
||||
branch=$1
|
||||
|
||||
if test -z "$2"
|
||||
then
|
||||
set x "$ref_stash@{0}"
|
||||
fi
|
||||
stash=$2
|
||||
|
||||
git-checkout -b $branch $stash^ &&
|
||||
apply_stash --index $stash &&
|
||||
drop_stash $stash
|
||||
}
|
||||
|
||||
# Main command set
|
||||
case "$1" in
|
||||
list)
|
||||
@@ -235,7 +267,7 @@ show)
|
||||
;;
|
||||
save)
|
||||
shift
|
||||
save_stash "$*" && git-reset --hard
|
||||
save_stash "$*"
|
||||
;;
|
||||
apply)
|
||||
shift
|
||||
@@ -264,12 +296,15 @@ pop)
|
||||
drop_stash "$@"
|
||||
fi
|
||||
;;
|
||||
branch)
|
||||
shift
|
||||
apply_to_branch "$@"
|
||||
;;
|
||||
*)
|
||||
if test $# -eq 0
|
||||
then
|
||||
save_stash &&
|
||||
echo '(To restore them type "git stash apply")' &&
|
||||
git-reset --hard
|
||||
echo '(To restore them type "git stash apply")'
|
||||
else
|
||||
usage
|
||||
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 },
|
||||
|
||||
222
parse-options.c
222
parse-options.c
@@ -1,33 +1,10 @@
|
||||
#include "git-compat-util.h"
|
||||
#include "parse-options.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define OPT_SHORT 1
|
||||
#define OPT_UNSET 2
|
||||
|
||||
struct optparse_t {
|
||||
const char **argv;
|
||||
const char **out;
|
||||
int argc, cpidx;
|
||||
const char *opt;
|
||||
};
|
||||
|
||||
static inline const char *get_arg(struct optparse_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)
|
||||
@@ -37,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_value(struct optparse_t *p,
|
||||
const struct option *opt, int flags)
|
||||
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 char *s, *arg;
|
||||
const int unset = flags & OPT_UNSET;
|
||||
@@ -64,7 +57,6 @@ static int get_value(struct optparse_t *p,
|
||||
}
|
||||
}
|
||||
|
||||
arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
|
||||
switch (opt->type) {
|
||||
case OPTION_BIT:
|
||||
if (unset)
|
||||
@@ -86,29 +78,24 @@ static int get_value(struct optparse_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:
|
||||
if (unset)
|
||||
return (*opt->callback)(opt, NULL, 1);
|
||||
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_NOARG)
|
||||
return (*opt->callback)(opt, NULL, 0);
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
return (*opt->callback)(opt, NULL, 0);
|
||||
if (!arg)
|
||||
return opterror(opt, "requires a value", flags);
|
||||
return (*opt->callback)(opt, get_arg(p), 0);
|
||||
return (*opt->callback)(opt, NULL, 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) {
|
||||
@@ -119,9 +106,9 @@ static int get_value(struct optparse_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;
|
||||
@@ -131,7 +118,7 @@ static int get_value(struct optparse_t *p,
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_short_opt(struct optparse_t *p, const struct option *options)
|
||||
static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
|
||||
{
|
||||
for (; options->type != OPTION_END; options++) {
|
||||
if (options->short_name == *p->opt) {
|
||||
@@ -139,10 +126,10 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options)
|
||||
return get_value(p, options, OPT_SHORT);
|
||||
}
|
||||
}
|
||||
return error("unknown switch `%c'", *p->opt);
|
||||
return -2;
|
||||
}
|
||||
|
||||
static int parse_long_opt(struct optparse_t *p, const char *arg,
|
||||
static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
|
||||
const struct option *options)
|
||||
{
|
||||
const char *arg_end = strchr(arg, '=');
|
||||
@@ -224,7 +211,7 @@ is_abbreviated:
|
||||
abbrev_option->long_name);
|
||||
if (abbrev_option)
|
||||
return get_value(p, abbrev_option, abbrev_flags);
|
||||
return error("unknown option `%s'", arg);
|
||||
return -2;
|
||||
}
|
||||
|
||||
void check_typos(const char *arg, const struct option *options)
|
||||
@@ -247,67 +234,126 @@ void check_typos(const char *arg, const struct option *options)
|
||||
}
|
||||
}
|
||||
|
||||
static NORETURN void usage_with_options_internal(const char * const *,
|
||||
const struct option *, int);
|
||||
|
||||
int parse_options(int argc, const char **argv, const struct option *options,
|
||||
const char * const usagestr[], int flags)
|
||||
void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, int flags)
|
||||
{
|
||||
struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->argc = argc - 1;
|
||||
ctx->argv = argv + 1;
|
||||
ctx->out = argv;
|
||||
ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
|
||||
ctx->flags = flags;
|
||||
}
|
||||
|
||||
for (; args.argc; args.argc--, args.argv++) {
|
||||
const char *arg = args.argv[0];
|
||||
static int usage_with_options_internal(const char * const *,
|
||||
const struct option *, int);
|
||||
|
||||
int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[])
|
||||
{
|
||||
/* we must reset ->opt, unknown short option leave it dangling */
|
||||
ctx->opt = NULL;
|
||||
|
||||
for (; ctx->argc; ctx->argc--, ctx->argv++) {
|
||||
const char *arg = ctx->argv[0];
|
||||
|
||||
if (*arg != '-' || !arg[1]) {
|
||||
if (flags & PARSE_OPT_STOP_AT_NON_OPTION)
|
||||
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
|
||||
break;
|
||||
args.out[args.cpidx++] = args.argv[0];
|
||||
ctx->out[ctx->cpidx++] = ctx->argv[0];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg[1] != '-') {
|
||||
args.opt = arg + 1;
|
||||
if (*args.opt == 'h')
|
||||
usage_with_options(usagestr, options);
|
||||
if (parse_short_opt(&args, options) < 0)
|
||||
usage_with_options(usagestr, options);
|
||||
if (args.opt)
|
||||
ctx->opt = arg + 1;
|
||||
if (*ctx->opt == 'h')
|
||||
return parse_options_usage(usagestr, options);
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
case -2:
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
if (ctx->opt)
|
||||
check_typos(arg + 1, options);
|
||||
while (args.opt) {
|
||||
if (*args.opt == 'h')
|
||||
usage_with_options(usagestr, options);
|
||||
if (parse_short_opt(&args, options) < 0)
|
||||
usage_with_options(usagestr, options);
|
||||
while (ctx->opt) {
|
||||
if (*ctx->opt == 'h')
|
||||
return parse_options_usage(usagestr, options);
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
case -2:
|
||||
/* fake a short option thing to hide the fact that we may have
|
||||
* started to parse aggregated stuff
|
||||
*
|
||||
* This is leaky, too bad.
|
||||
*/
|
||||
ctx->argv[0] = xstrdup(ctx->opt - 1);
|
||||
*(char *)ctx->argv[0] = '-';
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!arg[2]) { /* "--" */
|
||||
if (!(flags & PARSE_OPT_KEEP_DASHDASH)) {
|
||||
args.argc--;
|
||||
args.argv++;
|
||||
if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
|
||||
ctx->argc--;
|
||||
ctx->argv++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(arg + 2, "help-all"))
|
||||
usage_with_options_internal(usagestr, options, 1);
|
||||
return usage_with_options_internal(usagestr, options, 1);
|
||||
if (!strcmp(arg + 2, "help"))
|
||||
usage_with_options(usagestr, options);
|
||||
if (parse_long_opt(&args, arg + 2, options))
|
||||
usage_with_options(usagestr, options);
|
||||
return parse_options_usage(usagestr, options);
|
||||
switch (parse_long_opt(ctx, arg + 2, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
case -2:
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return PARSE_OPT_DONE;
|
||||
}
|
||||
|
||||
int parse_options_end(struct parse_opt_ctx_t *ctx)
|
||||
{
|
||||
memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
|
||||
ctx->out[ctx->cpidx + ctx->argc] = NULL;
|
||||
return ctx->cpidx + ctx->argc;
|
||||
}
|
||||
|
||||
int parse_options(int argc, const char **argv, const struct option *options,
|
||||
const char * const usagestr[], int flags)
|
||||
{
|
||||
struct parse_opt_ctx_t ctx;
|
||||
|
||||
parse_options_start(&ctx, argc, argv, flags);
|
||||
switch (parse_options_step(&ctx, options, usagestr)) {
|
||||
case PARSE_OPT_HELP:
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
break;
|
||||
default: /* PARSE_OPT_UNKNOWN */
|
||||
if (ctx.argv[0][1] == '-') {
|
||||
error("unknown option `%s'", ctx.argv[0] + 2);
|
||||
} else {
|
||||
error("unknown switch `%c'", *ctx.opt);
|
||||
}
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
|
||||
memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
|
||||
args.out[args.cpidx + args.argc] = NULL;
|
||||
return args.cpidx + args.argc;
|
||||
return parse_options_end(&ctx);
|
||||
}
|
||||
|
||||
#define USAGE_OPTS_WIDTH 24
|
||||
#define USAGE_GAP 2
|
||||
|
||||
void usage_with_options_internal(const char * const *usagestr,
|
||||
const struct option *opts, int full)
|
||||
int usage_with_options_internal(const char * const *usagestr,
|
||||
const struct option *opts, int full)
|
||||
{
|
||||
fprintf(stderr, "usage: %s\n", *usagestr++);
|
||||
while (*usagestr && **usagestr)
|
||||
@@ -392,15 +438,23 @@ void usage_with_options_internal(const char * const *usagestr,
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
|
||||
exit(129);
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
|
||||
void usage_with_options(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
const struct option *opts)
|
||||
{
|
||||
usage_with_options_internal(usagestr, opts, 0);
|
||||
exit(129);
|
||||
}
|
||||
|
||||
int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
{
|
||||
return usage_with_options_internal(usagestr, opts, 0);
|
||||
}
|
||||
|
||||
|
||||
/*----- some often used options -----*/
|
||||
#include "cache.h"
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ enum parse_opt_type {
|
||||
enum parse_opt_flags {
|
||||
PARSE_OPT_KEEP_DASHDASH = 1,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION = 2,
|
||||
PARSE_OPT_KEEP_ARGV0 = 4,
|
||||
};
|
||||
|
||||
enum parse_opt_option_flags {
|
||||
@@ -27,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;
|
||||
@@ -111,6 +113,40 @@ extern int parse_options(int argc, const char **argv,
|
||||
extern NORETURN void usage_with_options(const char * const *usagestr,
|
||||
const struct option *options);
|
||||
|
||||
/*----- incremantal advanced APIs -----*/
|
||||
|
||||
enum {
|
||||
PARSE_OPT_HELP = -1,
|
||||
PARSE_OPT_DONE,
|
||||
PARSE_OPT_UNKNOWN,
|
||||
};
|
||||
|
||||
/*
|
||||
* It's okay for the caller to consume argv/argc in the usual way.
|
||||
* Other fields of that structure are private to parse-options and should not
|
||||
* be modified in any way.
|
||||
*/
|
||||
struct parse_opt_ctx_t {
|
||||
const char **argv;
|
||||
const char **out;
|
||||
int argc, cpidx;
|
||||
const char *opt;
|
||||
int flags;
|
||||
};
|
||||
|
||||
extern int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts);
|
||||
|
||||
extern void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, int flags);
|
||||
|
||||
extern int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[]);
|
||||
|
||||
extern int parse_options_end(struct parse_opt_ctx_t *ctx);
|
||||
|
||||
|
||||
/*----- some often used options -----*/
|
||||
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
|
||||
|
||||
2
path.c
2
path.c
@@ -272,7 +272,7 @@ int adjust_shared_perm(const char *path)
|
||||
int tweak = shared_repository;
|
||||
if (!(mode & S_IWUSR))
|
||||
tweak &= ~0222;
|
||||
mode = (mode & ~0777) | tweak;
|
||||
mode |= tweak;
|
||||
} else {
|
||||
/* Preserve old PERM_UMASK behaviour */
|
||||
if (mode & S_IWUSR)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
117
remote.c
117
remote.c
@@ -1,6 +1,9 @@
|
||||
#include "cache.h"
|
||||
#include "remote.h"
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
|
||||
static struct refspec s_tag_refspec = {
|
||||
0,
|
||||
@@ -1222,3 +1225,117 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if there is anything to report, otherwise false.
|
||||
*/
|
||||
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct commit *ours, *theirs;
|
||||
char symmetric[84];
|
||||
struct rev_info revs;
|
||||
const char *rev_argv[10], *base;
|
||||
int rev_argc;
|
||||
|
||||
/*
|
||||
* Nothing to report unless we are marked to build on top of
|
||||
* somebody else.
|
||||
*/
|
||||
if (!branch ||
|
||||
!branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If what we used to build on no longer exists, there is
|
||||
* nothing to report.
|
||||
*/
|
||||
base = branch->merge[0]->dst;
|
||||
if (!resolve_ref(base, sha1, 1, NULL))
|
||||
return 0;
|
||||
theirs = lookup_commit(sha1);
|
||||
if (!theirs)
|
||||
return 0;
|
||||
|
||||
if (!resolve_ref(branch->refname, sha1, 1, NULL))
|
||||
return 0;
|
||||
ours = lookup_commit(sha1);
|
||||
if (!ours)
|
||||
return 0;
|
||||
|
||||
/* are we the same? */
|
||||
if (theirs == ours)
|
||||
return 0;
|
||||
|
||||
/* Run "rev-list --left-right ours...theirs" internally... */
|
||||
rev_argc = 0;
|
||||
rev_argv[rev_argc++] = NULL;
|
||||
rev_argv[rev_argc++] = "--left-right";
|
||||
rev_argv[rev_argc++] = symmetric;
|
||||
rev_argv[rev_argc++] = "--";
|
||||
rev_argv[rev_argc] = NULL;
|
||||
|
||||
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
||||
strcpy(symmetric + 40, "...");
|
||||
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
||||
prepare_revision_walk(&revs);
|
||||
|
||||
/* ... and count the commits on each side. */
|
||||
*num_ours = 0;
|
||||
*num_theirs = 0;
|
||||
while (1) {
|
||||
struct commit *c = get_revision(&revs);
|
||||
if (!c)
|
||||
break;
|
||||
if (c->object.flags & SYMMETRIC_LEFT)
|
||||
(*num_ours)++;
|
||||
else
|
||||
(*num_theirs)++;
|
||||
}
|
||||
|
||||
/* clear object flags smudged by the above traversal */
|
||||
clear_commit_marks(ours, ALL_REV_FLAGS);
|
||||
clear_commit_marks(theirs, ALL_REV_FLAGS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when there is anything to report, otherwise false.
|
||||
*/
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb)
|
||||
{
|
||||
int num_ours, num_theirs;
|
||||
const char *base, *remote_msg;
|
||||
|
||||
if (!stat_tracking_info(branch, &num_ours, &num_theirs))
|
||||
return 0;
|
||||
|
||||
base = branch->merge[0]->dst;
|
||||
if (!prefixcmp(base, "refs/remotes/")) {
|
||||
remote_msg = " remote";
|
||||
base += strlen("refs/remotes/");
|
||||
} else {
|
||||
remote_msg = "";
|
||||
}
|
||||
if (!num_theirs)
|
||||
strbuf_addf(sb, "Your branch is ahead of the tracked%s branch '%s' "
|
||||
"by %d commit%s.\n",
|
||||
remote_msg, base,
|
||||
num_ours, (num_ours == 1) ? "" : "s");
|
||||
else if (!num_ours)
|
||||
strbuf_addf(sb, "Your branch is behind the tracked%s branch '%s' "
|
||||
"by %d commit%s,\n"
|
||||
"and can be fast-forwarded.\n",
|
||||
remote_msg, base,
|
||||
num_theirs, (num_theirs == 1) ? "" : "s");
|
||||
else
|
||||
strbuf_addf(sb, "Your branch and the tracked%s branch '%s' "
|
||||
"have diverged,\nand respectively "
|
||||
"have %d and %d different commit(s) each.\n",
|
||||
remote_msg, base,
|
||||
num_ours, num_theirs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
4
remote.h
4
remote.h
@@ -129,4 +129,8 @@ enum match_refs_flags {
|
||||
MATCH_REFS_MIRROR = (1 << 1),
|
||||
};
|
||||
|
||||
/* Reporting of tracking info */
|
||||
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb);
|
||||
|
||||
#endif
|
||||
|
||||
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
|
||||
621
revision.c
621
revision.c
@@ -10,6 +10,7 @@
|
||||
#include "grep.h"
|
||||
#include "reflog-walk.h"
|
||||
#include "patch-ids.h"
|
||||
#include "decorate.h"
|
||||
|
||||
volatile show_early_output_fn_t show_early_output;
|
||||
|
||||
@@ -412,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;
|
||||
@@ -445,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;
|
||||
}
|
||||
@@ -470,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;
|
||||
@@ -611,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);
|
||||
@@ -973,6 +990,226 @@ static void add_ignore_packed(struct rev_info *revs, const char *name)
|
||||
revs->ignore_packed[num] = NULL;
|
||||
}
|
||||
|
||||
static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
|
||||
int *unkc, const char **unkv)
|
||||
{
|
||||
const char *arg = argv[0];
|
||||
|
||||
/* pseudo revision arguments */
|
||||
if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
|
||||
!strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
|
||||
!strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
|
||||
!strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk"))
|
||||
{
|
||||
unkv[(*unkc)++] = arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!prefixcmp(arg, "--max-count=")) {
|
||||
revs->max_count = atoi(arg + 12);
|
||||
} else if (!prefixcmp(arg, "--skip=")) {
|
||||
revs->skip_count = atoi(arg + 7);
|
||||
} else if ((*arg == '-') && isdigit(arg[1])) {
|
||||
/* accept -<digit>, like traditional "head" */
|
||||
revs->max_count = atoi(arg + 1);
|
||||
} else if (!strcmp(arg, "-n")) {
|
||||
if (argc <= 1)
|
||||
return error("-n requires an argument");
|
||||
revs->max_count = atoi(argv[1]);
|
||||
return 2;
|
||||
} else if (!prefixcmp(arg, "-n")) {
|
||||
revs->max_count = atoi(arg + 2);
|
||||
} else if (!prefixcmp(arg, "--max-age=")) {
|
||||
revs->max_age = atoi(arg + 10);
|
||||
} else if (!prefixcmp(arg, "--since=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
} else if (!prefixcmp(arg, "--after=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
} else if (!prefixcmp(arg, "--min-age=")) {
|
||||
revs->min_age = atoi(arg + 10);
|
||||
} else if (!prefixcmp(arg, "--before=")) {
|
||||
revs->min_age = approxidate(arg + 9);
|
||||
} else if (!prefixcmp(arg, "--until=")) {
|
||||
revs->min_age = approxidate(arg + 8);
|
||||
} else if (!strcmp(arg, "--first-parent")) {
|
||||
revs->first_parent_only = 1;
|
||||
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
|
||||
init_reflog_walk(&revs->reflog_info);
|
||||
} else if (!strcmp(arg, "--default")) {
|
||||
if (argc <= 1)
|
||||
return error("bad --default argument");
|
||||
revs->def = argv[1];
|
||||
return 2;
|
||||
} else if (!strcmp(arg, "--merge")) {
|
||||
revs->show_merge = 1;
|
||||
} else if (!strcmp(arg, "--topo-order")) {
|
||||
revs->lifo = 1;
|
||||
revs->topo_order = 1;
|
||||
} else if (!strcmp(arg, "--date-order")) {
|
||||
revs->lifo = 0;
|
||||
revs->topo_order = 1;
|
||||
} else if (!prefixcmp(arg, "--early-output")) {
|
||||
int count = 100;
|
||||
switch (arg[14]) {
|
||||
case '=':
|
||||
count = atoi(arg+15);
|
||||
/* Fallthrough */
|
||||
case 0:
|
||||
revs->topo_order = 1;
|
||||
revs->early_output = count;
|
||||
}
|
||||
} else if (!strcmp(arg, "--parents")) {
|
||||
revs->rewrite_parents = 1;
|
||||
revs->print_parents = 1;
|
||||
} else if (!strcmp(arg, "--dense")) {
|
||||
revs->dense = 1;
|
||||
} else if (!strcmp(arg, "--sparse")) {
|
||||
revs->dense = 0;
|
||||
} else if (!strcmp(arg, "--show-all")) {
|
||||
revs->show_all = 1;
|
||||
} else if (!strcmp(arg, "--remove-empty")) {
|
||||
revs->remove_empty_trees = 1;
|
||||
} else if (!strcmp(arg, "--no-merges")) {
|
||||
revs->no_merges = 1;
|
||||
} else if (!strcmp(arg, "--boundary")) {
|
||||
revs->boundary = 1;
|
||||
} else if (!strcmp(arg, "--left-right")) {
|
||||
revs->left_right = 1;
|
||||
} else if (!strcmp(arg, "--cherry-pick")) {
|
||||
revs->cherry_pick = 1;
|
||||
revs->limited = 1;
|
||||
} else if (!strcmp(arg, "--objects")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
} else if (!strcmp(arg, "--objects-edge")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
revs->edge_hint = 1;
|
||||
} else if (!strcmp(arg, "--unpacked")) {
|
||||
revs->unpacked = 1;
|
||||
free(revs->ignore_packed);
|
||||
revs->ignore_packed = NULL;
|
||||
revs->num_ignore_packed = 0;
|
||||
} else if (!prefixcmp(arg, "--unpacked=")) {
|
||||
revs->unpacked = 1;
|
||||
add_ignore_packed(revs, arg+11);
|
||||
} else if (!strcmp(arg, "-r")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
} else if (!strcmp(arg, "-t")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
|
||||
} else if (!strcmp(arg, "-m")) {
|
||||
revs->ignore_merges = 0;
|
||||
} else if (!strcmp(arg, "-c")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 0;
|
||||
revs->combine_merges = 1;
|
||||
} else if (!strcmp(arg, "--cc")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 1;
|
||||
revs->combine_merges = 1;
|
||||
} else if (!strcmp(arg, "-v")) {
|
||||
revs->verbose_header = 1;
|
||||
} else if (!strcmp(arg, "--pretty")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+8, revs);
|
||||
} else if (!prefixcmp(arg, "--pretty=")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+9, revs);
|
||||
} else if (!strcmp(arg, "--graph")) {
|
||||
revs->topo_order = 1;
|
||||
revs->rewrite_parents = 1;
|
||||
revs->graph = graph_init(revs);
|
||||
} else if (!strcmp(arg, "--root")) {
|
||||
revs->show_root_diff = 1;
|
||||
} else if (!strcmp(arg, "--no-commit-id")) {
|
||||
revs->no_commit_id = 1;
|
||||
} else if (!strcmp(arg, "--always")) {
|
||||
revs->always_show_header = 1;
|
||||
} else if (!strcmp(arg, "--no-abbrev")) {
|
||||
revs->abbrev = 0;
|
||||
} else if (!strcmp(arg, "--abbrev")) {
|
||||
revs->abbrev = DEFAULT_ABBREV;
|
||||
} else if (!prefixcmp(arg, "--abbrev=")) {
|
||||
revs->abbrev = strtoul(arg + 9, NULL, 10);
|
||||
if (revs->abbrev < MINIMUM_ABBREV)
|
||||
revs->abbrev = MINIMUM_ABBREV;
|
||||
else if (revs->abbrev > 40)
|
||||
revs->abbrev = 40;
|
||||
} else if (!strcmp(arg, "--abbrev-commit")) {
|
||||
revs->abbrev_commit = 1;
|
||||
} else if (!strcmp(arg, "--full-diff")) {
|
||||
revs->diff = 1;
|
||||
revs->full_diff = 1;
|
||||
} else if (!strcmp(arg, "--full-history")) {
|
||||
revs->simplify_history = 0;
|
||||
} else if (!strcmp(arg, "--relative-date")) {
|
||||
revs->date_mode = DATE_RELATIVE;
|
||||
} else if (!strncmp(arg, "--date=", 7)) {
|
||||
revs->date_mode = parse_date_format(arg + 7);
|
||||
} else if (!strcmp(arg, "--log-size")) {
|
||||
revs->show_log_size = 1;
|
||||
}
|
||||
/*
|
||||
* Grepping the commit log
|
||||
*/
|
||||
else if (!prefixcmp(arg, "--author=")) {
|
||||
add_header_grep(revs, "author", arg+9);
|
||||
} else if (!prefixcmp(arg, "--committer=")) {
|
||||
add_header_grep(revs, "committer", arg+12);
|
||||
} else if (!prefixcmp(arg, "--grep=")) {
|
||||
add_message_grep(revs, arg+7);
|
||||
} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->regflags |= REG_EXTENDED;
|
||||
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->regflags |= REG_ICASE;
|
||||
} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->fixed = 1;
|
||||
} else if (!strcmp(arg, "--all-match")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->all_match = 1;
|
||||
} else if (!prefixcmp(arg, "--encoding=")) {
|
||||
arg += 11;
|
||||
if (strcmp(arg, "none"))
|
||||
git_log_output_encoding = xstrdup(arg);
|
||||
else
|
||||
git_log_output_encoding = "";
|
||||
} else if (!strcmp(arg, "--reverse")) {
|
||||
revs->reverse ^= 1;
|
||||
} else if (!strcmp(arg, "--children")) {
|
||||
revs->children.name = "children";
|
||||
revs->limited = 1;
|
||||
} else {
|
||||
int opts = diff_opt_parse(&revs->diffopt, argv, argc);
|
||||
if (!opts)
|
||||
unkv[(*unkc)++] = arg;
|
||||
return opts;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[])
|
||||
{
|
||||
int n = handle_revision_opt(revs, ctx->argc, ctx->argv,
|
||||
&ctx->cpidx, ctx->out);
|
||||
if (n <= 0) {
|
||||
error("unknown option `%s'", ctx->argv[0]);
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
ctx->argv += n;
|
||||
ctx->argc -= n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse revision information, filling in the "rev_info" structure,
|
||||
* and removing the used arguments from the argument list.
|
||||
@@ -982,12 +1219,7 @@ static void add_ignore_packed(struct rev_info *revs, const char *name)
|
||||
*/
|
||||
int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
|
||||
{
|
||||
int i, flags, seen_dashdash, show_merge;
|
||||
const char **unrecognized = argv + 1;
|
||||
int left = 1;
|
||||
int all_match = 0;
|
||||
int regflags = 0;
|
||||
int fixed = 0;
|
||||
int i, flags, left, seen_dashdash;
|
||||
|
||||
/* First, search for "--" */
|
||||
seen_dashdash = 0;
|
||||
@@ -1003,58 +1235,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
break;
|
||||
}
|
||||
|
||||
flags = show_merge = 0;
|
||||
for (i = 1; i < argc; i++) {
|
||||
/* Second, deal with arguments and options */
|
||||
flags = 0;
|
||||
for (left = i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (*arg == '-') {
|
||||
int opts;
|
||||
if (!prefixcmp(arg, "--max-count=")) {
|
||||
revs->max_count = atoi(arg + 12);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--skip=")) {
|
||||
revs->skip_count = atoi(arg + 7);
|
||||
continue;
|
||||
}
|
||||
/* accept -<digit>, like traditional "head" */
|
||||
if ((*arg == '-') && isdigit(arg[1])) {
|
||||
revs->max_count = atoi(arg + 1);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-n")) {
|
||||
if (argc <= i + 1)
|
||||
die("-n requires an argument");
|
||||
revs->max_count = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "-n")) {
|
||||
revs->max_count = atoi(arg + 2);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--max-age=")) {
|
||||
revs->max_age = atoi(arg + 10);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--since=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--after=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--min-age=")) {
|
||||
revs->min_age = atoi(arg + 10);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--before=")) {
|
||||
revs->min_age = approxidate(arg + 9);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--until=")) {
|
||||
revs->min_age = approxidate(arg + 8);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(arg, "--all")) {
|
||||
handle_refs(revs, flags, for_each_ref);
|
||||
continue;
|
||||
@@ -1071,265 +1258,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
handle_refs(revs, flags, for_each_remote_ref);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--first-parent")) {
|
||||
revs->first_parent_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--reflog")) {
|
||||
handle_reflog(revs, flags);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-g") ||
|
||||
!strcmp(arg, "--walk-reflogs")) {
|
||||
init_reflog_walk(&revs->reflog_info);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--not")) {
|
||||
flags ^= UNINTERESTING;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--default")) {
|
||||
if (++i >= argc)
|
||||
die("bad --default argument");
|
||||
def = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--merge")) {
|
||||
show_merge = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--topo-order")) {
|
||||
revs->lifo = 1;
|
||||
revs->topo_order = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--date-order")) {
|
||||
revs->lifo = 0;
|
||||
revs->topo_order = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--early-output")) {
|
||||
int count = 100;
|
||||
switch (arg[14]) {
|
||||
case '=':
|
||||
count = atoi(arg+15);
|
||||
/* Fallthrough */
|
||||
case 0:
|
||||
revs->topo_order = 1;
|
||||
revs->early_output = count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!strcmp(arg, "--parents")) {
|
||||
revs->rewrite_parents = 1;
|
||||
revs->print_parents = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--dense")) {
|
||||
revs->dense = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--sparse")) {
|
||||
revs->dense = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--show-all")) {
|
||||
revs->show_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--remove-empty")) {
|
||||
revs->remove_empty_trees = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-merges")) {
|
||||
revs->no_merges = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--boundary")) {
|
||||
revs->boundary = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--left-right")) {
|
||||
revs->left_right = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--cherry-pick")) {
|
||||
revs->cherry_pick = 1;
|
||||
revs->limited = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--objects")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--objects-edge")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
revs->edge_hint = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--unpacked")) {
|
||||
revs->unpacked = 1;
|
||||
free(revs->ignore_packed);
|
||||
revs->ignore_packed = NULL;
|
||||
revs->num_ignore_packed = 0;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--unpacked=")) {
|
||||
revs->unpacked = 1;
|
||||
add_ignore_packed(revs, arg+11);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-r")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-t")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-m")) {
|
||||
revs->ignore_merges = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-c")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 0;
|
||||
revs->combine_merges = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--cc")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 1;
|
||||
revs->combine_merges = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-v")) {
|
||||
revs->verbose_header = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--pretty")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+8, revs);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--pretty=")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+9, revs);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--graph")) {
|
||||
revs->topo_order = 1;
|
||||
revs->rewrite_parents = 1;
|
||||
revs->graph = graph_init(revs);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--root")) {
|
||||
revs->show_root_diff = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-commit-id")) {
|
||||
revs->no_commit_id = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--always")) {
|
||||
revs->always_show_header = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-abbrev")) {
|
||||
revs->abbrev = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--abbrev")) {
|
||||
revs->abbrev = DEFAULT_ABBREV;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--abbrev=")) {
|
||||
revs->abbrev = strtoul(arg + 9, NULL, 10);
|
||||
if (revs->abbrev < MINIMUM_ABBREV)
|
||||
revs->abbrev = MINIMUM_ABBREV;
|
||||
else if (revs->abbrev > 40)
|
||||
revs->abbrev = 40;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--abbrev-commit")) {
|
||||
revs->abbrev_commit = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--full-diff")) {
|
||||
revs->diff = 1;
|
||||
revs->full_diff = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--full-history")) {
|
||||
revs->simplify_history = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--relative-date")) {
|
||||
revs->date_mode = DATE_RELATIVE;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(arg, "--date=", 7)) {
|
||||
revs->date_mode = parse_date_format(arg + 7);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--log-size")) {
|
||||
revs->show_log_size = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Grepping the commit log
|
||||
*/
|
||||
if (!prefixcmp(arg, "--author=")) {
|
||||
add_header_grep(revs, "author", arg+9);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--committer=")) {
|
||||
add_header_grep(revs, "committer", arg+12);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--grep=")) {
|
||||
add_message_grep(revs, arg+7);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--extended-regexp") ||
|
||||
!strcmp(arg, "-E")) {
|
||||
regflags |= REG_EXTENDED;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--regexp-ignore-case") ||
|
||||
!strcmp(arg, "-i")) {
|
||||
regflags |= REG_ICASE;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--fixed-strings") ||
|
||||
!strcmp(arg, "-F")) {
|
||||
fixed = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all-match")) {
|
||||
all_match = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--encoding=")) {
|
||||
arg += 11;
|
||||
if (strcmp(arg, "none"))
|
||||
git_log_output_encoding = xstrdup(arg);
|
||||
else
|
||||
git_log_output_encoding = "";
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--reverse")) {
|
||||
revs->reverse ^= 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-walk")) {
|
||||
revs->no_walk = 1;
|
||||
continue;
|
||||
@@ -1339,13 +1275,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
continue;
|
||||
}
|
||||
|
||||
opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
|
||||
opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv);
|
||||
if (opts > 0) {
|
||||
i += opts - 1;
|
||||
continue;
|
||||
}
|
||||
*unrecognized++ = arg;
|
||||
left++;
|
||||
if (opts < 0)
|
||||
exit(128);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1369,21 +1305,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
}
|
||||
}
|
||||
|
||||
if (revs->grep_filter) {
|
||||
revs->grep_filter->regflags |= regflags;
|
||||
revs->grep_filter->fixed = fixed;
|
||||
}
|
||||
|
||||
if (show_merge)
|
||||
if (revs->def == NULL)
|
||||
revs->def = def;
|
||||
if (revs->show_merge)
|
||||
prepare_show_merge(revs);
|
||||
if (def && !revs->pending.nr) {
|
||||
if (revs->def && !revs->pending.nr) {
|
||||
unsigned char sha1[20];
|
||||
struct object *object;
|
||||
unsigned mode;
|
||||
if (get_sha1_with_mode(def, sha1, &mode))
|
||||
die("bad default revision '%s'", def);
|
||||
object = get_reference(revs, def, sha1, 0);
|
||||
add_pending_object_with_mode(revs, object, def, mode);
|
||||
if (get_sha1_with_mode(revs->def, sha1, &mode))
|
||||
die("bad default revision '%s'", revs->def);
|
||||
object = get_reference(revs, revs->def, sha1, 0);
|
||||
add_pending_object_with_mode(revs, object, revs->def, mode);
|
||||
}
|
||||
|
||||
/* Did the user ask for any diff output? Run the diff! */
|
||||
@@ -1417,12 +1350,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
die("diff_setup_done failed");
|
||||
|
||||
if (revs->grep_filter) {
|
||||
revs->grep_filter->all_match = all_match;
|
||||
compile_grep_patterns(revs->grep_filter);
|
||||
}
|
||||
|
||||
if (revs->reverse && revs->reflog_info)
|
||||
die("cannot combine --reverse with --walk-reflogs");
|
||||
if (revs->rewrite_parents && revs->children.name)
|
||||
die("cannot combine --parents and --children");
|
||||
|
||||
/*
|
||||
* Limitations on the graph functionality
|
||||
@@ -1436,6 +1370,26 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
return left;
|
||||
}
|
||||
|
||||
static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
|
||||
{
|
||||
struct commit_list *l = xcalloc(1, sizeof(*l));
|
||||
|
||||
l->item = child;
|
||||
l->next = add_decoration(&revs->children, &parent->object, l);
|
||||
}
|
||||
|
||||
static void set_children(struct rev_info *revs)
|
||||
{
|
||||
struct commit_list *l;
|
||||
for (l = revs->commits; l; l = l->next) {
|
||||
struct commit *commit = l->item;
|
||||
struct commit_list *p;
|
||||
|
||||
for (p = commit->parents; p; p = p->next)
|
||||
add_child(revs, p->item, commit);
|
||||
}
|
||||
}
|
||||
|
||||
int prepare_revision_walk(struct rev_info *revs)
|
||||
{
|
||||
int nr = revs->pending.nr;
|
||||
@@ -1464,6 +1418,8 @@ int prepare_revision_walk(struct rev_info *revs)
|
||||
return -1;
|
||||
if (revs->topo_order)
|
||||
sort_in_topological_order(&revs->commits, revs->lifo);
|
||||
if (revs->children.name)
|
||||
set_children(revs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1475,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;
|
||||
@@ -1541,6 +1499,11 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
|
||||
commit->buffer, strlen(commit->buffer));
|
||||
}
|
||||
|
||||
static inline int want_ancestry(struct rev_info *revs)
|
||||
{
|
||||
return (revs->rewrite_parents || revs->children.name);
|
||||
}
|
||||
|
||||
enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
|
||||
{
|
||||
if (commit->object.flags & SHOWN)
|
||||
@@ -1561,13 +1524,13 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
|
||||
/* Commit without changes? */
|
||||
if (commit->object.flags & TREESAME) {
|
||||
/* drop merges unless we want parenthood */
|
||||
if (!revs->rewrite_parents)
|
||||
if (!want_ancestry(revs))
|
||||
return commit_ignore;
|
||||
/* non-merge - always ignore it */
|
||||
if (!commit->parents || !commit->parents->next)
|
||||
return commit_ignore;
|
||||
}
|
||||
if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0)
|
||||
if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
|
||||
return commit_error;
|
||||
}
|
||||
return commit_show;
|
||||
@@ -1597,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef REVISION_H
|
||||
#define REVISION_H
|
||||
|
||||
#include "parse-options.h"
|
||||
|
||||
#define SEEN (1u<<0)
|
||||
#define UNINTERESTING (1u<<1)
|
||||
#define TREESAME (1u<<2)
|
||||
@@ -11,6 +13,7 @@
|
||||
#define ADDED (1u<<7) /* Parents already parsed and added? */
|
||||
#define SYMMETRIC_LEFT (1u<<8)
|
||||
#define TOPOSORT (1u<<9) /* In the active toposort list.. */
|
||||
#define ALL_REV_FLAGS ((1u<<10)-1)
|
||||
|
||||
struct rev_info;
|
||||
struct log_info;
|
||||
@@ -25,6 +28,7 @@ struct rev_info {
|
||||
|
||||
/* Basic information */
|
||||
const char *prefix;
|
||||
const char *def;
|
||||
void *prune_data;
|
||||
unsigned int early_output;
|
||||
|
||||
@@ -65,6 +69,7 @@ struct rev_info {
|
||||
|
||||
/* Format info */
|
||||
unsigned int shown_one:1,
|
||||
show_merge:1,
|
||||
abbrev_commit:1,
|
||||
use_terminator:1,
|
||||
missing_newline:1;
|
||||
@@ -104,6 +109,7 @@ struct rev_info {
|
||||
struct diff_options pruning;
|
||||
|
||||
struct reflog_walk_info *reflog_info;
|
||||
struct decoration children;
|
||||
};
|
||||
|
||||
#define REV_TREE_SAME 0
|
||||
@@ -118,6 +124,9 @@ volatile show_early_output_fn_t show_early_output;
|
||||
|
||||
extern void init_revisions(struct rev_info *revs, const char *prefix);
|
||||
extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
|
||||
extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[]);
|
||||
extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
|
||||
|
||||
extern int prepare_revision_walk(struct rev_info *revs);
|
||||
|
||||
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;
|
||||
|
||||
@@ -15,6 +15,7 @@ test_expect_success setup '
|
||||
|
||||
>file &&
|
||||
git add file &&
|
||||
test_tick &&
|
||||
git commit -m initial &&
|
||||
echo >file &&
|
||||
git add file
|
||||
@@ -24,11 +25,11 @@ test_expect_success setup '
|
||||
test_expect_success 'write-tree should notice unwritable repository' '
|
||||
|
||||
(
|
||||
chmod a-w .git/objects
|
||||
chmod a-w .git/objects .git/objects/?? &&
|
||||
test_must_fail git write-tree
|
||||
)
|
||||
status=$?
|
||||
chmod 775 .git/objects
|
||||
chmod 775 .git/objects .git/objects/??
|
||||
(exit $status)
|
||||
|
||||
'
|
||||
@@ -36,11 +37,11 @@ test_expect_success 'write-tree should notice unwritable repository' '
|
||||
test_expect_success 'commit should notice unwritable repository' '
|
||||
|
||||
(
|
||||
chmod a-w .git/objects
|
||||
chmod a-w .git/objects .git/objects/?? &&
|
||||
test_must_fail git commit -m second
|
||||
)
|
||||
status=$?
|
||||
chmod 775 .git/objects
|
||||
chmod 775 .git/objects .git/objects/??
|
||||
(exit $status)
|
||||
|
||||
'
|
||||
@@ -48,12 +49,12 @@ test_expect_success 'commit should notice unwritable repository' '
|
||||
test_expect_success 'update-index should notice unwritable repository' '
|
||||
|
||||
(
|
||||
echo a >file &&
|
||||
chmod a-w .git/objects
|
||||
echo 6O >file &&
|
||||
chmod a-w .git/objects .git/objects/?? &&
|
||||
test_must_fail git update-index file
|
||||
)
|
||||
status=$?
|
||||
chmod 775 .git/objects
|
||||
chmod 775 .git/objects .git/objects/??
|
||||
(exit $status)
|
||||
|
||||
'
|
||||
@@ -62,11 +63,11 @@ test_expect_success 'add should notice unwritable repository' '
|
||||
|
||||
(
|
||||
echo b >file &&
|
||||
chmod a-w .git/objects
|
||||
chmod a-w .git/objects .git/objects/?? &&
|
||||
test_must_fail git add file
|
||||
)
|
||||
status=$?
|
||||
chmod 775 .git/objects
|
||||
chmod 775 .git/objects .git/objects/??
|
||||
(exit $status)
|
||||
|
||||
'
|
||||
|
||||
@@ -17,6 +17,29 @@ test_expect_success 'shared = 0400 (faulty permission u-w)' '
|
||||
test $ret != "0"
|
||||
'
|
||||
|
||||
for u in 002 022
|
||||
do
|
||||
test_expect_success "shared=1 does not clear bits preset by umask $u" '
|
||||
mkdir sub && (
|
||||
cd sub &&
|
||||
umask $u &&
|
||||
git init --shared=1 &&
|
||||
test 1 = "$(git config core.sharedrepository)"
|
||||
) &&
|
||||
actual=$(ls -l sub/.git/HEAD)
|
||||
case "$actual" in
|
||||
-rw-rw-r--*)
|
||||
: happy
|
||||
;;
|
||||
*)
|
||||
echo Oops, .git/HEAD is not 0664 but $actual
|
||||
false
|
||||
;;
|
||||
esac
|
||||
'
|
||||
rm -rf sub
|
||||
done
|
||||
|
||||
test_expect_success 'shared=all' '
|
||||
mkdir sub &&
|
||||
cd sub &&
|
||||
|
||||
@@ -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' '
|
||||
@@ -211,7 +211,7 @@ test_expect_success 'preserve merges with -p' '
|
||||
git add unrelated-file &&
|
||||
test_tick &&
|
||||
git commit -m "unrelated" &&
|
||||
git checkout -b to-be-rebased master &&
|
||||
git checkout -b another-branch master &&
|
||||
echo B > file1 &&
|
||||
test_tick &&
|
||||
git commit -m J file1 &&
|
||||
@@ -220,12 +220,28 @@ test_expect_success 'preserve merges with -p' '
|
||||
echo C > file1 &&
|
||||
test_tick &&
|
||||
git commit -m K file1 &&
|
||||
echo D > file1 &&
|
||||
test_tick &&
|
||||
git commit -m L1 file1 &&
|
||||
git checkout HEAD^ &&
|
||||
echo 1 > unrelated-file &&
|
||||
test_tick &&
|
||||
git commit -m L2 unrelated-file &&
|
||||
test_tick &&
|
||||
git merge another-branch &&
|
||||
echo E > file1 &&
|
||||
test_tick &&
|
||||
git commit -m M file1 &&
|
||||
git checkout -b to-be-rebased &&
|
||||
test_tick &&
|
||||
git rebase -i -p --onto branch1 master &&
|
||||
test $(git rev-parse HEAD^^2) = $(git rev-parse to-be-preserved) &&
|
||||
test $(git rev-parse HEAD~3) = $(git rev-parse branch1) &&
|
||||
test $(git show HEAD:file1) = C &&
|
||||
test $(git show HEAD~2:file1) = B
|
||||
test $(git rev-parse HEAD~6) = $(git rev-parse branch1) &&
|
||||
test $(git rev-parse HEAD~4^2) = $(git rev-parse to-be-preserved) &&
|
||||
test $(git rev-parse HEAD^^2^) = $(git rev-parse HEAD^^^) &&
|
||||
test $(git show HEAD~5:file1) = B &&
|
||||
test $(git show HEAD~3:file1) = C &&
|
||||
test $(git show HEAD:file1) = E &&
|
||||
test $(git show HEAD:unrelated-file) = 1
|
||||
'
|
||||
|
||||
test_expect_success '--continue tries to commit' '
|
||||
|
||||
@@ -74,7 +74,7 @@ testrebase() {
|
||||
'
|
||||
}
|
||||
|
||||
testrebase "" .dotest
|
||||
testrebase " --merge" .git/.dotest-merge
|
||||
testrebase "" .git/rebase
|
||||
testrebase " --merge" .git/rebase-merge
|
||||
|
||||
test_done
|
||||
|
||||
30
t/t3503-cherry-pick-root.sh
Executable file
30
t/t3503-cherry-pick-root.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test cherry-picking a root commit'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
echo first > file1 &&
|
||||
git add file1 &&
|
||||
test_tick &&
|
||||
git commit -m "first" &&
|
||||
|
||||
git symbolic-ref HEAD refs/heads/second &&
|
||||
rm .git/index file1 &&
|
||||
echo second > file2 &&
|
||||
git add file2 &&
|
||||
test_tick &&
|
||||
git commit -m "second"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick a root commit' '
|
||||
|
||||
git cherry-pick master &&
|
||||
test first = $(cat file1)
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -66,6 +66,73 @@ test_expect_success 'revert works (commit)' '
|
||||
grep "unchanged *+3/-0 file" output
|
||||
'
|
||||
|
||||
cat >expected <<EOF
|
||||
EOF
|
||||
cat >fake_editor.sh <<EOF
|
||||
EOF
|
||||
chmod a+x fake_editor.sh
|
||||
test_set_editor "$(pwd)/fake_editor.sh"
|
||||
test_expect_success 'dummy edit works' '
|
||||
(echo e; echo a) | git add -p &&
|
||||
git diff > diff &&
|
||||
test_cmp expected diff
|
||||
'
|
||||
|
||||
cat >patch <<EOF
|
||||
@@ -1,1 +1,4 @@
|
||||
this
|
||||
+patch
|
||||
-doesn't
|
||||
apply
|
||||
EOF
|
||||
echo "#!$SHELL_PATH" >fake_editor.sh
|
||||
cat >>fake_editor.sh <<\EOF
|
||||
mv -f "$1" oldpatch &&
|
||||
mv -f patch "$1"
|
||||
EOF
|
||||
chmod a+x fake_editor.sh
|
||||
test_set_editor "$(pwd)/fake_editor.sh"
|
||||
test_expect_success 'bad edit rejected' '
|
||||
git reset &&
|
||||
(echo e; echo n; echo d) | git add -p >output &&
|
||||
grep "hunk does not apply" output
|
||||
'
|
||||
|
||||
cat >patch <<EOF
|
||||
this patch
|
||||
is garbage
|
||||
EOF
|
||||
test_expect_success 'garbage edit rejected' '
|
||||
git reset &&
|
||||
(echo e; echo n; echo d) | git add -p >output &&
|
||||
grep "hunk does not apply" output
|
||||
'
|
||||
|
||||
cat >patch <<EOF
|
||||
@@ -1,0 +1,0 @@
|
||||
baseline
|
||||
+content
|
||||
+newcontent
|
||||
+lines
|
||||
EOF
|
||||
cat >expected <<EOF
|
||||
diff --git a/file b/file
|
||||
index b5dd6c9..f910ae9 100644
|
||||
--- a/file
|
||||
+++ b/file
|
||||
@@ -1,4 +1,4 @@
|
||||
baseline
|
||||
content
|
||||
-newcontent
|
||||
+more
|
||||
lines
|
||||
EOF
|
||||
test_expect_success 'real edit works' '
|
||||
(echo e; echo n; echo d) | git add -p &&
|
||||
git diff >output &&
|
||||
test_cmp expected output
|
||||
'
|
||||
|
||||
if test "$(git config --bool core.filemode)" = false
|
||||
then
|
||||
say 'skipping filemode tests (filesystem does not properly support modes)'
|
||||
|
||||
@@ -125,4 +125,64 @@ $test_expect 'stash pop' '
|
||||
test 0 = $(git stash list | wc -l)
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
diff --git a/file2 b/file2
|
||||
new file mode 100644
|
||||
index 0000000..1fe912c
|
||||
--- /dev/null
|
||||
+++ b/file2
|
||||
@@ -0,0 +1 @@
|
||||
+bar2
|
||||
EOF
|
||||
|
||||
cat > expect1 << EOF
|
||||
diff --git a/file b/file
|
||||
index 257cc56..5716ca5 100644
|
||||
--- a/file
|
||||
+++ b/file
|
||||
@@ -1 +1 @@
|
||||
-foo
|
||||
+bar
|
||||
EOF
|
||||
|
||||
cat > expect2 << EOF
|
||||
diff --git a/file b/file
|
||||
index 7601807..5716ca5 100644
|
||||
--- a/file
|
||||
+++ b/file
|
||||
@@ -1 +1 @@
|
||||
-baz
|
||||
+bar
|
||||
diff --git a/file2 b/file2
|
||||
new file mode 100644
|
||||
index 0000000..1fe912c
|
||||
--- /dev/null
|
||||
+++ b/file2
|
||||
@@ -0,0 +1 @@
|
||||
+bar2
|
||||
EOF
|
||||
|
||||
test_expect_success 'stash branch' '
|
||||
echo foo > file &&
|
||||
git commit file -m first
|
||||
echo bar > file &&
|
||||
echo bar2 > file2 &&
|
||||
git add file2 &&
|
||||
git stash &&
|
||||
echo baz > file &&
|
||||
git commit file -m second &&
|
||||
git stash branch stashbranch &&
|
||||
test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
|
||||
test $(git rev-parse HEAD) = $(git rev-parse master^) &&
|
||||
git diff --cached > output &&
|
||||
test_cmp output expect &&
|
||||
git diff > output &&
|
||||
test_cmp output expect1 &&
|
||||
git add file &&
|
||||
git commit -m alternate\ second &&
|
||||
git diff master..stashbranch > output &&
|
||||
test_cmp output expect2 &&
|
||||
test 0 = $(git stash list | wc -l)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
70
t/t6040-tracking-info.sh
Executable file
70
t/t6040-tracking-info.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='remote tracking stats'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
advance () {
|
||||
echo "$1" >"$1" &&
|
||||
git add "$1" &&
|
||||
test_tick &&
|
||||
git commit -m "$1"
|
||||
}
|
||||
|
||||
test_expect_success setup '
|
||||
for i in a b c;
|
||||
do
|
||||
advance $i || break
|
||||
done &&
|
||||
git clone . test &&
|
||||
(
|
||||
cd test &&
|
||||
git checkout -b b1 origin &&
|
||||
git reset --hard HEAD^ &&
|
||||
advance d &&
|
||||
git checkout -b b2 origin &&
|
||||
git reset --hard b1 &&
|
||||
git checkout -b b3 origin &&
|
||||
git reset --hard HEAD^ &&
|
||||
git checkout -b b4 origin &&
|
||||
advance e &&
|
||||
advance f
|
||||
)
|
||||
'
|
||||
|
||||
script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
|
||||
cat >expect <<\EOF
|
||||
b1 ahead 1, behind 1
|
||||
b2 ahead 1, behind 1
|
||||
b3 behind 1
|
||||
b4 ahead 2
|
||||
EOF
|
||||
|
||||
test_expect_success 'branch -v' '
|
||||
(
|
||||
cd test &&
|
||||
git branch -v
|
||||
) |
|
||||
sed -n -e "$script" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'checkout' '
|
||||
(
|
||||
cd test && git checkout b1
|
||||
) >actual &&
|
||||
grep -e "have 1 and 1 different" actual
|
||||
'
|
||||
|
||||
test_expect_success 'status' '
|
||||
(
|
||||
cd test &&
|
||||
git checkout b1 >/dev/null &&
|
||||
# reports nothing to commit
|
||||
test_must_fail git status
|
||||
) >actual &&
|
||||
grep -e "have 1 and 1 different" actual
|
||||
'
|
||||
|
||||
|
||||
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
|
||||
;;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user