mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'junio/master' into devel
Conflicts: Makefile
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -82,6 +82,7 @@ git-mktag
|
||||
git-mktree
|
||||
git-name-rev
|
||||
git-mv
|
||||
git-notes
|
||||
git-pack-redundant
|
||||
git-pack-objects
|
||||
git-pack-refs
|
||||
@@ -153,6 +154,7 @@ test-match-trees
|
||||
test-parse-options
|
||||
test-path-utils
|
||||
test-sha1
|
||||
test-sigchain
|
||||
common-cmds.h
|
||||
*.tar.gz
|
||||
*.dsc
|
||||
|
||||
39
Documentation/RelNotes-1.6.1.2.txt
Normal file
39
Documentation/RelNotes-1.6.1.2.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
GIT v1.6.1.2 Release Notes
|
||||
==========================
|
||||
|
||||
Fixes since v1.6.1.1
|
||||
--------------------
|
||||
|
||||
* The logic for rename detectin in internal diff used by commands like
|
||||
"git diff" and "git blame" have been optimized to avoid loading the same
|
||||
blob repeatedly.
|
||||
|
||||
* We did not allow writing out a blob that is larger than 2GB for no good
|
||||
reason.
|
||||
|
||||
* "git format-patch -o $dir", when $dir is a relative directory, used it
|
||||
as relative to the root of the work tree, not relative to the current
|
||||
directory.
|
||||
|
||||
* v1.6.1 introduced an optimization for "git push" into a repository (A)
|
||||
that borrows its objects from another repository (B) to avoid sending
|
||||
objects that are available in repository B, when they are not yet used
|
||||
by repository A. However the code on the "git push" sender side was
|
||||
buggy and did not work when repository B had new objects that are not
|
||||
known by the sender. This caused pushing into a "forked" repository
|
||||
served by v1.6.1 software using "git push" from v1.6.1 sometimes did not
|
||||
work. The bug was purely on the "git push" sender side, and has been
|
||||
corrected.
|
||||
|
||||
* "git status -v" did not paint its diff output in colour even when
|
||||
color.ui configuration was set.
|
||||
|
||||
* "git ls-tree" learned --full-tree option to help Porcelain scripts that
|
||||
want to always see the full path regardless of the current working
|
||||
directory.
|
||||
|
||||
* "git grep" incorrectly searched in work tree paths even when they are
|
||||
marked as assume-unchanged. It now searches in the index entries.
|
||||
|
||||
* "git gc" with no grace period needlessly ejected packed but unreachable
|
||||
objects in their loose form, only to delete them right away.
|
||||
@@ -6,6 +6,11 @@ Updates since v1.6.1
|
||||
|
||||
(subsystems)
|
||||
|
||||
* git-svn updates.
|
||||
|
||||
* gitweb updates, including a new patch view and RSS/Atom feed
|
||||
improvements.
|
||||
|
||||
(portability)
|
||||
|
||||
(performance)
|
||||
@@ -15,25 +20,63 @@ Updates since v1.6.1
|
||||
|
||||
(usability, bells and whistles)
|
||||
|
||||
* "git-add -p" learned 'g'oto action to jump directly to a hunk.
|
||||
* automatic typo correction works on aliases as well
|
||||
|
||||
* git-cherry defaults to HEAD when the <upstream> argument is not given.
|
||||
* @{-1} is a way to refer to the last branch you were on. This is
|
||||
accepted not only where an object name is expected, but anywhere
|
||||
a branch name is expected. E.g. "git branch --track mybranch @{-1}"
|
||||
"git rev-parse --symbolic-full-name @{-1}".
|
||||
|
||||
* git-cvsserver can be told not to add extra "via git-CVS emulator" to the
|
||||
commit log message it serves via gitcvs.commitmsgannotation configuration.
|
||||
* "git add -p" learned 'g'oto action to jump directly to a hunk.
|
||||
|
||||
* git-diff learned a new option --inter-hunk-context to coalesce close
|
||||
* when "git am" stops upon a patch that does not apply, it shows the
|
||||
title of the offending patch.
|
||||
|
||||
* "git am --directory=<dir>" and "git am --reject" passes these options
|
||||
to underlying "git apply".
|
||||
|
||||
* "git clone" now makes its best effort when cloning from an empty
|
||||
repository to set up configuration variables to refer to the remote
|
||||
repository.
|
||||
|
||||
* "git checkout -" is a shorthand for "git checkout @{-1}".
|
||||
|
||||
* "git cherry" defaults to whatever the current branch is tracking (if
|
||||
exists) when the <upstream> argument is not given.
|
||||
|
||||
* "git cvsserver" can be told not to add extra "via git-CVS emulator" to
|
||||
the commit log message it serves via gitcvs.commitmsgannotation
|
||||
configuration.
|
||||
|
||||
* "git diff" learned a new option --inter-hunk-context to coalesce close
|
||||
hunks together and show context between them.
|
||||
|
||||
* git-filter-branch learned --prune-empty option that discards commits
|
||||
* The definition of what constitutes a word for "git diff --color-words"
|
||||
can be customized via gitattributes, command line or a configuration.
|
||||
|
||||
* "git diff" learned --patience to run "patience diff" algorithm.
|
||||
|
||||
* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
|
||||
not work as expected.
|
||||
|
||||
* "git filter-branch" learned --prune-empty option that discards commits
|
||||
that do not change the contents.
|
||||
|
||||
* git-ls-tree learned --full-tree option that shows the path in full
|
||||
* "git grep -w" and "git grep" for fixed strings have been optimized.
|
||||
|
||||
* "git log" and friends include HEAD to the set of starting points
|
||||
when --all is given. This makes a difference when you are not on
|
||||
any branch.
|
||||
|
||||
* "git ls-tree" learned --full-tree option that shows the path in full
|
||||
regardless of where in the work tree hierarchy the command was started.
|
||||
|
||||
* git-mergetool learned -y(--no-prompt) option to disable prompting.
|
||||
* "git mergetool" learned -y(--no-prompt) option to disable prompting.
|
||||
|
||||
* "git-reset --merge" is a new mode that works similar to the way
|
||||
* "git rebase -i" can transplant a history down to root to elsewhere
|
||||
with --root option.
|
||||
|
||||
* "git reset --merge" is a new mode that works similar to the way
|
||||
"git checkout" switches branches, taking the local changes while
|
||||
switching to another commit.
|
||||
|
||||
@@ -52,14 +95,12 @@ release, unless otherwise noted.
|
||||
* git-bundle did not exclude annotated tags even when a range given from the
|
||||
command line wanted to.
|
||||
|
||||
* git-grep did not work correctly for index entries with assume-unchanged bit.
|
||||
|
||||
* branch switching and merges had a silly bug that did not validate
|
||||
the correct directory when making sure an existing subdirectory is
|
||||
clean.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.6.1-134-ge98c6a1
|
||||
O=v1.6.1.2-252-g8c95d3c
|
||||
echo O=$(git describe master)
|
||||
git shortlog --no-merges $O..master ^maint
|
||||
|
||||
@@ -422,6 +422,19 @@ relatively high IO latencies. With this set to 'true', git will do the
|
||||
index comparison to the filesystem data in parallel, allowing
|
||||
overlapping IO's.
|
||||
|
||||
core.notesRef::
|
||||
When showing commit messages, also show notes which are stored in
|
||||
the given ref. This ref is expected to contain files named
|
||||
after the full SHA-1 of the commit they annotate.
|
||||
+
|
||||
If such a file exists in the given ref, the referenced blob is read, and
|
||||
appended to the commit message, separated by a "Notes:" line. If the
|
||||
given ref itself does not exist, it is not an error, but means that no
|
||||
notes should be printed.
|
||||
+
|
||||
This setting defaults to "refs/notes/commits", and can be overridden by
|
||||
the `GIT_NOTES_REF` environment variable.
|
||||
|
||||
alias.*::
|
||||
Command aliases for the linkgit:git[1] command wrapper - e.g.
|
||||
after defining "alias.last = cat-file commit HEAD", the invocation
|
||||
|
||||
@@ -10,7 +10,8 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
|
||||
[--3way] [--interactive]
|
||||
[--3way] [--interactive] [--committer-date-is-author-date]
|
||||
[--ignore-date]
|
||||
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
|
||||
[--reject]
|
||||
[<mbox> | <Maildir>...]
|
||||
@@ -73,6 +74,20 @@ default. You could use `--no-utf8` to override this.
|
||||
--interactive::
|
||||
Run interactively.
|
||||
|
||||
--committer-date-is-author-date::
|
||||
By default the command records the date from the e-mail
|
||||
message as the commit author date, and uses the time of
|
||||
commit creation as the committer date. This allows the
|
||||
user to lie about the committer date by using the same
|
||||
timestamp as the author date.
|
||||
|
||||
--ignore-date::
|
||||
By default the command records the date from the e-mail
|
||||
message as the commit author date, and uses the time of
|
||||
commit creation as the committer date. This allows the
|
||||
user to lie about author timestamp by using the same
|
||||
timestamp as the committer date.
|
||||
|
||||
--skip::
|
||||
Skip the current patch. This is only meaningful when
|
||||
restarting an aborted patch.
|
||||
|
||||
@@ -84,7 +84,7 @@ defining the basis. More than one reference may be packaged, and more
|
||||
than one basis can be specified. The objects packaged are those not
|
||||
contained in the union of the given bases. Each basis can be
|
||||
specified explicitly (e.g., ^master~10), or implicitly (e.g.,
|
||||
master~10..master, master --since=10.days.ago).
|
||||
master~10..master, --since=10.days.ago master).
|
||||
|
||||
It is very important that the basis used be held by the destination.
|
||||
It is okay to err on the side of conservatism, causing the bundle file
|
||||
@@ -94,75 +94,111 @@ when unpacking at the destination.
|
||||
EXAMPLE
|
||||
-------
|
||||
|
||||
Assume two repositories exist as R1 on machine A, and R2 on machine B.
|
||||
Assume you want to transfer the history from a repository R1 on machine A
|
||||
to another repository R2 on machine B.
|
||||
For whatever reason, direct connection between A and B is not allowed,
|
||||
but we can move data from A to B via some mechanism (CD, email, etc).
|
||||
We want to update R2 with developments made on branch master in R1.
|
||||
|
||||
To create the bundle you have to specify the basis. You have some options:
|
||||
To bootstrap the process, you can first create a bundle that doesn't have
|
||||
any basis. You can use a tag to remember up to what commit you sent out
|
||||
in order to make it easy to later update the other repository with
|
||||
incremental bundle,
|
||||
|
||||
- Without basis.
|
||||
+
|
||||
This is useful when sending the whole history.
|
||||
----------------
|
||||
machineA$ cd R1
|
||||
machineA$ git bundle create file.bdl master
|
||||
machineA$ git tag -f lastR2bundle master
|
||||
----------------
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master
|
||||
------------
|
||||
Then you sneakernet file.bdl to the target machine B. Because you don't
|
||||
have to have any object to extract objects from such a bundle, not only
|
||||
you can fetch/pull from a bundle, you can clone from it as if it was a
|
||||
remote repository.
|
||||
|
||||
- Using temporally tags.
|
||||
+
|
||||
We set a tag in R1 (lastR2bundle) after the previous such transport,
|
||||
and move it afterwards to help build the bundle.
|
||||
----------------
|
||||
machineB$ git clone /home/me/tmp/file.bdl R2
|
||||
----------------
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master ^lastR2bundle
|
||||
$ git tag -f lastR2bundle master
|
||||
------------
|
||||
|
||||
- Using a tag present in both repositories
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master ^v1.0.0
|
||||
------------
|
||||
|
||||
- A basis based on time.
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master --since=10.days.ago
|
||||
------------
|
||||
|
||||
- With a limit on the number of commits
|
||||
|
||||
------------
|
||||
$ git bundle create mybundle master -n 10
|
||||
------------
|
||||
|
||||
Then you move mybundle from A to B, and in R2 on B:
|
||||
|
||||
------------
|
||||
$ git bundle verify mybundle
|
||||
$ git fetch mybundle master:localRef
|
||||
------------
|
||||
|
||||
With something like this in the config in R2:
|
||||
This will define a remote called "origin" in the resulting repository that
|
||||
lets you fetch and pull from the bundle. $GIT_DIR/config file in R2 may
|
||||
have an entry like this:
|
||||
|
||||
------------------------
|
||||
[remote "bundle"]
|
||||
[remote "origin"]
|
||||
url = /home/me/tmp/file.bdl
|
||||
fetch = refs/heads/*:refs/remotes/origin/*
|
||||
------------------------
|
||||
|
||||
You can first sneakernet the bundle file to ~/tmp/file.bdl and
|
||||
then these commands on machine B:
|
||||
You can fetch/pull to update the resulting mine.git repository after
|
||||
replacing the bundle you store at /home/me/tmp/file.bdl with incremental
|
||||
updates from here on.
|
||||
|
||||
------------
|
||||
$ git ls-remote bundle
|
||||
$ git fetch bundle
|
||||
$ git pull bundle
|
||||
------------
|
||||
After working more in the original repository, you can create an
|
||||
incremental bundle to update the other:
|
||||
|
||||
would treat it as if it is talking with a remote side over the
|
||||
network.
|
||||
----------------
|
||||
machineA$ cd R1
|
||||
machineA$ git bundle create file.bdl lastR2bundle..master
|
||||
machineA$ git tag -f lastR2bundle master
|
||||
----------------
|
||||
|
||||
and sneakernet it to the other machine to replace /home/me/tmp/file.bdl,
|
||||
and pull from it.
|
||||
|
||||
----------------
|
||||
machineB$ cd R2
|
||||
machineB$ git pull
|
||||
----------------
|
||||
|
||||
If you know up to what commit the intended recipient repository should
|
||||
have the necessary objects for, you can use that knowledge to specify the
|
||||
basis, giving a cut-off point to limit the revisions and objects that go
|
||||
in the resulting bundle. The previous example used lastR2bundle tag
|
||||
for this purpose, but you can use other options you would give to
|
||||
the linkgit:git-log[1] command. Here are more examples:
|
||||
|
||||
You can use a tag that is present in both.
|
||||
|
||||
----------------
|
||||
$ git bundle create mybundle v1.0.0..master
|
||||
----------------
|
||||
|
||||
You can use a basis based on time.
|
||||
|
||||
----------------
|
||||
$ git bundle create mybundle --since=10.days master
|
||||
----------------
|
||||
|
||||
Or you can use the number of commits.
|
||||
|
||||
----------------
|
||||
$ git bundle create mybundle -10 master
|
||||
----------------
|
||||
|
||||
You can run `git-bundle verify` to see if you can extract from a bundle
|
||||
that was created with a basis.
|
||||
|
||||
----------------
|
||||
$ git bundle verify mybundle
|
||||
----------------
|
||||
|
||||
This will list what commits you must have in order to extract from the
|
||||
bundle and will error out if you don't have them.
|
||||
|
||||
A bundle from a recipient repository's point of view is just like a
|
||||
regular repository it fetches/pulls from. You can for example map
|
||||
refs, like this example, when fetching:
|
||||
|
||||
----------------
|
||||
$ git fetch mybundle master:localRef
|
||||
----------------
|
||||
|
||||
Or see what refs it offers.
|
||||
|
||||
----------------
|
||||
$ git ls-remote mybundle
|
||||
----------------
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
@@ -133,6 +133,10 @@ the conflicted merge in the specified paths.
|
||||
+
|
||||
When this parameter names a non-branch (but still a valid commit object),
|
||||
your HEAD becomes 'detached'.
|
||||
+
|
||||
As a special case, the "`@\{-N\}`" syntax for the N-th last branch
|
||||
checks out the branch (instead of detaching). You may also specify
|
||||
"`-`" which is synonymous with "`@\{-1\}`".
|
||||
|
||||
|
||||
Detached HEAD
|
||||
|
||||
46
Documentation/git-notes.txt
Normal file
46
Documentation/git-notes.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
git-notes(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
git-notes - Add/inspect commit notes
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-notes' (edit | show) [commit]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command allows you to add notes to commit messages, without
|
||||
changing the commit. To discern these notes from the message stored
|
||||
in the commit object, the notes are indented like the message, after
|
||||
an unindented line saying "Notes:".
|
||||
|
||||
To disable commit notes, you have to set the config variable
|
||||
core.notesRef to the empty string. Alternatively, you can set it
|
||||
to a different ref, something like "refs/notes/bugzilla". This setting
|
||||
can be overridden by the environment variable "GIT_NOTES_REF".
|
||||
|
||||
|
||||
SUBCOMMANDS
|
||||
-----------
|
||||
|
||||
edit::
|
||||
Edit the notes for a given commit (defaults to HEAD).
|
||||
|
||||
show::
|
||||
Show the notes for a given commit (defaults to HEAD).
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Johannes Schindelin <johannes.schindelin@gmx.de>
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Documentation by Johannes Schindelin
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
@@ -33,26 +33,28 @@ OPTIONS
|
||||
of a remote (see the section <<REMOTES,REMOTES>> below).
|
||||
|
||||
<refspec>...::
|
||||
The canonical format of a <refspec> parameter is
|
||||
`+?<src>:<dst>`; that is, an optional plus `{plus}`, followed
|
||||
by the source ref, followed by a colon `:`, followed by
|
||||
the destination ref.
|
||||
The format of a <refspec> parameter is an optional plus
|
||||
`{plus}`, followed by the source ref <src>, followed
|
||||
by a colon `:`, followed by the destination ref <dst>.
|
||||
It is used to specify with what <src> object the <dst> ref
|
||||
in the remote repository is to be updated.
|
||||
+
|
||||
The <src> side represents the source branch (or arbitrary
|
||||
"SHA1 expression", such as `master~4` (four parents before the
|
||||
tip of `master` branch); see linkgit:git-rev-parse[1]) that you
|
||||
want to push. The <dst> side represents the destination location.
|
||||
The <src> is often the name of the branch you would want to push, but
|
||||
it can be any arbitrary "SHA-1 expression", such as `master~4` or
|
||||
`HEAD` (see linkgit:git-rev-parse[1]).
|
||||
+
|
||||
The local ref that matches <src> is used
|
||||
to fast forward the remote ref that matches <dst>. If
|
||||
the optional leading plus `+` is used, the remote ref is updated
|
||||
even if it does not result in a fast forward update.
|
||||
The <dst> tells which ref on the remote side is updated with this
|
||||
push. Arbitrary expressions cannot be used here, an actual ref must
|
||||
be named. If `:`<dst> is omitted, the same ref as <src> will be
|
||||
updated.
|
||||
+
|
||||
The object referenced by <src> is used to fast forward the ref <dst>
|
||||
on the remote side. If the optional leading plus `{plus}` is used, the
|
||||
remote ref is updated even if it does not result in a fast forward
|
||||
update.
|
||||
+
|
||||
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
|
||||
+
|
||||
A lonely <src> parameter (without a colon and a destination) pushes
|
||||
the <src> to the same name in the destination repository.
|
||||
+
|
||||
Pushing an empty <src> allows you to delete the <dst> ref from
|
||||
the remote repository.
|
||||
+
|
||||
@@ -190,9 +192,9 @@ git push origin master::
|
||||
with it. If `master` did not exist remotely, it would be
|
||||
created.
|
||||
|
||||
git push origin :experimental::
|
||||
Find a ref that matches `experimental` in the `origin` repository
|
||||
(e.g. `refs/heads/experimental`), and delete it.
|
||||
git push origin HEAD::
|
||||
A handy way to push the current branch to the same name on the
|
||||
remote.
|
||||
|
||||
git push origin master:satellite/master dev:satellite/dev::
|
||||
Use the source ref that matches `master` (e.g. `refs/heads/master`)
|
||||
@@ -200,6 +202,11 @@ git push origin master:satellite/master dev:satellite/dev::
|
||||
`refs/remotes/satellite/master`) in the `origin` repository, then
|
||||
do the same for `dev` and `satellite/dev`.
|
||||
|
||||
git push origin HEAD:master::
|
||||
Push the current branch to the remote ref matching `master` in the
|
||||
`origin` repository. This form is convenient to push the current
|
||||
branch without thinking about its local name.
|
||||
|
||||
git push origin master:refs/heads/experimental::
|
||||
Create the branch `experimental` in the `origin` repository
|
||||
by copying the current `master` branch. This form is only
|
||||
@@ -207,6 +214,11 @@ git push origin master:refs/heads/experimental::
|
||||
the local name and the remote name are different; otherwise,
|
||||
the ref name on its own will work.
|
||||
|
||||
git push origin :experimental::
|
||||
Find a ref that matches `experimental` in the `origin` repository
|
||||
(e.g. `refs/heads/experimental`), and delete it.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano <gitster@pobox.com>, later rewritten in C
|
||||
|
||||
@@ -212,6 +212,9 @@ when you run 'git-merge'.
|
||||
reflog of the current branch. For example, if you are on the
|
||||
branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
|
||||
|
||||
* The special construct '@\{-<n>\}' means the <n>th branch checked out
|
||||
before the current one.
|
||||
|
||||
* A suffix '{caret}' to a revision parameter means the first parent of
|
||||
that commit object. '{caret}<n>' means the <n>th parent (i.e.
|
||||
'rev{caret}'
|
||||
|
||||
@@ -82,7 +82,7 @@ her family name fully spelled out, a proper `.mailmap` file would look like:
|
||||
# Note how we don't need an entry for <jane@laptop.(none)>, because the
|
||||
# real name of that author is correct already, and coalesced directly.
|
||||
Jane Doe <jane@desktop.(none)>
|
||||
Joe R. Developer <joe@random.com>
|
||||
Joe R. Developer <joe@example.com>
|
||||
------------
|
||||
|
||||
Author
|
||||
|
||||
@@ -99,12 +99,12 @@ OPTIONS
|
||||
will show the revisions given by "git rev-list {caret}master
|
||||
topic1 topic2"
|
||||
|
||||
-g::
|
||||
--reflog[=<n>[,<base>]] [<ref>]::
|
||||
Shows <n> most recent ref-log entries for the given
|
||||
ref. If <base> is given, <n> entries going back from
|
||||
that entry. <base> can be specified as count or date.
|
||||
`-g` can be used as a short-hand for this option. When
|
||||
no explicit <ref> parameter is given, it defaults to the
|
||||
When no explicit <ref> parameter is given, it defaults to the
|
||||
current branch (or `HEAD` if it is detached).
|
||||
|
||||
Note that --more, --list, --independent and --merge-base options
|
||||
|
||||
@@ -12,7 +12,7 @@ SYNOPSIS
|
||||
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
|
||||
<name> [<commit> | <object>]
|
||||
'git tag' -d <name>...
|
||||
'git tag' [-n[<num>]] -l [<pattern>]
|
||||
'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
|
||||
'git tag' -v <name>...
|
||||
|
||||
DESCRIPTION
|
||||
@@ -68,6 +68,9 @@ OPTIONS
|
||||
List tags with names that match the given pattern (or all if no pattern is given).
|
||||
Typing "git tag" without arguments, also lists all tags.
|
||||
|
||||
--contains <commit>::
|
||||
Only list tags which contain the specified commit.
|
||||
|
||||
-m <msg>::
|
||||
Use the given tag message (instead of prompting).
|
||||
If multiple `-m` options are given, their values are
|
||||
|
||||
@@ -5,15 +5,14 @@
|
||||
of a remote (see the section <<REMOTES,REMOTES>> below).
|
||||
|
||||
<refspec>::
|
||||
The canonical format of a <refspec> parameter is
|
||||
`+?<src>:<dst>`; that is, an optional plus `{plus}`, followed
|
||||
by the source ref, followed by a colon `:`, followed by
|
||||
the destination ref.
|
||||
The format of a <refspec> parameter is an optional plus
|
||||
`{plus}`, followed by the source ref <src>, followed
|
||||
by a colon `:`, followed by the destination ref <dst>.
|
||||
+
|
||||
The remote ref that matches <src>
|
||||
is fetched, and if <dst> is not empty string, the local
|
||||
ref that matches it is fast forwarded using <src>.
|
||||
Again, if the optional plus `+` is used, the local ref
|
||||
If the optional plus `+` is used, the local ref
|
||||
is updated even if it does not result in a fast forward
|
||||
update.
|
||||
+
|
||||
|
||||
@@ -21,7 +21,7 @@ allocated memory or not), use `strbuf_detach()` to unwrap a memory
|
||||
buffer from its strbuf shell in a safe way. That is the sole supported
|
||||
way. This will give you a malloced buffer that you can later `free()`.
|
||||
+
|
||||
However, it it totally safe to modify anything in the string pointed by
|
||||
However, it is totally safe to modify anything in the string pointed by
|
||||
the `buf` member, between the indices `0` and `len-1` (inclusive).
|
||||
|
||||
. The `buf` member is a byte array that has at least `len + 1` bytes
|
||||
|
||||
@@ -6,10 +6,10 @@ to name the remote repository:
|
||||
|
||||
===============================================================
|
||||
- rsync://host.xz/path/to/repo.git/
|
||||
- http://host.xz/path/to/repo.git/
|
||||
- https://host.xz/path/to/repo.git/
|
||||
- git://host.xz/path/to/repo.git/
|
||||
- git://host.xz/~user/path/to/repo.git/
|
||||
- http://host.xz{startsb}:port{endsb}/path/to/repo.git/
|
||||
- https://host.xz{startsb}:port{endsb}/path/to/repo.git/
|
||||
- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
|
||||
- git://host.xz{startsb}:port{endsb}/~user/path/to/repo.git/
|
||||
- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
|
||||
- ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
|
||||
- ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
|
||||
|
||||
@@ -1507,7 +1507,7 @@ so on a different branch and then coming back), unstash the
|
||||
work-in-progress changes.
|
||||
|
||||
------------------------------------------------
|
||||
$ git stash "work in progress for foo feature"
|
||||
$ git stash save "work in progress for foo feature"
|
||||
------------------------------------------------
|
||||
|
||||
This command will save your changes away to the `stash`, and
|
||||
|
||||
27
Makefile
27
Makefile
@@ -23,6 +23,9 @@ all::
|
||||
# Define NO_EXPAT if you do not have expat installed. git-http-push is
|
||||
# not built, and you cannot push using http:// and https:// transports.
|
||||
#
|
||||
# Define EXPATDIR=/foo/bar if your expat header and library files are in
|
||||
# /foo/bar/include and /foo/bar/lib directories.
|
||||
#
|
||||
# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
|
||||
#
|
||||
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
|
||||
@@ -262,6 +265,7 @@ SCRIPT_SH += git-merge-octopus.sh
|
||||
SCRIPT_SH += git-merge-one-file.sh
|
||||
SCRIPT_SH += git-merge-resolve.sh
|
||||
SCRIPT_SH += git-mergetool.sh
|
||||
SCRIPT_SH += git-notes.sh
|
||||
SCRIPT_SH += git-parse-remote.sh
|
||||
SCRIPT_SH += git-pull.sh
|
||||
SCRIPT_SH += git-quiltimport.sh
|
||||
@@ -374,6 +378,7 @@ LIB_H += ll-merge.h
|
||||
LIB_H += log-tree.h
|
||||
LIB_H += mailmap.h
|
||||
LIB_H += merge-recursive.h
|
||||
LIB_H += notes.h
|
||||
LIB_H += object.h
|
||||
LIB_H += pack.h
|
||||
LIB_H += pack-refs.h
|
||||
@@ -392,6 +397,7 @@ LIB_H += revision.h
|
||||
LIB_H += run-command.h
|
||||
LIB_H += sha1-lookup.h
|
||||
LIB_H += sideband.h
|
||||
LIB_H += sigchain.h
|
||||
LIB_H += strbuf.h
|
||||
LIB_H += tag.h
|
||||
LIB_H += transport.h
|
||||
@@ -455,6 +461,7 @@ LIB_OBJS += match-trees.o
|
||||
LIB_OBJS += merge-file.o
|
||||
LIB_OBJS += merge-recursive.o
|
||||
LIB_OBJS += name-hash.o
|
||||
LIB_OBJS += notes.o
|
||||
LIB_OBJS += object.o
|
||||
LIB_OBJS += pack-check.o
|
||||
LIB_OBJS += pack-refs.o
|
||||
@@ -485,6 +492,7 @@ LIB_OBJS += sha1-lookup.o
|
||||
LIB_OBJS += sha1_name.o
|
||||
LIB_OBJS += shallow.o
|
||||
LIB_OBJS += sideband.o
|
||||
LIB_OBJS += sigchain.o
|
||||
LIB_OBJS += strbuf.o
|
||||
LIB_OBJS += symlinks.o
|
||||
LIB_OBJS += tag.o
|
||||
@@ -644,10 +652,12 @@ endif
|
||||
ifeq ($(uname_S),Darwin)
|
||||
NEEDS_SSL_WITH_CRYPTO = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
ifneq ($(shell expr "$(uname_R)" : '9\.'),2)
|
||||
ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
|
||||
OLD_ICONV = UnfortunatelyYes
|
||||
endif
|
||||
NO_STRLCPY = YesPlease
|
||||
ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
|
||||
NO_STRLCPY = YesPlease
|
||||
endif
|
||||
NO_MEMMEM = YesPlease
|
||||
THREADED_DELTA_SEARCH = YesPlease
|
||||
endif
|
||||
@@ -855,7 +865,12 @@ else
|
||||
endif
|
||||
endif
|
||||
ifndef NO_EXPAT
|
||||
EXPAT_LIBEXPAT = -lexpat
|
||||
ifdef EXPATDIR
|
||||
BASIC_CFLAGS += -I$(EXPATDIR)/include
|
||||
EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat
|
||||
else
|
||||
EXPAT_LIBEXPAT = -lexpat
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -1375,11 +1390,13 @@ TEST_PROGRAMS += test-chmtime$X
|
||||
TEST_PROGRAMS += test-ctype$X
|
||||
TEST_PROGRAMS += test-date$X
|
||||
TEST_PROGRAMS += test-delta$X
|
||||
TEST_PROGRAMS += test-dump-cache-tree$X
|
||||
TEST_PROGRAMS += test-genrandom$X
|
||||
TEST_PROGRAMS += test-match-trees$X
|
||||
TEST_PROGRAMS += test-parse-options$X
|
||||
TEST_PROGRAMS += test-path-utils$X
|
||||
TEST_PROGRAMS += test-sha1$X
|
||||
TEST_PROGRAMS += test-sigchain$X
|
||||
|
||||
all:: $(TEST_PROGRAMS)
|
||||
|
||||
@@ -1425,14 +1442,14 @@ remove-dashes:
|
||||
|
||||
### Installation rules
|
||||
|
||||
ifeq ($(abspath $(template_dir)),$(template_dir))
|
||||
ifneq ($(filter /%,$(firstword $(template_dir))),)
|
||||
template_instdir = $(template_dir)
|
||||
else
|
||||
template_instdir = $(prefix)/$(template_dir)
|
||||
endif
|
||||
export template_instdir
|
||||
|
||||
ifeq ($(abspath $(gitexecdir)),$(gitexecdir))
|
||||
ifneq ($(filter /%,$(firstword $(gitexecdir))),)
|
||||
gitexec_instdir = $(gitexecdir)
|
||||
else
|
||||
gitexec_instdir = $(prefix)/$(gitexecdir)
|
||||
|
||||
@@ -2441,7 +2441,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
|
||||
return error("%s: %s", old_name, strerror(errno));
|
||||
}
|
||||
|
||||
if (!cached)
|
||||
if (!cached && !tpatch)
|
||||
st_mode = ce_mode_from_stat(*ce, st->st_mode);
|
||||
|
||||
if (patch->is_new < 0)
|
||||
@@ -2453,7 +2453,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
|
||||
if (st_mode != patch->old_mode)
|
||||
fprintf(stderr, "warning: %s has type %o, expected %o\n",
|
||||
old_name, st_mode, patch->old_mode);
|
||||
if (!patch->new_mode)
|
||||
if (!patch->new_mode && !patch->is_delete)
|
||||
patch->new_mode = st_mode;
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "string-list.h"
|
||||
#include "mailmap.h"
|
||||
#include "parse-options.h"
|
||||
#include "utf8.h"
|
||||
|
||||
static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
|
||||
|
||||
@@ -1618,13 +1619,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
|
||||
printf(" %*d", max_orig_digits,
|
||||
ent->s_lno + 1 + cnt);
|
||||
|
||||
if (!(opt & OUTPUT_NO_AUTHOR))
|
||||
printf(" (%-*.*s %10s",
|
||||
longest_author, longest_author,
|
||||
ci.author,
|
||||
if (!(opt & OUTPUT_NO_AUTHOR)) {
|
||||
int pad = longest_author - utf8_strwidth(ci.author);
|
||||
printf(" (%s%*s %10s",
|
||||
ci.author, pad, "",
|
||||
format_time(ci.author_time,
|
||||
ci.author_tz,
|
||||
show_raw_time));
|
||||
}
|
||||
printf(" %*d) ",
|
||||
max_digits, ent->lno + 1 + cnt);
|
||||
}
|
||||
@@ -1755,7 +1757,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
|
||||
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
|
||||
suspect->commit->object.flags |= METAINFO_SHOWN;
|
||||
get_commit_info(suspect->commit, &ci, 1);
|
||||
num = strlen(ci.author);
|
||||
num = utf8_strwidth(ci.author);
|
||||
if (longest_author < num)
|
||||
longest_author = num;
|
||||
}
|
||||
|
||||
@@ -193,21 +193,6 @@ struct ref_list {
|
||||
int kinds;
|
||||
};
|
||||
|
||||
static int has_commit(struct commit *commit, struct commit_list *with_commit)
|
||||
{
|
||||
if (!with_commit)
|
||||
return 1;
|
||||
while (with_commit) {
|
||||
struct commit *other;
|
||||
|
||||
other = with_commit->item;
|
||||
with_commit = with_commit->next;
|
||||
if (in_merge_bases(other, &commit, 1))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct ref_list *ref_list = (struct ref_list*)(cb_data);
|
||||
@@ -231,7 +216,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
|
||||
return error("branch '%s' does not point at a commit", refname);
|
||||
|
||||
/* Filter with with_commit if specified */
|
||||
if (!has_commit(commit, ref_list->with_commit))
|
||||
if (!is_descendant_of(commit, ref_list->with_commit))
|
||||
return 0;
|
||||
|
||||
/* Don't add types the caller doesn't want */
|
||||
@@ -401,7 +386,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
|
||||
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
|
||||
|
||||
detached = (detached && (kinds & REF_LOCAL_BRANCH));
|
||||
if (detached && head_commit && has_commit(head_commit, with_commit)) {
|
||||
if (detached && head_commit &&
|
||||
is_descendant_of(head_commit, with_commit)) {
|
||||
struct ref_item item;
|
||||
item.name = xstrdup("(no branch)");
|
||||
item.kind = REF_LOCAL_BRANCH;
|
||||
@@ -466,22 +452,6 @@ static void rename_branch(const char *oldname, const char *newname, int force)
|
||||
strbuf_release(&newsection);
|
||||
}
|
||||
|
||||
static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct commit *commit;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
if (get_sha1(arg, sha1))
|
||||
die("malformed object name %s", arg);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit)
|
||||
die("no such commit %s", arg);
|
||||
commit_list_insert(commit, opt->value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
merge_filter = ((opt->long_name[0] == 'n')
|
||||
@@ -517,13 +487,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
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",
|
||||
parse_opt_with_commit, (intptr_t)"HEAD",
|
||||
},
|
||||
{
|
||||
OPTION_CALLBACK, 0, "with", &with_commit, "commit",
|
||||
"print only branches that contain the commit",
|
||||
PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
|
||||
opt_parse_with_commit, (intptr_t) "HEAD",
|
||||
parse_opt_with_commit, (intptr_t) "HEAD",
|
||||
},
|
||||
OPT__ABBREV(&abbrev),
|
||||
|
||||
|
||||
@@ -351,8 +351,16 @@ struct branch_info {
|
||||
static void setup_branch_path(struct branch_info *branch)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addstr(&buf, "refs/heads/");
|
||||
strbuf_addstr(&buf, branch->name);
|
||||
int ret;
|
||||
|
||||
if ((ret = interpret_nth_last_branch(branch->name, &buf))
|
||||
&& ret == strlen(branch->name)) {
|
||||
branch->name = xstrdup(buf.buf);
|
||||
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
|
||||
} else {
|
||||
strbuf_addstr(&buf, "refs/heads/");
|
||||
strbuf_addstr(&buf, branch->name);
|
||||
}
|
||||
branch->path = strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
@@ -661,6 +669,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
|
||||
arg = argv[0];
|
||||
has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
|
||||
|
||||
if (!strcmp(arg, "-"))
|
||||
arg = "@{-1}";
|
||||
|
||||
if (get_sha1(arg, rev)) {
|
||||
if (has_dash_dash) /* case (1) */
|
||||
die("invalid reference: %s", arg);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "strbuf.h"
|
||||
#include "dir.h"
|
||||
#include "pack-refs.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
/*
|
||||
* Overall FIXMEs:
|
||||
@@ -288,7 +289,7 @@ static void remove_junk(void)
|
||||
static void remove_junk_on_signal(int signo)
|
||||
{
|
||||
remove_junk();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
@@ -441,7 +442,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
junk_git_dir = git_dir;
|
||||
atexit(remove_junk);
|
||||
signal(SIGINT, remove_junk_on_signal);
|
||||
sigchain_push_common(remove_junk_on_signal);
|
||||
|
||||
setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
static char *get_stdin(void)
|
||||
{
|
||||
@@ -186,7 +187,7 @@ static void remove_keep(void)
|
||||
static void remove_keep_on_signal(int signo)
|
||||
{
|
||||
remove_keep();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
@@ -245,7 +246,7 @@ static int fetch_native_store(FILE *fp,
|
||||
char buffer[1024];
|
||||
int err = 0;
|
||||
|
||||
signal(SIGINT, remove_keep_on_signal);
|
||||
sigchain_push_common(remove_keep_on_signal);
|
||||
atexit(remove_keep);
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), stdin)) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "transport.h"
|
||||
#include "run-command.h"
|
||||
#include "parse-options.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
static const char * const builtin_fetch_usage[] = {
|
||||
"git fetch [options] [<repository> <refspec>...]",
|
||||
@@ -58,7 +59,7 @@ static void unlock_pack(void)
|
||||
static void unlock_pack_on_signal(int signo)
|
||||
{
|
||||
unlock_pack();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
@@ -672,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
ref_nr = j;
|
||||
}
|
||||
|
||||
signal(SIGINT, unlock_pack_on_signal);
|
||||
sigchain_push_common(unlock_pack_on_signal);
|
||||
atexit(unlock_pack);
|
||||
exit_code = do_fetch(transport,
|
||||
parse_fetch_refspec(ref_nr, refs), ref_nr);
|
||||
|
||||
@@ -23,6 +23,7 @@ static int check_full;
|
||||
static int check_strict;
|
||||
static int keep_cache_objects;
|
||||
static unsigned char head_sha1[20];
|
||||
static const char *head_points_at;
|
||||
static int errors_found;
|
||||
static int write_lost_and_found;
|
||||
static int verbose;
|
||||
@@ -473,6 +474,8 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
|
||||
|
||||
static void get_default_heads(void)
|
||||
{
|
||||
if (head_points_at && !is_null_sha1(head_sha1))
|
||||
fsck_handle_ref("HEAD", head_sha1, 0, NULL);
|
||||
for_each_ref(fsck_handle_ref, NULL);
|
||||
if (include_reflogs)
|
||||
for_each_reflog(fsck_handle_reflog, NULL);
|
||||
@@ -512,14 +515,13 @@ static void fsck_object_dir(const char *path)
|
||||
|
||||
static int fsck_head_link(void)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
int flag;
|
||||
int null_is_error = 0;
|
||||
const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "Checking HEAD link\n");
|
||||
|
||||
head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
|
||||
if (!head_points_at)
|
||||
return error("Invalid HEAD");
|
||||
if (!strcmp(head_points_at, "HEAD"))
|
||||
@@ -528,7 +530,7 @@ static int fsck_head_link(void)
|
||||
else if (prefixcmp(head_points_at, "refs/heads/"))
|
||||
return error("HEAD points to something strange (%s)",
|
||||
head_points_at);
|
||||
if (is_null_sha1(sha1)) {
|
||||
if (is_null_sha1(head_sha1)) {
|
||||
if (null_is_error)
|
||||
return error("HEAD: detached HEAD points at nothing");
|
||||
fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
|
||||
@@ -584,6 +586,7 @@ static struct option fsck_opts[] = {
|
||||
int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i, heads;
|
||||
struct alternate_object_database *alt;
|
||||
|
||||
errors_found = 0;
|
||||
|
||||
@@ -595,17 +598,19 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
|
||||
fsck_head_link();
|
||||
fsck_object_dir(get_object_directory());
|
||||
|
||||
prepare_alt_odb();
|
||||
for (alt = alt_odb_list; alt; alt = alt->next) {
|
||||
char namebuf[PATH_MAX];
|
||||
int namelen = alt->name - alt->base;
|
||||
memcpy(namebuf, alt->base, namelen);
|
||||
namebuf[namelen - 1] = 0;
|
||||
fsck_object_dir(namebuf);
|
||||
}
|
||||
|
||||
if (check_full) {
|
||||
struct alternate_object_database *alt;
|
||||
struct packed_git *p;
|
||||
prepare_alt_odb();
|
||||
for (alt = alt_odb_list; alt; alt = alt->next) {
|
||||
char namebuf[PATH_MAX];
|
||||
int namelen = alt->name - alt->base;
|
||||
memcpy(namebuf, alt->base, namelen);
|
||||
namebuf[namelen - 1] = 0;
|
||||
fsck_object_dir(namebuf);
|
||||
}
|
||||
|
||||
prepare_packed_git();
|
||||
for (p = packed_git; p; p = p->next)
|
||||
/* verify gives error messages itself */
|
||||
@@ -624,8 +629,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
heads = 0;
|
||||
for (i = 0; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (!get_sha1(arg, head_sha1)) {
|
||||
struct object *obj = lookup_object(head_sha1);
|
||||
unsigned char sha1[20];
|
||||
if (!get_sha1(arg, sha1)) {
|
||||
struct object *obj = lookup_object(sha1);
|
||||
|
||||
/* Error is printed by lookup_object(). */
|
||||
if (!obj)
|
||||
|
||||
@@ -291,6 +291,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
push_arg("-E");
|
||||
if (opt->regflags & REG_ICASE)
|
||||
push_arg("-i");
|
||||
if (opt->binary == GREP_BINARY_NOMATCH)
|
||||
push_arg("-I");
|
||||
if (opt->word_regexp)
|
||||
push_arg("-w");
|
||||
if (opt->name_only)
|
||||
|
||||
@@ -29,6 +29,9 @@ static struct strbuf **p_hdr_data, **s_hdr_data;
|
||||
#define MAX_HDR_PARSED 10
|
||||
#define MAX_BOUNDARIES 5
|
||||
|
||||
static void cleanup_space(struct strbuf *sb);
|
||||
|
||||
|
||||
static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
|
||||
{
|
||||
struct strbuf *src = name;
|
||||
@@ -109,11 +112,19 @@ static void handle_from(const struct strbuf *from)
|
||||
strbuf_add(&email, at, el);
|
||||
strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
|
||||
|
||||
/* The remainder is name. It could be "John Doe <john.doe@xz>"
|
||||
* or "john.doe@xz (John Doe)", but we have removed the
|
||||
* email part, so trim from both ends, possibly removing
|
||||
* the () pair at the end.
|
||||
/* The remainder is name. It could be
|
||||
*
|
||||
* - "John Doe <john.doe@xz>" (a), or
|
||||
* - "john.doe@xz (John Doe)" (b), or
|
||||
* - "John (zzz) Doe <john.doe@xz> (Comment)" (c)
|
||||
*
|
||||
* but we have removed the email part, so
|
||||
*
|
||||
* - remove extra spaces which could stay after email (case 'c'), and
|
||||
* - trim from both ends, possibly removing the () pair at the end
|
||||
* (cases 'a' and 'b').
|
||||
*/
|
||||
cleanup_space(&f);
|
||||
strbuf_trim(&f);
|
||||
if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
|
||||
strbuf_remove(&f, 0, 1);
|
||||
@@ -430,13 +441,6 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
|
||||
c -= 'a' - 26;
|
||||
else if ('0' <= c && c <= '9')
|
||||
c -= '0' - 52;
|
||||
else if (c == '=') {
|
||||
/* padding is almost like (c == 0), except we do
|
||||
* not output NUL resulting only from it;
|
||||
* for now we just trust the data.
|
||||
*/
|
||||
c = 0;
|
||||
}
|
||||
else
|
||||
continue; /* garbage */
|
||||
switch (pos++) {
|
||||
@@ -514,7 +518,25 @@ static int decode_header_bq(struct strbuf *it)
|
||||
rfc2047 = 1;
|
||||
|
||||
if (in != ep) {
|
||||
strbuf_add(&outbuf, in, ep - in);
|
||||
/*
|
||||
* We are about to process an encoded-word
|
||||
* that begins at ep, but there is something
|
||||
* before the encoded word.
|
||||
*/
|
||||
char *scan;
|
||||
for (scan = in; scan < ep; scan++)
|
||||
if (!isspace(*scan))
|
||||
break;
|
||||
|
||||
if (scan != ep || in == it->buf) {
|
||||
/*
|
||||
* We should not lose that "something",
|
||||
* unless we have just processed an
|
||||
* encoded-word, and there is only LWS
|
||||
* before the one we are about to process.
|
||||
*/
|
||||
strbuf_add(&outbuf, in, ep - in);
|
||||
}
|
||||
in = ep;
|
||||
}
|
||||
/* E.g.
|
||||
@@ -860,6 +882,7 @@ static void handle_info(void)
|
||||
}
|
||||
output_header_lines(fout, "Subject", hdr);
|
||||
} else if (!memcmp(header[i], "From", 4)) {
|
||||
cleanup_space(hdr);
|
||||
handle_from(hdr);
|
||||
fprintf(fout, "Author: %s\n", name.buf);
|
||||
fprintf(fout, "Email: %s\n", email.buf);
|
||||
|
||||
@@ -36,8 +36,8 @@ struct strategy {
|
||||
};
|
||||
|
||||
static const char * const builtin_merge_usage[] = {
|
||||
"git-merge [options] <remote>...",
|
||||
"git-merge [options] <msg> HEAD <remote>",
|
||||
"git merge [options] <remote>...",
|
||||
"git merge [options] <msg> HEAD <remote>",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
@@ -162,7 +162,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
argc += last - first;
|
||||
}
|
||||
} else if (lstat(dst, &st) == 0) {
|
||||
} else if (cache_name_pos(src, length) < 0)
|
||||
bad = "not under version control";
|
||||
else if (lstat(dst, &st) == 0) {
|
||||
bad = "destination exists";
|
||||
if (force) {
|
||||
/*
|
||||
@@ -177,9 +179,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
||||
} else
|
||||
bad = "Cannot overwrite";
|
||||
}
|
||||
} else if (cache_name_pos(src, length) < 0)
|
||||
bad = "not under version control";
|
||||
else if (string_list_has_string(&src_for_dst, dst))
|
||||
} else if (string_list_has_string(&src_for_dst, dst))
|
||||
bad = "multiple sources for the same target";
|
||||
else
|
||||
string_list_insert(dst, &src_for_dst);
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
#include "remote.h"
|
||||
#include "transport.h"
|
||||
|
||||
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
|
||||
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
|
||||
|
||||
enum deny_action {
|
||||
DENY_UNCONFIGURED,
|
||||
DENY_IGNORE,
|
||||
DENY_WARN,
|
||||
DENY_REFUSE,
|
||||
@@ -19,7 +20,7 @@ enum deny_action {
|
||||
|
||||
static int deny_deletes = 0;
|
||||
static int deny_non_fast_forwards = 0;
|
||||
static enum deny_action deny_current_branch = DENY_WARN;
|
||||
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
|
||||
static int receive_fsck_objects;
|
||||
static int receive_unpack_limit = -1;
|
||||
static int transfer_unpack_limit = -1;
|
||||
@@ -214,6 +215,35 @@ static int is_ref_checked_out(const char *ref)
|
||||
return !strcmp(head, ref);
|
||||
}
|
||||
|
||||
static char *warn_unconfigured_deny_msg[] = {
|
||||
"Updating the currently checked out branch may cause confusion,",
|
||||
"as the index and work tree do not reflect changes that are in HEAD.",
|
||||
"As a result, you may see the changes you just pushed into it",
|
||||
"reverted when you run 'git diff' over there, and you may want",
|
||||
"to run 'git reset --hard' before starting to work to recover.",
|
||||
"",
|
||||
"You can set 'receive.denyCurrentBranch' configuration variable to",
|
||||
"'refuse' in the remote repository to forbid pushing into its",
|
||||
"current branch."
|
||||
"",
|
||||
"To allow pushing into the current branch, you can set it to 'ignore';",
|
||||
"but this is not recommended unless you arranged to update its work",
|
||||
"tree to match what you pushed in some other way.",
|
||||
"",
|
||||
"To squelch this message, you can set it to 'warn'.",
|
||||
"",
|
||||
"Note that the default will change in a future version of git",
|
||||
"to refuse updating the current branch unless you have the",
|
||||
"configuration variable set to either 'ignore' or 'warn'."
|
||||
};
|
||||
|
||||
static void warn_unconfigured_deny(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_msg); i++)
|
||||
warning(warn_unconfigured_deny_msg[i]);
|
||||
}
|
||||
|
||||
static const char *update(struct command *cmd)
|
||||
{
|
||||
const char *name = cmd->ref_name;
|
||||
@@ -227,22 +257,20 @@ static const char *update(struct command *cmd)
|
||||
return "funny refname";
|
||||
}
|
||||
|
||||
switch (deny_current_branch) {
|
||||
case DENY_IGNORE:
|
||||
break;
|
||||
case DENY_WARN:
|
||||
if (!is_ref_checked_out(name))
|
||||
if (is_ref_checked_out(name)) {
|
||||
switch (deny_current_branch) {
|
||||
case DENY_IGNORE:
|
||||
break;
|
||||
warning("updating the currently checked out branch; this may"
|
||||
" cause confusion,\n"
|
||||
"as the index and working tree do not reflect changes"
|
||||
" that are now in HEAD.");
|
||||
break;
|
||||
case DENY_REFUSE:
|
||||
if (!is_ref_checked_out(name))
|
||||
case DENY_UNCONFIGURED:
|
||||
case DENY_WARN:
|
||||
warning("updating the current branch");
|
||||
if (deny_current_branch == DENY_UNCONFIGURED)
|
||||
warn_unconfigured_deny();
|
||||
break;
|
||||
error("refusing to update checked out branch: %s", name);
|
||||
return "branch is currently checked out";
|
||||
case DENY_REFUSE:
|
||||
error("refusing to update checked out branch: %s", name);
|
||||
return "branch is currently checked out";
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
|
||||
|
||||
@@ -298,7 +298,7 @@ static int add_known_remote(struct remote *remote, void *cb_data)
|
||||
|
||||
struct branches_for_remote {
|
||||
struct remote *remote;
|
||||
struct string_list *branches;
|
||||
struct string_list *branches, *skipped;
|
||||
struct known_remotes *keep;
|
||||
};
|
||||
|
||||
@@ -323,6 +323,16 @@ static int add_branch_for_removal(const char *refname,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* don't delete non-remote refs */
|
||||
if (prefixcmp(refname, "refs/remotes")) {
|
||||
/* advise user how to delete local branches */
|
||||
if (!prefixcmp(refname, "refs/heads/"))
|
||||
string_list_append(abbrev_branch(refname),
|
||||
branches->skipped);
|
||||
/* silently skip over other non-remote refs */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* make sure that symrefs are deleted */
|
||||
if (flags & REF_ISSYMREF)
|
||||
return unlink(git_path("%s", refname));
|
||||
@@ -542,8 +552,11 @@ static int rm(int argc, const char **argv)
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct known_remotes known_remotes = { NULL, NULL };
|
||||
struct string_list branches = { NULL, 0, 0, 1 };
|
||||
struct branches_for_remote cb_data = { NULL, &branches, &known_remotes };
|
||||
int i;
|
||||
struct string_list skipped = { NULL, 0, 0, 1 };
|
||||
struct branches_for_remote cb_data = {
|
||||
NULL, &branches, &skipped, &known_remotes
|
||||
};
|
||||
int i, result;
|
||||
|
||||
if (argc != 2)
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
@@ -583,14 +596,26 @@ static int rm(int argc, const char **argv)
|
||||
* refs, which are invalidated when deleting a branch.
|
||||
*/
|
||||
cb_data.remote = remote;
|
||||
i = for_each_ref(add_branch_for_removal, &cb_data);
|
||||
result = for_each_ref(add_branch_for_removal, &cb_data);
|
||||
strbuf_release(&buf);
|
||||
|
||||
if (!i)
|
||||
i = remove_branches(&branches);
|
||||
if (!result)
|
||||
result = remove_branches(&branches);
|
||||
string_list_clear(&branches, 1);
|
||||
|
||||
return i;
|
||||
if (skipped.nr) {
|
||||
fprintf(stderr, skipped.nr == 1 ?
|
||||
"Note: A non-remote branch was not removed; "
|
||||
"to delete it, use:\n" :
|
||||
"Note: Non-remote branches were not removed; "
|
||||
"to delete them, use:\n");
|
||||
for (i = 0; i < skipped.nr; i++)
|
||||
fprintf(stderr, " git branch -d %s\n",
|
||||
skipped.items[i].string);
|
||||
}
|
||||
string_list_clear(&skipped, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void show_list(const char *title, struct string_list *list,
|
||||
|
||||
@@ -15,6 +15,20 @@ static struct send_pack_args args = {
|
||||
/* .receivepack = */ "git-receive-pack",
|
||||
};
|
||||
|
||||
static int feed_object(const unsigned char *sha1, int fd, int negative)
|
||||
{
|
||||
char buf[42];
|
||||
|
||||
if (negative && !has_sha1_file(sha1))
|
||||
return 1;
|
||||
|
||||
memcpy(buf + negative, sha1_to_hex(sha1), 40);
|
||||
if (negative)
|
||||
buf[0] = '^';
|
||||
buf[40 + negative] = '\n';
|
||||
return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a pack stream and spit it out into file descriptor fd
|
||||
*/
|
||||
@@ -35,7 +49,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
|
||||
};
|
||||
struct child_process po;
|
||||
int i;
|
||||
char buf[42];
|
||||
|
||||
if (args.use_thin_pack)
|
||||
argv[4] = "--thin";
|
||||
@@ -51,31 +64,17 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
|
||||
* We feed the pack-objects we just spawned with revision
|
||||
* parameters by writing to the pipe.
|
||||
*/
|
||||
for (i = 0; i < extra->nr; i++) {
|
||||
memcpy(buf + 1, sha1_to_hex(&extra->array[i][0]), 40);
|
||||
buf[0] = '^';
|
||||
buf[41] = '\n';
|
||||
if (!write_or_whine(po.in, buf, 42, "send-pack: send refs"))
|
||||
for (i = 0; i < extra->nr; i++)
|
||||
if (!feed_object(extra->array[i], po.in, 1))
|
||||
break;
|
||||
}
|
||||
|
||||
while (refs) {
|
||||
if (!is_null_sha1(refs->old_sha1) &&
|
||||
has_sha1_file(refs->old_sha1)) {
|
||||
memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
|
||||
buf[0] = '^';
|
||||
buf[41] = '\n';
|
||||
if (!write_or_whine(po.in, buf, 42,
|
||||
"send-pack: send refs"))
|
||||
break;
|
||||
}
|
||||
if (!is_null_sha1(refs->new_sha1)) {
|
||||
memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
|
||||
buf[40] = '\n';
|
||||
if (!write_or_whine(po.in, buf, 41,
|
||||
"send-pack: send refs"))
|
||||
break;
|
||||
}
|
||||
!feed_object(refs->old_sha1, po.in, 1))
|
||||
break;
|
||||
if (!is_null_sha1(refs->new_sha1) &&
|
||||
!feed_object(refs->new_sha1, po.in, 0))
|
||||
break;
|
||||
refs = refs->next;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
|
||||
check_symref(argv[0], quiet);
|
||||
break;
|
||||
case 2:
|
||||
if (!strcmp(argv[0], "HEAD") &&
|
||||
prefixcmp(argv[1], "refs/heads/"))
|
||||
die("Refusing to point HEAD outside of refs/heads/");
|
||||
create_symref(argv[0], argv[1], msg);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -26,6 +26,7 @@ static char signingkey[1000];
|
||||
struct tag_filter {
|
||||
const char *pattern;
|
||||
int lines;
|
||||
struct commit_list *with_commit;
|
||||
};
|
||||
|
||||
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
|
||||
@@ -42,6 +43,16 @@ static int show_reference(const char *refname, const unsigned char *sha1,
|
||||
char *buf, *sp, *eol;
|
||||
size_t len;
|
||||
|
||||
if (filter->with_commit) {
|
||||
struct commit *commit;
|
||||
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
if (!commit)
|
||||
return 0;
|
||||
if (!is_descendant_of(commit, filter->with_commit))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!filter->lines) {
|
||||
printf("%s\n", refname);
|
||||
return 0;
|
||||
@@ -79,7 +90,8 @@ static int show_reference(const char *refname, const unsigned char *sha1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int list_tags(const char *pattern, int lines)
|
||||
static int list_tags(const char *pattern, int lines,
|
||||
struct commit_list *with_commit)
|
||||
{
|
||||
struct tag_filter filter;
|
||||
|
||||
@@ -88,6 +100,7 @@ static int list_tags(const char *pattern, int lines)
|
||||
|
||||
filter.pattern = pattern;
|
||||
filter.lines = lines;
|
||||
filter.with_commit = with_commit;
|
||||
|
||||
for_each_tag_ref(show_reference, (void *) &filter);
|
||||
|
||||
@@ -360,6 +373,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
list = 0, delete = 0, verify = 0;
|
||||
const char *msgfile = NULL, *keyid = NULL;
|
||||
struct msg_arg msg = { 0, STRBUF_INIT };
|
||||
struct commit_list *with_commit = NULL;
|
||||
struct option options[] = {
|
||||
OPT_BOOLEAN('l', NULL, &list, "list tag names"),
|
||||
{ OPTION_INTEGER, 'n', NULL, &lines, NULL,
|
||||
@@ -378,6 +392,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
OPT_STRING('u', NULL, &keyid, "key-id",
|
||||
"use another key to sign the tag"),
|
||||
OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
|
||||
|
||||
OPT_GROUP("Tag listing options"),
|
||||
{
|
||||
OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
|
||||
"print only tags that contain the commit",
|
||||
PARSE_OPT_LASTARG_DEFAULT,
|
||||
parse_opt_with_commit, (intptr_t)"HEAD",
|
||||
},
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -402,9 +424,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
if (list + delete + verify > 1)
|
||||
usage_with_options(git_tag_usage, options);
|
||||
if (list)
|
||||
return list_tags(argv[0], lines == -1 ? 0 : lines);
|
||||
return list_tags(argv[0], lines == -1 ? 0 : lines,
|
||||
with_commit);
|
||||
if (lines != -1)
|
||||
die("-n option is only allowed with -l.");
|
||||
if (with_commit)
|
||||
die("--contains option is only allowed with -l.");
|
||||
if (delete)
|
||||
return for_each_tag_name(argv, delete_tag);
|
||||
if (verify)
|
||||
|
||||
@@ -107,7 +107,7 @@ static int verify_one_pack(const char *path, int verbose)
|
||||
return err;
|
||||
}
|
||||
|
||||
static const char verify_pack_usage[] = "git-verify-pack [-v] <pack>...";
|
||||
static const char verify_pack_usage[] = "git verify-pack [-v] <pack>...";
|
||||
|
||||
int cmd_verify_pack(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
|
||||
4
cache.h
4
cache.h
@@ -371,6 +371,8 @@ static inline enum object_type object_type(unsigned int mode)
|
||||
#define GITATTRIBUTES_FILE ".gitattributes"
|
||||
#define INFOATTRIBUTES_FILE "info/attributes"
|
||||
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
|
||||
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
|
||||
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
|
||||
|
||||
extern int is_bare_repository_cfg;
|
||||
extern int is_bare_repository(void);
|
||||
@@ -542,6 +544,7 @@ enum rebase_setup_type {
|
||||
|
||||
extern enum branch_track git_branch_track;
|
||||
extern enum rebase_setup_type autorebase;
|
||||
extern char *notes_ref_name;
|
||||
|
||||
#define GIT_REPO_VERSION 0
|
||||
extern int repository_format_version;
|
||||
@@ -667,6 +670,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
|
||||
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
|
||||
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
|
||||
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
|
||||
extern int interpret_nth_last_branch(const char *str, struct strbuf *);
|
||||
|
||||
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
|
||||
extern const char *ref_rev_parse_rules[];
|
||||
|
||||
@@ -73,6 +73,7 @@ git-mktag plumbingmanipulators
|
||||
git-mktree plumbingmanipulators
|
||||
git-mv mainporcelain common
|
||||
git-name-rev plumbinginterrogators
|
||||
git-notes mainporcelain
|
||||
git-pack-objects plumbingmanipulators
|
||||
git-pack-redundant plumbinginterrogators
|
||||
git-pack-refs ancillarymanipulators
|
||||
|
||||
16
commit.c
16
commit.c
@@ -5,6 +5,7 @@
|
||||
#include "utf8.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "notes.h"
|
||||
|
||||
int save_commit_buffer = 1;
|
||||
|
||||
@@ -705,6 +706,21 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
|
||||
return get_merge_bases_many(one, 1, &two, cleanup);
|
||||
}
|
||||
|
||||
int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
|
||||
{
|
||||
if (!with_commit)
|
||||
return 1;
|
||||
while (with_commit) {
|
||||
struct commit *other;
|
||||
|
||||
other = with_commit->item;
|
||||
with_commit = with_commit->next;
|
||||
if (in_merge_bases(other, &commit, 1))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int in_merge_bases(struct commit *commit, struct commit **reference, int num)
|
||||
{
|
||||
struct commit_list *bases, *b;
|
||||
|
||||
1
commit.h
1
commit.h
@@ -133,6 +133,7 @@ extern int is_repository_shallow(void);
|
||||
extern struct commit_list *get_shallow_commits(struct object_array *heads,
|
||||
int depth, int shallow_flag, int not_shallow_flag);
|
||||
|
||||
int is_descendant_of(struct commit *, struct commit_list *);
|
||||
int in_merge_bases(struct commit *, struct commit **, int);
|
||||
|
||||
extern int interactive_add(int argc, const char **argv, const char *prefix);
|
||||
|
||||
@@ -21,12 +21,12 @@ typedef int pid_t;
|
||||
#define WEXITSTATUS(x) ((x) & 0xff)
|
||||
#define WIFSIGNALED(x) ((unsigned)(x) > 259)
|
||||
|
||||
#define SIGKILL 0
|
||||
#define SIGCHLD 0
|
||||
#define SIGPIPE 0
|
||||
#define SIGHUP 0
|
||||
#define SIGQUIT 0
|
||||
#define SIGALRM 100
|
||||
#define SIGHUP 1
|
||||
#define SIGQUIT 3
|
||||
#define SIGKILL 9
|
||||
#define SIGPIPE 13
|
||||
#define SIGALRM 14
|
||||
#define SIGCHLD 17
|
||||
|
||||
#define F_GETFD 1
|
||||
#define F_SETFD 2
|
||||
|
||||
5
config.c
5
config.c
@@ -469,6 +469,11 @@ static int git_default_core_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.notesref")) {
|
||||
notes_ref_name = xstrdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.pager"))
|
||||
return git_config_string(&pager_program, var, value);
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ TCLTK_PATH = @TCLTK_PATH@
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
gitexecdir = @libexecdir@/git-core/
|
||||
gitexecdir = @libexecdir@/git-core
|
||||
datarootdir = @datarootdir@
|
||||
template_dir = @datadir@/git-core/templates/
|
||||
template_dir = @datadir@/git-core/templates
|
||||
|
||||
mandir=@mandir@
|
||||
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
# are currently in a git repository. The %s token will be
|
||||
# the name of the current branch.
|
||||
#
|
||||
# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
|
||||
# value, unstaged (*) and staged (+) changes will be shown next
|
||||
# to the branch name. You can configure this per-repository
|
||||
# with the bash.showDirtyState variable, which defaults to true
|
||||
# once GIT_PS1_SHOWDIRTYSTATE is enabled.
|
||||
#
|
||||
# To submit patches:
|
||||
#
|
||||
# *) Read Documentation/SubmittingPatches
|
||||
@@ -116,10 +122,26 @@ __git_ps1 ()
|
||||
fi
|
||||
fi
|
||||
|
||||
local w
|
||||
local i
|
||||
|
||||
if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then
|
||||
if test "$(git config --bool bash.showDirtyState)" != "false"; then
|
||||
git diff --no-ext-diff --ignore-submodules \
|
||||
--quiet --exit-code || w="*"
|
||||
if git rev-parse --quiet --verify HEAD >/dev/null; then
|
||||
git diff-index --cached --quiet \
|
||||
--ignore-submodules HEAD -- || i="+"
|
||||
else
|
||||
i="#"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "${1-}" ]; then
|
||||
printf "$1" "${b##refs/heads/}$r"
|
||||
printf "$1" "${b##refs/heads/}$w$i$r"
|
||||
else
|
||||
printf " (%s)" "${b##refs/heads/}$r"
|
||||
printf " (%s)" "${b##refs/heads/}$w$i$r"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
|
||||
# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge,
|
||||
# vimdiff, gvimdiff, and custom user-configurable tools.
|
||||
# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff,
|
||||
# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools.
|
||||
# This script is typically launched by using the 'git difftool'
|
||||
# convenience command.
|
||||
#
|
||||
@@ -73,6 +73,10 @@ launch_merge_tool () {
|
||||
> /dev/null 2>&1
|
||||
;;
|
||||
|
||||
kompare)
|
||||
"$merge_tool_path" "$LOCAL" "$REMOTE"
|
||||
;;
|
||||
|
||||
tkdiff)
|
||||
"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
|
||||
;;
|
||||
@@ -134,7 +138,7 @@ valid_custom_tool() {
|
||||
# Built-in merge tools are always valid.
|
||||
valid_tool() {
|
||||
case "$1" in
|
||||
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
|
||||
kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
|
||||
;; # happy
|
||||
*)
|
||||
if ! valid_custom_tool "$1"
|
||||
@@ -177,31 +181,24 @@ fi
|
||||
|
||||
# Try to guess an appropriate merge tool if no tool has been set.
|
||||
if test -z "$merge_tool"; then
|
||||
|
||||
# We have a $DISPLAY so try some common UNIX merge tools
|
||||
if test -n "$DISPLAY"; then
|
||||
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
|
||||
# If gnome then prefer meld
|
||||
if test -n "$GNOME_DESKTOP_SESSION_ID"; then
|
||||
merge_tool_candidates="meld $merge_tool_candidates"
|
||||
fi
|
||||
# If KDE then prefer kdiff3
|
||||
if test "$KDE_FULL_SESSION" = "true"; then
|
||||
merge_tool_candidates="kdiff3 $merge_tool_candidates"
|
||||
# If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
|
||||
if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
|
||||
merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff"
|
||||
else
|
||||
merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff"
|
||||
fi
|
||||
fi
|
||||
|
||||
# $EDITOR is emacs so add emerge as a candidate
|
||||
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
|
||||
merge_tool_candidates="$merge_tool_candidates emerge"
|
||||
# $EDITOR is emacs so add emerge as a candidate
|
||||
merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
|
||||
elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
|
||||
# $EDITOR is vim so add vimdiff as a candidate
|
||||
merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
|
||||
else
|
||||
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
|
||||
fi
|
||||
|
||||
# $EDITOR is vim so add vimdiff as a candidate
|
||||
if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
|
||||
merge_tool_candidates="$merge_tool_candidates vimdiff"
|
||||
fi
|
||||
|
||||
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
|
||||
echo "merge tool candidates: $merge_tool_candidates"
|
||||
|
||||
# Loop over each candidate and stop when a valid merge tool is found.
|
||||
|
||||
@@ -28,7 +28,8 @@ OPTIONS
|
||||
--tool=<tool>::
|
||||
Use the merge resolution program specified by <tool>.
|
||||
Valid merge tools are:
|
||||
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
|
||||
kdiff3, kompare, tkdiff, meld, xxdiff, emerge,
|
||||
vimdiff, gvimdiff, ecmerge, and opendiff
|
||||
+
|
||||
If a merge resolution program is not specified, 'git-difftool'
|
||||
will use the configuration variable `merge.tool`. If the
|
||||
|
||||
180
contrib/git-resurrect.sh
Executable file
180
contrib/git-resurrect.sh
Executable file
@@ -0,0 +1,180 @@
|
||||
#!/bin/sh
|
||||
|
||||
USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>"
|
||||
LONG_USAGE="git-resurrect attempts to find traces of a branch tip
|
||||
called <name>, and tries to resurrect it. Currently, the reflog is
|
||||
searched for checkout messages, and with -r also merge messages. With
|
||||
-m and -t, the history of all refs is scanned for Merge <name> into
|
||||
other/Merge <other> into <name> (respectively) commit subjects, which
|
||||
is rather slow but allows you to resurrect other people's topic
|
||||
branches."
|
||||
|
||||
OPTIONS_SPEC="\
|
||||
git resurrect $USAGE
|
||||
--
|
||||
b,branch= save branch as <newname> instead of <name>
|
||||
a,all same as -l -r -m -t
|
||||
k,keep-going full rev-list scan (instead of first match)
|
||||
l,reflog scan reflog for checkouts (enabled by default)
|
||||
r,reflog-merges scan for merges recorded in reflog
|
||||
m,merges scan for merges into other branches (slow)
|
||||
t,merge-targets scan for merges of other branches into <name>
|
||||
n,dry-run don't recreate the branch"
|
||||
|
||||
. git-sh-setup
|
||||
|
||||
search_reflog () {
|
||||
sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
|
||||
< "$GIT_DIR"/logs/HEAD
|
||||
}
|
||||
|
||||
search_reflog_merges () {
|
||||
git rev-parse $(
|
||||
sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \
|
||||
< "$GIT_DIR"/logs/HEAD
|
||||
)
|
||||
}
|
||||
|
||||
_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
|
||||
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
|
||||
|
||||
search_merges () {
|
||||
git rev-list --all --grep="Merge branch '$1'" \
|
||||
--pretty=tformat:"%P %s" |
|
||||
sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}"
|
||||
}
|
||||
|
||||
search_merge_targets () {
|
||||
git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \
|
||||
--pretty=tformat:"%H %s" --all |
|
||||
sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} "
|
||||
}
|
||||
|
||||
dry_run=
|
||||
early_exit=q
|
||||
scan_reflog=t
|
||||
scan_reflog_merges=
|
||||
scan_merges=
|
||||
scan_merge_targets=
|
||||
new_name=
|
||||
|
||||
while test "$#" != 0; do
|
||||
case "$1" in
|
||||
-b|--branch)
|
||||
shift
|
||||
new_name="$1"
|
||||
;;
|
||||
-n|--dry-run)
|
||||
dry_run=t
|
||||
;;
|
||||
--no-dry-run)
|
||||
dry_run=
|
||||
;;
|
||||
-k|--keep-going)
|
||||
early_exit=
|
||||
;;
|
||||
--no-keep-going)
|
||||
early_exit=q
|
||||
;;
|
||||
-m|--merges)
|
||||
scan_merges=t
|
||||
;;
|
||||
--no-merges)
|
||||
scan_merges=
|
||||
;;
|
||||
-l|--reflog)
|
||||
scan_reflog=t
|
||||
;;
|
||||
--no-reflog)
|
||||
scan_reflog=
|
||||
;;
|
||||
-r|--reflog_merges)
|
||||
scan_reflog_merges=t
|
||||
;;
|
||||
--no-reflog_merges)
|
||||
scan_reflog_merges=
|
||||
;;
|
||||
-t|--merge-targets)
|
||||
scan_merge_targets=t
|
||||
;;
|
||||
--no-merge-targets)
|
||||
scan_merge_targets=
|
||||
;;
|
||||
-a|--all)
|
||||
scan_reflog=t
|
||||
scan_reflog_merges=t
|
||||
scan_merges=t
|
||||
scan_merge_targets=t
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
test "$#" = 1 || usage
|
||||
|
||||
all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets"
|
||||
if test -z "$all_strategies"; then
|
||||
die "must enable at least one of -lrmt"
|
||||
fi
|
||||
|
||||
branch="$1"
|
||||
test -z "$new_name" && new_name="$branch"
|
||||
|
||||
if test ! -z "$scan_reflog"; then
|
||||
if test -r "$GIT_DIR"/logs/HEAD; then
|
||||
candidates="$(search_reflog $branch)"
|
||||
else
|
||||
die 'reflog scanning requested, but' \
|
||||
'$GIT_DIR/logs/HEAD not readable'
|
||||
fi
|
||||
fi
|
||||
if test ! -z "$scan_reflog_merges"; then
|
||||
if test -r "$GIT_DIR"/logs/HEAD; then
|
||||
candidates="$candidates $(search_reflog_merges $branch)"
|
||||
else
|
||||
die 'reflog scanning requested, but' \
|
||||
'$GIT_DIR/logs/HEAD not readable'
|
||||
fi
|
||||
fi
|
||||
if test ! -z "$scan_merges"; then
|
||||
candidates="$candidates $(search_merges $branch)"
|
||||
fi
|
||||
if test ! -z "$scan_merge_targets"; then
|
||||
candidates="$candidates $(search_merge_targets $branch)"
|
||||
fi
|
||||
|
||||
candidates="$(git rev-parse $candidates | sort -u)"
|
||||
|
||||
if test -z "$candidates"; then
|
||||
hint=
|
||||
test "z$all_strategies" != "ztttt" \
|
||||
&& hint=" (maybe try again with -a)"
|
||||
die "no candidates for $branch found$hint"
|
||||
fi
|
||||
|
||||
echo "** Candidates for $branch **"
|
||||
for cmt in $candidates; do
|
||||
git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt
|
||||
done \
|
||||
| sort -n | cut -d: -f2-
|
||||
|
||||
newest="$(git rev-list -1 $candidates)"
|
||||
if test ! -z "$dry_run"; then
|
||||
printf "** Most recent: "
|
||||
git --no-pager log -1 --pretty=tformat:"%h %s" $newest
|
||||
elif ! git rev-parse --verify --quiet $new_name >/dev/null; then
|
||||
printf "** Restoring $new_name to "
|
||||
git --no-pager log -1 --pretty=tformat:"%h %s" $newest
|
||||
git branch $new_name $newest
|
||||
else
|
||||
printf "Most recent: "
|
||||
git --no-pager log -1 --pretty=tformat:"%h %s" $newest
|
||||
echo "** $new_name already exists, doing nothing"
|
||||
fi
|
||||
@@ -40,7 +40,7 @@ static int get_mode(const char *path, int *mode)
|
||||
*mode = 0;
|
||||
else if (!strcmp(path, "-"))
|
||||
*mode = create_ce_mode(0666);
|
||||
else if (stat(path, &st))
|
||||
else if (lstat(path, &st))
|
||||
return error("Could not access '%s'", path);
|
||||
else
|
||||
*mode = st.st_mode;
|
||||
|
||||
254
diff.c
254
diff.c
@@ -12,6 +12,7 @@
|
||||
#include "run-command.h"
|
||||
#include "utf8.h"
|
||||
#include "userdiff.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
#ifdef NO_FAST_WORKING_DIRECTORY
|
||||
#define FAST_WORKING_DIRECTORY 0
|
||||
@@ -170,6 +171,33 @@ static struct diff_tempfile {
|
||||
char tmp_path[PATH_MAX];
|
||||
} diff_temp[2];
|
||||
|
||||
static struct diff_tempfile *claim_diff_tempfile(void) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
|
||||
if (!diff_temp[i].name)
|
||||
return diff_temp + i;
|
||||
die("BUG: diff is failing to clean up its tempfiles");
|
||||
}
|
||||
|
||||
static int remove_tempfile_installed;
|
||||
|
||||
static void remove_tempfile(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
|
||||
if (diff_temp[i].name == diff_temp[i].tmp_path) {
|
||||
unlink(diff_temp[i].name);
|
||||
diff_temp[i].name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_tempfile_on_signal(int signo)
|
||||
{
|
||||
remove_tempfile();
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
static int count_lines(const char *data, int size)
|
||||
{
|
||||
int count, ch, completely_empty = 1, nl_just_seen = 0;
|
||||
@@ -1940,10 +1968,11 @@ static void prep_temp_blob(struct diff_tempfile *temp,
|
||||
sprintf(temp->mode, "%06o", mode);
|
||||
}
|
||||
|
||||
static void prepare_temp_file(const char *name,
|
||||
struct diff_tempfile *temp,
|
||||
struct diff_filespec *one)
|
||||
static struct diff_tempfile *prepare_temp_file(const char *name,
|
||||
struct diff_filespec *one)
|
||||
{
|
||||
struct diff_tempfile *temp = claim_diff_tempfile();
|
||||
|
||||
if (!DIFF_FILE_VALID(one)) {
|
||||
not_a_valid_file:
|
||||
/* A '-' entry produces this for file-2, and
|
||||
@@ -1952,7 +1981,13 @@ static void prepare_temp_file(const char *name,
|
||||
temp->name = "/dev/null";
|
||||
strcpy(temp->hex, ".");
|
||||
strcpy(temp->mode, ".");
|
||||
return;
|
||||
return temp;
|
||||
}
|
||||
|
||||
if (!remove_tempfile_installed) {
|
||||
atexit(remove_tempfile);
|
||||
sigchain_push_common(remove_tempfile_on_signal);
|
||||
remove_tempfile_installed = 1;
|
||||
}
|
||||
|
||||
if (!one->sha1_valid ||
|
||||
@@ -1992,7 +2027,7 @@ static void prepare_temp_file(const char *name,
|
||||
*/
|
||||
sprintf(temp->mode, "%06o", one->mode);
|
||||
}
|
||||
return;
|
||||
return temp;
|
||||
}
|
||||
else {
|
||||
if (diff_populate_filespec(one, 0))
|
||||
@@ -2000,24 +2035,7 @@ static void prepare_temp_file(const char *name,
|
||||
prep_temp_blob(temp, one->data, one->size,
|
||||
one->sha1, one->mode);
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_tempfile(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
if (diff_temp[i].name == diff_temp[i].tmp_path) {
|
||||
unlink(diff_temp[i].name);
|
||||
diff_temp[i].name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_tempfile_on_signal(int signo)
|
||||
{
|
||||
remove_tempfile();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
raise(signo);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* An external diff command takes:
|
||||
@@ -2035,34 +2053,22 @@ static void run_external_diff(const char *pgm,
|
||||
int complete_rewrite)
|
||||
{
|
||||
const char *spawn_arg[10];
|
||||
struct diff_tempfile *temp = diff_temp;
|
||||
int retval;
|
||||
static int atexit_asked = 0;
|
||||
const char *othername;
|
||||
const char **arg = &spawn_arg[0];
|
||||
|
||||
othername = (other? other : name);
|
||||
if (one && two) {
|
||||
prepare_temp_file(name, &temp[0], one);
|
||||
prepare_temp_file(othername, &temp[1], two);
|
||||
if (! atexit_asked &&
|
||||
(temp[0].name == temp[0].tmp_path ||
|
||||
temp[1].name == temp[1].tmp_path)) {
|
||||
atexit_asked = 1;
|
||||
atexit(remove_tempfile);
|
||||
}
|
||||
signal(SIGINT, remove_tempfile_on_signal);
|
||||
}
|
||||
|
||||
if (one && two) {
|
||||
struct diff_tempfile *temp_one, *temp_two;
|
||||
const char *othername = (other ? other : name);
|
||||
temp_one = prepare_temp_file(name, one);
|
||||
temp_two = prepare_temp_file(othername, two);
|
||||
*arg++ = pgm;
|
||||
*arg++ = name;
|
||||
*arg++ = temp[0].name;
|
||||
*arg++ = temp[0].hex;
|
||||
*arg++ = temp[0].mode;
|
||||
*arg++ = temp[1].name;
|
||||
*arg++ = temp[1].hex;
|
||||
*arg++ = temp[1].mode;
|
||||
*arg++ = temp_one->name;
|
||||
*arg++ = temp_one->hex;
|
||||
*arg++ = temp_one->mode;
|
||||
*arg++ = temp_two->name;
|
||||
*arg++ = temp_two->hex;
|
||||
*arg++ = temp_two->mode;
|
||||
if (other) {
|
||||
*arg++ = other;
|
||||
*arg++ = xfrm_msg;
|
||||
@@ -2081,16 +2087,86 @@ static void run_external_diff(const char *pgm,
|
||||
}
|
||||
}
|
||||
|
||||
static int similarity_index(struct diff_filepair *p)
|
||||
{
|
||||
return p->score * 100 / MAX_SCORE;
|
||||
}
|
||||
|
||||
static void fill_metainfo(struct strbuf *msg,
|
||||
const char *name,
|
||||
const char *other,
|
||||
struct diff_filespec *one,
|
||||
struct diff_filespec *two,
|
||||
struct diff_options *o,
|
||||
struct diff_filepair *p)
|
||||
{
|
||||
strbuf_init(msg, PATH_MAX * 2 + 300);
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_COPIED:
|
||||
strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
|
||||
strbuf_addstr(msg, "\ncopy from ");
|
||||
quote_c_style(name, msg, NULL, 0);
|
||||
strbuf_addstr(msg, "\ncopy to ");
|
||||
quote_c_style(other, msg, NULL, 0);
|
||||
strbuf_addch(msg, '\n');
|
||||
break;
|
||||
case DIFF_STATUS_RENAMED:
|
||||
strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
|
||||
strbuf_addstr(msg, "\nrename from ");
|
||||
quote_c_style(name, msg, NULL, 0);
|
||||
strbuf_addstr(msg, "\nrename to ");
|
||||
quote_c_style(other, msg, NULL, 0);
|
||||
strbuf_addch(msg, '\n');
|
||||
break;
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
if (p->score) {
|
||||
strbuf_addf(msg, "dissimilarity index %d%%\n",
|
||||
similarity_index(p));
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
/* nothing */
|
||||
;
|
||||
}
|
||||
if (one && two && hashcmp(one->sha1, two->sha1)) {
|
||||
int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
|
||||
|
||||
if (DIFF_OPT_TST(o, BINARY)) {
|
||||
mmfile_t mf;
|
||||
if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
|
||||
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
|
||||
abbrev = 40;
|
||||
}
|
||||
strbuf_addf(msg, "index %.*s..%.*s",
|
||||
abbrev, sha1_to_hex(one->sha1),
|
||||
abbrev, sha1_to_hex(two->sha1));
|
||||
if (one->mode == two->mode)
|
||||
strbuf_addf(msg, " %06o", one->mode);
|
||||
strbuf_addch(msg, '\n');
|
||||
}
|
||||
if (msg->len)
|
||||
strbuf_setlen(msg, msg->len - 1);
|
||||
}
|
||||
|
||||
static void run_diff_cmd(const char *pgm,
|
||||
const char *name,
|
||||
const char *other,
|
||||
const char *attr_path,
|
||||
struct diff_filespec *one,
|
||||
struct diff_filespec *two,
|
||||
const char *xfrm_msg,
|
||||
struct strbuf *msg,
|
||||
struct diff_options *o,
|
||||
int complete_rewrite)
|
||||
struct diff_filepair *p)
|
||||
{
|
||||
const char *xfrm_msg = NULL;
|
||||
int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
|
||||
|
||||
if (msg) {
|
||||
fill_metainfo(msg, name, other, one, two, o, p);
|
||||
xfrm_msg = msg->len ? msg->buf : NULL;
|
||||
}
|
||||
|
||||
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
|
||||
pgm = NULL;
|
||||
else {
|
||||
@@ -2130,11 +2206,6 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
|
||||
hashclr(one->sha1);
|
||||
}
|
||||
|
||||
static int similarity_index(struct diff_filepair *p)
|
||||
{
|
||||
return p->score * 100 / MAX_SCORE;
|
||||
}
|
||||
|
||||
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
|
||||
{
|
||||
/* Strip the prefix but do not molest /dev/null and absolute paths */
|
||||
@@ -2148,13 +2219,11 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
{
|
||||
const char *pgm = external_diff();
|
||||
struct strbuf msg;
|
||||
char *xfrm_msg;
|
||||
struct diff_filespec *one = p->one;
|
||||
struct diff_filespec *two = p->two;
|
||||
const char *name;
|
||||
const char *other;
|
||||
const char *attr_path;
|
||||
int complete_rewrite = 0;
|
||||
|
||||
name = p->one->path;
|
||||
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
|
||||
@@ -2164,83 +2233,34 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p)) {
|
||||
run_diff_cmd(pgm, name, NULL, attr_path,
|
||||
NULL, NULL, NULL, o, 0);
|
||||
NULL, NULL, NULL, o, p);
|
||||
return;
|
||||
}
|
||||
|
||||
diff_fill_sha1_info(one);
|
||||
diff_fill_sha1_info(two);
|
||||
|
||||
strbuf_init(&msg, PATH_MAX * 2 + 300);
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_COPIED:
|
||||
strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
|
||||
strbuf_addstr(&msg, "\ncopy from ");
|
||||
quote_c_style(name, &msg, NULL, 0);
|
||||
strbuf_addstr(&msg, "\ncopy to ");
|
||||
quote_c_style(other, &msg, NULL, 0);
|
||||
strbuf_addch(&msg, '\n');
|
||||
break;
|
||||
case DIFF_STATUS_RENAMED:
|
||||
strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
|
||||
strbuf_addstr(&msg, "\nrename from ");
|
||||
quote_c_style(name, &msg, NULL, 0);
|
||||
strbuf_addstr(&msg, "\nrename to ");
|
||||
quote_c_style(other, &msg, NULL, 0);
|
||||
strbuf_addch(&msg, '\n');
|
||||
break;
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
if (p->score) {
|
||||
strbuf_addf(&msg, "dissimilarity index %d%%\n",
|
||||
similarity_index(p));
|
||||
complete_rewrite = 1;
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
/* nothing */
|
||||
;
|
||||
}
|
||||
|
||||
if (hashcmp(one->sha1, two->sha1)) {
|
||||
int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
|
||||
|
||||
if (DIFF_OPT_TST(o, BINARY)) {
|
||||
mmfile_t mf;
|
||||
if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
|
||||
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
|
||||
abbrev = 40;
|
||||
}
|
||||
strbuf_addf(&msg, "index %.*s..%.*s",
|
||||
abbrev, sha1_to_hex(one->sha1),
|
||||
abbrev, sha1_to_hex(two->sha1));
|
||||
if (one->mode == two->mode)
|
||||
strbuf_addf(&msg, " %06o", one->mode);
|
||||
strbuf_addch(&msg, '\n');
|
||||
}
|
||||
|
||||
if (msg.len)
|
||||
strbuf_setlen(&msg, msg.len - 1);
|
||||
xfrm_msg = msg.len ? msg.buf : NULL;
|
||||
|
||||
if (!pgm &&
|
||||
DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
|
||||
(S_IFMT & one->mode) != (S_IFMT & two->mode)) {
|
||||
/* a filepair that changes between file and symlink
|
||||
/*
|
||||
* a filepair that changes between file and symlink
|
||||
* needs to be split into deletion and creation.
|
||||
*/
|
||||
struct diff_filespec *null = alloc_filespec(two->path);
|
||||
run_diff_cmd(NULL, name, other, attr_path,
|
||||
one, null, xfrm_msg, o, 0);
|
||||
one, null, &msg, o, p);
|
||||
free(null);
|
||||
strbuf_release(&msg);
|
||||
|
||||
null = alloc_filespec(one->path);
|
||||
run_diff_cmd(NULL, name, other, attr_path,
|
||||
null, two, xfrm_msg, o, 0);
|
||||
null, two, &msg, o, p);
|
||||
free(null);
|
||||
}
|
||||
else
|
||||
run_diff_cmd(pgm, name, other, attr_path,
|
||||
one, two, xfrm_msg, o, complete_rewrite);
|
||||
one, two, &msg, o, p);
|
||||
|
||||
strbuf_release(&msg);
|
||||
}
|
||||
@@ -3537,15 +3557,15 @@ void diff_unmerge(struct diff_options *options,
|
||||
static char *run_textconv(const char *pgm, struct diff_filespec *spec,
|
||||
size_t *outsize)
|
||||
{
|
||||
struct diff_tempfile temp;
|
||||
struct diff_tempfile *temp;
|
||||
const char *argv[3];
|
||||
const char **arg = argv;
|
||||
struct child_process child;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
prepare_temp_file(spec->path, &temp, spec);
|
||||
temp = prepare_temp_file(spec->path, spec);
|
||||
*arg++ = pgm;
|
||||
*arg++ = temp.name;
|
||||
*arg++ = temp->name;
|
||||
*arg = NULL;
|
||||
|
||||
memset(&child, 0, sizeof(child));
|
||||
@@ -3554,13 +3574,11 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
|
||||
if (start_command(&child) != 0 ||
|
||||
strbuf_read(&buf, child.out, 0) < 0 ||
|
||||
finish_command(&child) != 0) {
|
||||
if (temp.name == temp.tmp_path)
|
||||
unlink(temp.name);
|
||||
remove_tempfile();
|
||||
error("error running textconv command '%s'", pgm);
|
||||
return NULL;
|
||||
}
|
||||
if (temp.name == temp.tmp_path)
|
||||
unlink(temp.name);
|
||||
remove_tempfile();
|
||||
|
||||
return strbuf_detach(&buf, outsize);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
|
||||
|
||||
/* Parallel index stat data preload? */
|
||||
int core_preload_index = 0;
|
||||
char *notes_ref_name;
|
||||
|
||||
/* This is set by setup_git_dir_gently() and/or git_default_config() */
|
||||
char *git_work_tree_cfg;
|
||||
|
||||
@@ -801,6 +801,7 @@ n - do not stage this hunk
|
||||
a - stage this and all the remaining hunks in the file
|
||||
d - do not stage this hunk nor any of the remaining hunks in the file
|
||||
g - select a hunk to go to
|
||||
/ - search for a hunk matching the given regex
|
||||
j - leave this hunk undecided, see next undecided hunk
|
||||
J - leave this hunk undecided, see next hunk
|
||||
k - leave this hunk undecided, see previous undecided hunk
|
||||
@@ -929,25 +930,25 @@ sub patch_update_file {
|
||||
for ($i = 0; $i < $ix; $i++) {
|
||||
if (!defined $hunk[$i]{USE}) {
|
||||
$prev = 1;
|
||||
$other .= '/k';
|
||||
$other .= ',k';
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($ix) {
|
||||
$other .= '/K';
|
||||
$other .= ',K';
|
||||
}
|
||||
for ($i = $ix + 1; $i < $num; $i++) {
|
||||
if (!defined $hunk[$i]{USE}) {
|
||||
$next = 1;
|
||||
$other .= '/j';
|
||||
$other .= ',j';
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($ix < $num - 1) {
|
||||
$other .= '/J';
|
||||
$other .= ',J';
|
||||
}
|
||||
if ($num > 1) {
|
||||
$other .= '/g';
|
||||
$other .= ',g';
|
||||
}
|
||||
for ($i = 0; $i < $num; $i++) {
|
||||
if (!defined $hunk[$i]{USE}) {
|
||||
@@ -958,13 +959,13 @@ sub patch_update_file {
|
||||
last if (!$undecided);
|
||||
|
||||
if (hunk_splittable($hunk[$ix]{TEXT})) {
|
||||
$other .= '/s';
|
||||
$other .= ',s';
|
||||
}
|
||||
$other .= '/e';
|
||||
$other .= ',e';
|
||||
for (@{$hunk[$ix]{DISPLAY}}) {
|
||||
print;
|
||||
}
|
||||
print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? ";
|
||||
print colored $prompt_color, "Stage this hunk [y,n,a,d,/$other,?]? ";
|
||||
my $line = <STDIN>;
|
||||
if ($line) {
|
||||
if ($line =~ /^y/i) {
|
||||
@@ -993,6 +994,9 @@ sub patch_update_file {
|
||||
}
|
||||
print "go to which hunk$extra? ";
|
||||
$response = <STDIN>;
|
||||
if (!defined $response) {
|
||||
$response = '';
|
||||
}
|
||||
chomp $response;
|
||||
}
|
||||
if ($response !~ /^\s*\d+\s*$/) {
|
||||
@@ -1013,30 +1017,68 @@ sub patch_update_file {
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($other =~ /K/ && $line =~ /^K/) {
|
||||
$ix--;
|
||||
next;
|
||||
}
|
||||
elsif ($other =~ /J/ && $line =~ /^J/) {
|
||||
$ix++;
|
||||
next;
|
||||
}
|
||||
elsif ($other =~ /k/ && $line =~ /^k/) {
|
||||
elsif ($line =~ m|^/(.*)|) {
|
||||
my $search_string;
|
||||
eval {
|
||||
$search_string = qr{$1}m;
|
||||
};
|
||||
if ($@) {
|
||||
my ($err,$exp) = ($@, $1);
|
||||
$err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
|
||||
print STDERR "Malformed search regexp $exp: $err\n";
|
||||
next;
|
||||
}
|
||||
my $iy = $ix;
|
||||
while (1) {
|
||||
my $text = join ("", @{$hunk[$iy]{TEXT}});
|
||||
last if ($text =~ $search_string);
|
||||
$iy++;
|
||||
$iy = 0 if ($iy >= $num);
|
||||
if ($ix == $iy) {
|
||||
print STDERR "No hunk matches the given pattern\n";
|
||||
last;
|
||||
}
|
||||
}
|
||||
$ix = $iy;
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^K/) {
|
||||
if ($other =~ /K/) {
|
||||
$ix--;
|
||||
last if (!$ix ||
|
||||
!defined $hunk[$ix]{USE});
|
||||
}
|
||||
else {
|
||||
print STDERR "No previous hunk\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($other =~ /j/ && $line =~ /^j/) {
|
||||
while (1) {
|
||||
elsif ($line =~ /^J/) {
|
||||
if ($other =~ /J/) {
|
||||
$ix++;
|
||||
last if ($ix >= $num ||
|
||||
!defined $hunk[$ix]{USE});
|
||||
}
|
||||
else {
|
||||
print STDERR "No next hunk\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^k/) {
|
||||
if ($other =~ /k/) {
|
||||
while (1) {
|
||||
$ix--;
|
||||
last if (!$ix ||
|
||||
!defined $hunk[$ix]{USE});
|
||||
}
|
||||
}
|
||||
else {
|
||||
print STDERR "No previous hunk\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
elsif ($line =~ /^j/) {
|
||||
if ($other !~ /j/) {
|
||||
print STDERR "No next hunk\n";
|
||||
next;
|
||||
}
|
||||
}
|
||||
elsif ($other =~ /s/ && $line =~ /^s/) {
|
||||
my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
|
||||
if (1 < @split) {
|
||||
|
||||
33
git-am.sh
33
git-am.sh
@@ -8,9 +8,8 @@ OPTIONS_SPEC="\
|
||||
git am [options] [<mbox>|<Maildir>...]
|
||||
git am [options] (--resolved | --skip | --abort)
|
||||
--
|
||||
d,dotest= (removed -- do not use)
|
||||
i,interactive run interactively
|
||||
b,binary (historical option -- no-op)
|
||||
b,binary* (historical option -- no-op)
|
||||
3,3way allow fall back on 3way merging if needed
|
||||
s,signoff add a Signed-off-by line to the commit message
|
||||
u,utf8 recode into utf8 (default)
|
||||
@@ -24,7 +23,9 @@ resolvemsg= override error message when patch failure occurs
|
||||
r,resolved to be used after a patch failure
|
||||
skip skip the current patch
|
||||
abort restore the original branch and abort the patching operation.
|
||||
rebasing (internal use for git-rebase)"
|
||||
committer-date-is-author-date lie about committer date
|
||||
ignore-date use current timestamp for author date
|
||||
rebasing* (internal use for git-rebase)"
|
||||
|
||||
. git-sh-setup
|
||||
prefix=$(git rev-parse --show-prefix)
|
||||
@@ -134,6 +135,8 @@ dotest="$GIT_DIR/rebase-apply"
|
||||
sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
|
||||
resolvemsg= resume=
|
||||
git_apply_opt=
|
||||
committer_date_is_author_date=
|
||||
ignore_date=
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
@@ -171,6 +174,10 @@ do
|
||||
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
|
||||
--reject)
|
||||
git_apply_opt="$git_apply_opt $1" ;;
|
||||
--committer-date-is-author-date)
|
||||
committer_date_is_author_date=t ;;
|
||||
--ignore-date)
|
||||
ignore_date=t ;;
|
||||
--)
|
||||
shift; break ;;
|
||||
*)
|
||||
@@ -204,7 +211,7 @@ then
|
||||
# unreliable -- stdin could be /dev/null for example
|
||||
# and the caller did not intend to feed us a patch but
|
||||
# wanted to continue unattended.
|
||||
tty -s
|
||||
test -t 0
|
||||
;;
|
||||
*)
|
||||
false
|
||||
@@ -280,10 +287,7 @@ fi
|
||||
case "$resolved" in
|
||||
'')
|
||||
files=$(git diff-index --cached --name-only HEAD --) || exit
|
||||
if [ "$files" ]; then
|
||||
echo "Dirty index: cannot apply patches (dirty: $files)" >&2
|
||||
exit 1
|
||||
fi
|
||||
test "$files" && die "Dirty index: cannot apply patches (dirty: $files)"
|
||||
esac
|
||||
|
||||
if test "$(cat "$dotest/utf8")" = t
|
||||
@@ -524,7 +528,18 @@ do
|
||||
|
||||
tree=$(git write-tree) &&
|
||||
parent=$(git rev-parse --verify HEAD) &&
|
||||
commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
|
||||
commit=$(
|
||||
if test -n "$ignore_date"
|
||||
then
|
||||
GIT_AUTHOR_DATE=
|
||||
fi
|
||||
if test -n "$committer_date_is_author_date"
|
||||
then
|
||||
GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
||||
export GIT_COMMITTER_DATE
|
||||
fi &&
|
||||
git commit-tree $tree -p $parent <"$dotest/final-commit"
|
||||
) &&
|
||||
git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
|
||||
stop_here $this
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ my $methods = {
|
||||
'history' => \&req_CATCHALL,
|
||||
'watchers' => \&req_EMPTY,
|
||||
'editors' => \&req_EMPTY,
|
||||
'noop' => \&req_EMPTY,
|
||||
'annotate' => \&req_annotate,
|
||||
'Global_option' => \&req_Globaloption,
|
||||
#'annotate' => \&req_CATCHALL,
|
||||
@@ -1413,14 +1414,14 @@ sub req_ci
|
||||
close $pipe || die "bad pipe: $! $?";
|
||||
}
|
||||
|
||||
$updater->update();
|
||||
|
||||
### Then hooks/post-update
|
||||
$hook = $ENV{GIT_DIR}.'hooks/post-update';
|
||||
if (-x $hook) {
|
||||
system($hook, "refs/heads/$state->{module}");
|
||||
}
|
||||
|
||||
$updater->update();
|
||||
|
||||
# foreach file specified on the command line ...
|
||||
foreach my $filename ( @committedfiles )
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@ SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
prefix=$(git rev-parse --show-prefix)
|
||||
|
||||
# Returns true if the mode reflects a symlink
|
||||
is_symlink () {
|
||||
@@ -127,6 +126,14 @@ check_unchanged () {
|
||||
fi
|
||||
}
|
||||
|
||||
checkout_staged_file () {
|
||||
tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^ ]*\) ')
|
||||
|
||||
if test $? -eq 0 -a -n "$tmpfile" ; then
|
||||
mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
|
||||
fi
|
||||
}
|
||||
|
||||
merge_file () {
|
||||
MERGED="$1"
|
||||
|
||||
@@ -153,9 +160,9 @@ merge_file () {
|
||||
local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
|
||||
remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
|
||||
|
||||
base_present && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
|
||||
local_present && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
|
||||
remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
|
||||
base_present && checkout_staged_file 1 "$MERGED" "$BASE"
|
||||
local_present && checkout_staged_file 2 "$MERGED" "$LOCAL"
|
||||
remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE"
|
||||
|
||||
if test -z "$local_mode" -o -z "$remote_mode"; then
|
||||
echo "Deleted merge conflict for '$MERGED':"
|
||||
@@ -390,21 +397,19 @@ fi
|
||||
|
||||
if test -z "$merge_tool" ; then
|
||||
if test -n "$DISPLAY"; then
|
||||
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
|
||||
if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
|
||||
merge_tool_candidates="meld $merge_tool_candidates"
|
||||
fi
|
||||
if test "$KDE_FULL_SESSION" = "true"; then
|
||||
merge_tool_candidates="kdiff3 $merge_tool_candidates"
|
||||
merge_tool_candidates="meld kdiff3 tkdiff xxdiff gvimdiff"
|
||||
else
|
||||
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
|
||||
fi
|
||||
fi
|
||||
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
|
||||
merge_tool_candidates="$merge_tool_candidates emerge"
|
||||
merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
|
||||
elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
|
||||
merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
|
||||
else
|
||||
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
|
||||
fi
|
||||
if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
|
||||
merge_tool_candidates="$merge_tool_candidates vimdiff"
|
||||
fi
|
||||
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
|
||||
echo "merge tool candidates: $merge_tool_candidates"
|
||||
for i in $merge_tool_candidates; do
|
||||
init_merge_tool_path $i
|
||||
|
||||
65
git-notes.sh
Executable file
65
git-notes.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
|
||||
USAGE="(edit | show) [commit]"
|
||||
. git-sh-setup
|
||||
|
||||
test -n "$3" && usage
|
||||
|
||||
test -z "$1" && usage
|
||||
ACTION="$1"; shift
|
||||
|
||||
test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)"
|
||||
test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits"
|
||||
|
||||
COMMIT=$(git rev-parse --verify --default HEAD "$@") ||
|
||||
die "Invalid commit: $@"
|
||||
|
||||
MESSAGE="$GIT_DIR"/new-notes-$COMMIT
|
||||
trap '
|
||||
test -f "$MESSAGE" && rm "$MESSAGE"
|
||||
' 0
|
||||
|
||||
case "$ACTION" in
|
||||
edit)
|
||||
GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MESSAGE"
|
||||
|
||||
GIT_INDEX_FILE="$MESSAGE".idx
|
||||
export GIT_INDEX_FILE
|
||||
|
||||
CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ')
|
||||
if [ -z "$CURRENT_HEAD" ]; then
|
||||
PARENT=
|
||||
else
|
||||
PARENT="-p $CURRENT_HEAD"
|
||||
git read-tree "$GIT_NOTES_REF" || die "Could not read index"
|
||||
git cat-file blob :$COMMIT >> "$MESSAGE" 2> /dev/null
|
||||
fi
|
||||
|
||||
${VISUAL:-${EDITOR:-vi}} "$MESSAGE"
|
||||
|
||||
grep -v ^# < "$MESSAGE" | git stripspace > "$MESSAGE".processed
|
||||
mv "$MESSAGE".processed "$MESSAGE"
|
||||
if [ -s "$MESSAGE" ]; then
|
||||
BLOB=$(git hash-object -w "$MESSAGE") ||
|
||||
die "Could not write into object database"
|
||||
git update-index --add --cacheinfo 0644 $BLOB $COMMIT ||
|
||||
die "Could not write index"
|
||||
else
|
||||
test -z "$CURRENT_HEAD" &&
|
||||
die "Will not initialise with empty tree"
|
||||
git update-index --force-remove $COMMIT ||
|
||||
die "Could not update index"
|
||||
fi
|
||||
|
||||
TREE=$(git write-tree) || die "Could not write tree"
|
||||
NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||
|
||||
die "Could not annotate"
|
||||
git update-ref -m "Annotate $COMMIT" \
|
||||
"$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD
|
||||
;;
|
||||
show)
|
||||
git show "$GIT_NOTES_REF":$COMMIT
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
esac
|
||||
@@ -373,17 +373,15 @@ do_next () {
|
||||
pick_one -n $sha1 || failed=t
|
||||
case "$(peek_next_command)" in
|
||||
squash|s)
|
||||
EDIT_COMMIT=
|
||||
USE_OUTPUT=output
|
||||
MSG_OPT=-F
|
||||
MSG_FILE="$MSG"
|
||||
EDIT_OR_FILE="$MSG"
|
||||
cp "$MSG" "$SQUASH_MSG"
|
||||
;;
|
||||
*)
|
||||
EDIT_COMMIT=-e
|
||||
USE_OUTPUT=
|
||||
MSG_OPT=
|
||||
MSG_FILE=
|
||||
EDIT_OR_FILE=-e
|
||||
rm -f "$SQUASH_MSG" || exit
|
||||
cp "$MSG" "$GIT_DIR"/SQUASH_MSG
|
||||
rm -f "$GIT_DIR"/MERGE_MSG || exit
|
||||
@@ -397,7 +395,8 @@ do_next () {
|
||||
GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
|
||||
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
|
||||
GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
|
||||
$USE_OUTPUT git commit --no-verify $MSG_OPT "$MSG_FILE" $EDIT_COMMIT || failed=t
|
||||
$USE_OUTPUT git commit --no-verify \
|
||||
$MSG_OPT "$EDIT_OR_FILE" || failed=t
|
||||
fi
|
||||
if test $failed = t
|
||||
then
|
||||
|
||||
21
git.c
21
git.c
@@ -2,6 +2,7 @@
|
||||
#include "exec_cmd.h"
|
||||
#include "cache.h"
|
||||
#include "quote.h"
|
||||
#include "run-command.h"
|
||||
|
||||
const char git_usage_string[] =
|
||||
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
|
||||
@@ -219,7 +220,7 @@ struct cmd_struct {
|
||||
int option;
|
||||
};
|
||||
|
||||
static int run_command(struct cmd_struct *p, int argc, const char **argv)
|
||||
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
{
|
||||
int status;
|
||||
struct stat st;
|
||||
@@ -384,7 +385,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
struct cmd_struct *p = commands+i;
|
||||
if (strcmp(p->cmd, cmd))
|
||||
continue;
|
||||
exit(run_command(p, argc, argv));
|
||||
exit(run_builtin(p, argc, argv));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,6 +393,7 @@ static void execv_dashed_external(const char **argv)
|
||||
{
|
||||
struct strbuf cmd = STRBUF_INIT;
|
||||
const char *tmp;
|
||||
int status;
|
||||
|
||||
strbuf_addf(&cmd, "git-%s", argv[0]);
|
||||
|
||||
@@ -406,10 +408,17 @@ static void execv_dashed_external(const char **argv)
|
||||
|
||||
trace_argv_printf(argv, "trace: exec:");
|
||||
|
||||
/* execvp() can only ever return if it fails */
|
||||
execvp(cmd.buf, (char **)argv);
|
||||
|
||||
trace_printf("trace: exec failed: %s\n", strerror(errno));
|
||||
/*
|
||||
* if we fail because the command is not found, it is
|
||||
* OK to return. Otherwise, we just pass along the status code.
|
||||
*/
|
||||
status = run_command_v_opt(argv, 0);
|
||||
if (status != -ERR_RUN_COMMAND_EXEC) {
|
||||
if (IS_RUN_COMMAND_ERR(status))
|
||||
die("unable to run '%s'", argv[0]);
|
||||
exit(-status);
|
||||
}
|
||||
errno = ENOENT; /* as if we called execvp */
|
||||
|
||||
argv[0] = tmp;
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ BuildRequires: perl(Error)
|
||||
%description -n perl-Git
|
||||
Perl interface to Git
|
||||
|
||||
%define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-core-%{version}
|
||||
%define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-%{version}
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
@@ -190,6 +190,9 @@ rm -rf $RPM_BUILD_ROOT
|
||||
# No files for you!
|
||||
|
||||
%changelog
|
||||
* Mon Feb 04 2009 David J. Mellor <dmellor@whistlingcat.com>
|
||||
- fixed broken git help -w after renaming the git-core package to git.
|
||||
|
||||
* Fri Sep 12 2008 Quy Tonthat <qtonthat@gmail.com>
|
||||
- move git-cvsserver to bindir.
|
||||
|
||||
|
||||
@@ -162,14 +162,12 @@ not include variables usually directly set during build):
|
||||
$GITWEB_LIST during installation. If empty, $projectroot is used
|
||||
to scan for repositories.
|
||||
* $my_url, $my_uri
|
||||
URL and absolute URL of gitweb script; you might need to set those
|
||||
variables if you are using 'pathinfo' feature: see also below.
|
||||
Full URL and absolute URL of gitweb script;
|
||||
in earlier versions of gitweb you might have need to set those
|
||||
variables, now there should be no need to do it.
|
||||
* $home_link
|
||||
Target of the home link on top of all pages (the first part of view
|
||||
"breadcrumbs"). By default set to absolute URI of a page; you might
|
||||
need to set it up to [base] gitweb URI if you use 'pathinfo' feature
|
||||
(alternative format of the URLs, with project name embedded directly
|
||||
in the path part of URL).
|
||||
"breadcrumbs"). By default set to absolute URI of a page ($my_uri).
|
||||
* @stylesheets
|
||||
List of URIs of stylesheets (relative to base URI of a page). You
|
||||
might specify more than one stylesheet, for example use gitweb.css
|
||||
@@ -322,6 +320,82 @@ something like the following in your gitweb.conf (or gitweb_config.perl) file:
|
||||
$home_link = "/";
|
||||
|
||||
|
||||
PATH_INFO usage
|
||||
-----------------------
|
||||
If you enable PATH_INFO usage in gitweb by putting
|
||||
|
||||
$feature{'pathinfo'}{'default'} = [1];
|
||||
|
||||
in your gitweb.conf, it is possible to set up your server so that it
|
||||
consumes and produces URLs in the form
|
||||
|
||||
http://git.example.com/project.git/shortlog/sometag
|
||||
|
||||
by using a configuration such as the following, that assumes that
|
||||
/var/www/gitweb is the DocumentRoot of your webserver, and that it
|
||||
contains the gitweb.cgi script and complementary static files
|
||||
(stylesheet, favicon):
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerAlias git.example.com
|
||||
|
||||
DocumentRoot /var/www/gitweb
|
||||
|
||||
<Directory /var/www/gitweb>
|
||||
Options ExecCGI
|
||||
AddHandler cgi-script cgi
|
||||
|
||||
DirectoryIndex gitweb.cgi
|
||||
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
The rewrite rule guarantees that existing static files will be properly
|
||||
served, whereas any other URL will be passed to gitweb as PATH_INFO
|
||||
parameter.
|
||||
|
||||
Notice that in this case you don't need special settings for
|
||||
@stylesheets, $my_uri and $home_link, but you lose "dumb client" access
|
||||
to your project .git dirs. A possible workaround for the latter is the
|
||||
following: in your project root dir (e.g. /pub/git) have the projects
|
||||
named without a .git extension (e.g. /pub/git/project instead of
|
||||
/pub/git/project.git) and configure Apache as follows:
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerAlias git.example.com
|
||||
|
||||
DocumentRoot /var/www/gitweb
|
||||
|
||||
AliasMatch ^(/.*?)(\.git)(/.*)? /pub/git$1$3
|
||||
<Directory /var/www/gitweb>
|
||||
Options ExecCGI
|
||||
AddHandler cgi-script cgi
|
||||
|
||||
DirectoryIndex gitweb.cgi
|
||||
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
The additional AliasMatch makes it so that
|
||||
|
||||
http://git.example.com/project.git
|
||||
|
||||
will give raw access to the project's git dir (so that the project can
|
||||
be cloned), while
|
||||
|
||||
http://git.example.com/project
|
||||
|
||||
will provide human-friendly gitweb access.
|
||||
|
||||
|
||||
Originally written by:
|
||||
Kay Sievers <kay.sievers@vrfy.org>
|
||||
|
||||
|
||||
@@ -2901,9 +2901,14 @@ sub git_header_html {
|
||||
<meta name="robots" content="index, nofollow"/>
|
||||
<title>$title</title>
|
||||
EOF
|
||||
# print out each stylesheet that exist
|
||||
# the stylesheet, favicon etc urls won't work correctly with path_info
|
||||
# unless we set the appropriate base URL
|
||||
if ($ENV{'PATH_INFO'}) {
|
||||
print '<base href="'.esc_url($my_url).'" />\n';
|
||||
}
|
||||
# print out each stylesheet that exist, providing backwards capability
|
||||
# for those people who defined $stylesheet in a config file
|
||||
if (defined $stylesheet) {
|
||||
#provides backwards capability for those people who define style sheet in a config file
|
||||
print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
|
||||
} else {
|
||||
foreach my $stylesheet (@stylesheets) {
|
||||
@@ -6015,7 +6020,25 @@ sub git_feed {
|
||||
}
|
||||
if (defined($commitlist[0])) {
|
||||
%latest_commit = %{$commitlist[0]};
|
||||
%latest_date = parse_date($latest_commit{'author_epoch'});
|
||||
my $latest_epoch = $latest_commit{'committer_epoch'};
|
||||
%latest_date = parse_date($latest_epoch);
|
||||
my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
|
||||
if (defined $if_modified) {
|
||||
my $since;
|
||||
if (eval { require HTTP::Date; 1; }) {
|
||||
$since = HTTP::Date::str2time($if_modified);
|
||||
} elsif (eval { require Time::ParseDate; 1; }) {
|
||||
$since = Time::ParseDate::parsedate($if_modified, GMT => 1);
|
||||
}
|
||||
if (defined $since && $latest_epoch <= $since) {
|
||||
print $cgi->header(
|
||||
-type => $content_type,
|
||||
-charset => 'utf-8',
|
||||
-last_modified => $latest_date{'rfc2822'},
|
||||
-status => '304 Not Modified');
|
||||
return;
|
||||
}
|
||||
}
|
||||
print $cgi->header(
|
||||
-type => $content_type,
|
||||
-charset => 'utf-8',
|
||||
@@ -6074,7 +6097,24 @@ XML
|
||||
print "<title>$title</title>\n" .
|
||||
"<link>$alt_url</link>\n" .
|
||||
"<description>$descr</description>\n" .
|
||||
"<language>en</language>\n";
|
||||
"<language>en</language>\n" .
|
||||
# project owner is responsible for 'editorial' content
|
||||
"<managingEditor>$owner</managingEditor>\n";
|
||||
if (defined $logo || defined $favicon) {
|
||||
# prefer the logo to the favicon, since RSS
|
||||
# doesn't allow both
|
||||
my $img = esc_url($logo || $favicon);
|
||||
print "<image>\n" .
|
||||
"<url>$img</url>\n" .
|
||||
"<title>$title</title>\n" .
|
||||
"<link>$alt_url</link>\n" .
|
||||
"</image>\n";
|
||||
}
|
||||
if (%latest_date) {
|
||||
print "<pubDate>$latest_date{'rfc2822'}</pubDate>\n";
|
||||
print "<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\n";
|
||||
}
|
||||
print "<generator>gitweb v.$version/$git_version</generator>\n";
|
||||
} elsif ($format eq 'atom') {
|
||||
print <<XML;
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
@@ -6101,6 +6141,7 @@ XML
|
||||
} else {
|
||||
print "<updated>$latest_date{'iso-8601'}</updated>\n";
|
||||
}
|
||||
print "<generator version='$version/$git_version'>gitweb</generator>\n";
|
||||
}
|
||||
|
||||
# contents
|
||||
|
||||
75
http-push.c
75
http-push.c
@@ -10,6 +10,7 @@
|
||||
#include "exec_cmd.h"
|
||||
#include "remote.h"
|
||||
#include "list-objects.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
#include <expat.h>
|
||||
|
||||
@@ -209,6 +210,15 @@ static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum d
|
||||
return dav_headers;
|
||||
}
|
||||
|
||||
static void append_remote_object_url(struct strbuf *buf, const char *url,
|
||||
const char *hex,
|
||||
int only_two_digit_prefix)
|
||||
{
|
||||
strbuf_addf(buf, "%sobjects/%.*s/", url, 2, hex);
|
||||
if (!only_two_digit_prefix)
|
||||
strbuf_addf(buf, "%s", hex+2);
|
||||
}
|
||||
|
||||
static void finish_request(struct transfer_request *request);
|
||||
static void release_request(struct transfer_request *request);
|
||||
|
||||
@@ -221,6 +231,15 @@ static void process_response(void *callback_data)
|
||||
}
|
||||
|
||||
#ifdef USE_CURL_MULTI
|
||||
|
||||
static char *get_remote_object_url(const char *url, const char *hex,
|
||||
int only_two_digit_prefix)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
|
||||
void *data)
|
||||
{
|
||||
@@ -255,7 +274,6 @@ static void start_fetch_loose(struct transfer_request *request)
|
||||
char *filename;
|
||||
char prevfile[PATH_MAX];
|
||||
char *url;
|
||||
char *posn;
|
||||
int prevlocal;
|
||||
unsigned char prev_buf[PREV_BUF_SIZE];
|
||||
ssize_t prev_read = 0;
|
||||
@@ -305,17 +323,8 @@ static void start_fetch_loose(struct transfer_request *request)
|
||||
|
||||
git_SHA1_Init(&request->c);
|
||||
|
||||
url = xmalloc(strlen(remote->url) + 50);
|
||||
request->url = xmalloc(strlen(remote->url) + 50);
|
||||
strcpy(url, remote->url);
|
||||
posn = url + strlen(remote->url);
|
||||
strcpy(posn, "objects/");
|
||||
posn += 8;
|
||||
memcpy(posn, hex, 2);
|
||||
posn += 2;
|
||||
*(posn++) = '/';
|
||||
strcpy(posn, hex + 2);
|
||||
strcpy(request->url, url);
|
||||
url = get_remote_object_url(remote->url, hex, 0);
|
||||
request->url = xstrdup(url);
|
||||
|
||||
/* If a previous temp file is present, process what was already
|
||||
fetched. */
|
||||
@@ -388,16 +397,8 @@ static void start_mkcol(struct transfer_request *request)
|
||||
{
|
||||
char *hex = sha1_to_hex(request->obj->sha1);
|
||||
struct active_request_slot *slot;
|
||||
char *posn;
|
||||
|
||||
request->url = xmalloc(strlen(remote->url) + 13);
|
||||
strcpy(request->url, remote->url);
|
||||
posn = request->url + strlen(remote->url);
|
||||
strcpy(posn, "objects/");
|
||||
posn += 8;
|
||||
memcpy(posn, hex, 2);
|
||||
posn += 2;
|
||||
strcpy(posn, "/");
|
||||
request->url = get_remote_object_url(remote->url, hex, 1);
|
||||
|
||||
slot = get_active_slot();
|
||||
slot->callback_func = process_response;
|
||||
@@ -512,7 +513,7 @@ static void start_put(struct transfer_request *request)
|
||||
{
|
||||
char *hex = sha1_to_hex(request->obj->sha1);
|
||||
struct active_request_slot *slot;
|
||||
char *posn;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
enum object_type type;
|
||||
char hdr[50];
|
||||
void *unpacked;
|
||||
@@ -551,21 +552,14 @@ static void start_put(struct transfer_request *request)
|
||||
|
||||
request->buffer.buf.len = stream.total_out;
|
||||
|
||||
request->url = xmalloc(strlen(remote->url) +
|
||||
strlen(request->lock->token) + 51);
|
||||
strcpy(request->url, remote->url);
|
||||
posn = request->url + strlen(remote->url);
|
||||
strcpy(posn, "objects/");
|
||||
posn += 8;
|
||||
memcpy(posn, hex, 2);
|
||||
posn += 2;
|
||||
*(posn++) = '/';
|
||||
strcpy(posn, hex + 2);
|
||||
request->dest = xmalloc(strlen(request->url) + 14);
|
||||
sprintf(request->dest, "Destination: %s", request->url);
|
||||
posn += 38;
|
||||
*(posn++) = '_';
|
||||
strcpy(posn, request->lock->token);
|
||||
strbuf_addstr(&buf, "Destination: ");
|
||||
append_remote_object_url(&buf, remote->url, hex, 0);
|
||||
request->dest = strbuf_detach(&buf, NULL);
|
||||
|
||||
append_remote_object_url(&buf, remote->url, hex, 0);
|
||||
strbuf_addstr(&buf, "_");
|
||||
strbuf_addstr(&buf, request->lock->token);
|
||||
request->url = strbuf_detach(&buf, NULL);
|
||||
|
||||
slot = get_active_slot();
|
||||
slot->callback_func = process_response;
|
||||
@@ -1384,7 +1378,7 @@ static void remove_locks(void)
|
||||
static void remove_locks_on_signal(int signo)
|
||||
{
|
||||
remove_locks();
|
||||
signal(signo, SIG_DFL);
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
@@ -2279,10 +2273,7 @@ int main(int argc, char **argv)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
signal(SIGINT, remove_locks_on_signal);
|
||||
signal(SIGHUP, remove_locks_on_signal);
|
||||
signal(SIGQUIT, remove_locks_on_signal);
|
||||
signal(SIGTERM, remove_locks_on_signal);
|
||||
sigchain_push_common(remove_locks_on_signal);
|
||||
|
||||
/* Check whether the remote has server info files */
|
||||
remote->can_update_info_refs = 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2005, Junio C Hamano
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
static struct lock_file *lock_file_list;
|
||||
static const char *alternate_index_output;
|
||||
@@ -24,7 +25,7 @@ static void remove_lock_file(void)
|
||||
static void remove_lock_file_on_signal(int signo)
|
||||
{
|
||||
remove_lock_file();
|
||||
signal(signo, SIG_DFL);
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
@@ -136,11 +137,7 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
|
||||
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
|
||||
if (0 <= lk->fd) {
|
||||
if (!lock_file_list) {
|
||||
signal(SIGINT, remove_lock_file_on_signal);
|
||||
signal(SIGHUP, remove_lock_file_on_signal);
|
||||
signal(SIGTERM, remove_lock_file_on_signal);
|
||||
signal(SIGQUIT, remove_lock_file_on_signal);
|
||||
signal(SIGPIPE, remove_lock_file_on_signal);
|
||||
sigchain_push_common(remove_lock_file_on_signal);
|
||||
atexit(remove_lock_file);
|
||||
}
|
||||
lk->owner = getpid();
|
||||
|
||||
@@ -92,7 +92,9 @@ int main(int argc, char **argv)
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
if (argc < 3)
|
||||
usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
|
||||
usage("git merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
|
||||
|
||||
git_extract_argv0_path(argv[0]);
|
||||
|
||||
git_extract_argv0_path(argv[0]);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "blob.h"
|
||||
#include "exec_cmd.h"
|
||||
|
||||
static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
|
||||
static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
|
||||
static int resolve_directories = 1;
|
||||
|
||||
struct merge_list {
|
||||
|
||||
4
mktag.c
4
mktag.c
@@ -158,7 +158,9 @@ int main(int argc, char **argv)
|
||||
unsigned char result_sha1[20];
|
||||
|
||||
if (argc != 1)
|
||||
usage("git-mktag < signaturefile");
|
||||
usage("git mktag < signaturefile");
|
||||
|
||||
git_extract_argv0_path(argv[0]);
|
||||
|
||||
git_extract_argv0_path(argv[0]);
|
||||
|
||||
|
||||
2
mktree.c
2
mktree.c
@@ -62,7 +62,7 @@ static void write_tree(unsigned char *sha1)
|
||||
write_sha1_file(buf.buf, buf.len, tree_type, sha1);
|
||||
}
|
||||
|
||||
static const char mktree_usage[] = "git-mktree [-z]";
|
||||
static const char mktree_usage[] = "git mktree [-z]";
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
|
||||
160
notes.c
Normal file
160
notes.c
Normal file
@@ -0,0 +1,160 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "notes.h"
|
||||
#include "refs.h"
|
||||
#include "utf8.h"
|
||||
#include "strbuf.h"
|
||||
#include "tree-walk.h"
|
||||
|
||||
struct entry {
|
||||
unsigned char commit_sha1[20];
|
||||
unsigned char notes_sha1[20];
|
||||
};
|
||||
|
||||
struct hash_map {
|
||||
struct entry *entries;
|
||||
off_t count, size;
|
||||
};
|
||||
|
||||
static int initialized;
|
||||
static struct hash_map hash_map;
|
||||
|
||||
static int hash_index(struct hash_map *map, const unsigned char *sha1)
|
||||
{
|
||||
int i = ((*(unsigned int *)sha1) % map->size);
|
||||
|
||||
for (;;) {
|
||||
unsigned char *current = map->entries[i].commit_sha1;
|
||||
|
||||
if (!hashcmp(sha1, current))
|
||||
return i;
|
||||
|
||||
if (is_null_sha1(current))
|
||||
return -1 - i;
|
||||
|
||||
if (++i == map->size)
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_entry(const unsigned char *commit_sha1,
|
||||
const unsigned char *notes_sha1)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (hash_map.count + 1 > hash_map.size >> 1) {
|
||||
int i, old_size = hash_map.size;
|
||||
struct entry *old = hash_map.entries;
|
||||
|
||||
hash_map.size = old_size ? old_size << 1 : 64;
|
||||
hash_map.entries = (struct entry *)
|
||||
xcalloc(sizeof(struct entry), hash_map.size);
|
||||
|
||||
for (i = 0; i < old_size; i++)
|
||||
if (!is_null_sha1(old[i].commit_sha1)) {
|
||||
index = -1 - hash_index(&hash_map,
|
||||
old[i].commit_sha1);
|
||||
memcpy(hash_map.entries + index, old + i,
|
||||
sizeof(struct entry));
|
||||
}
|
||||
free(old);
|
||||
}
|
||||
|
||||
index = hash_index(&hash_map, commit_sha1);
|
||||
if (index < 0) {
|
||||
index = -1 - index;
|
||||
hash_map.count++;
|
||||
}
|
||||
|
||||
hashcpy(hash_map.entries[index].commit_sha1, commit_sha1);
|
||||
hashcpy(hash_map.entries[index].notes_sha1, notes_sha1);
|
||||
}
|
||||
|
||||
static void initialize_hash_map(const char *notes_ref_name)
|
||||
{
|
||||
unsigned char sha1[20], commit_sha1[20];
|
||||
unsigned mode;
|
||||
struct tree_desc desc;
|
||||
struct name_entry entry;
|
||||
void *buf;
|
||||
|
||||
if (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||
|
||||
get_tree_entry(commit_sha1, "", sha1, &mode))
|
||||
return;
|
||||
|
||||
buf = fill_tree_descriptor(&desc, sha1);
|
||||
if (!buf)
|
||||
die("Could not read %s for notes-index", sha1_to_hex(sha1));
|
||||
|
||||
while (tree_entry(&desc, &entry))
|
||||
if (!get_sha1(entry.path, commit_sha1))
|
||||
add_entry(commit_sha1, entry.sha1);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static unsigned char *lookup_notes(const unsigned char *commit_sha1)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!hash_map.size)
|
||||
return NULL;
|
||||
|
||||
index = hash_index(&hash_map, commit_sha1);
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
return hash_map.entries[index].notes_sha1;
|
||||
}
|
||||
|
||||
void get_commit_notes(const struct commit *commit, struct strbuf *sb,
|
||||
const char *output_encoding)
|
||||
{
|
||||
static const char *utf8 = "utf-8";
|
||||
unsigned char *sha1;
|
||||
char *msg, *msg_p;
|
||||
unsigned long linelen, msglen;
|
||||
enum object_type type;
|
||||
|
||||
if (!initialized) {
|
||||
const char *env = getenv(GIT_NOTES_REF_ENVIRONMENT);
|
||||
if (env)
|
||||
notes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);
|
||||
else if (!notes_ref_name)
|
||||
notes_ref_name = GIT_NOTES_DEFAULT_REF;
|
||||
initialize_hash_map(notes_ref_name);
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
sha1 = lookup_notes(commit->object.sha1);
|
||||
if (!sha1)
|
||||
return;
|
||||
|
||||
if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
|
||||
type != OBJ_BLOB)
|
||||
return;
|
||||
|
||||
if (output_encoding && *output_encoding &&
|
||||
strcmp(utf8, output_encoding)) {
|
||||
char *reencoded = reencode_string(msg, output_encoding, utf8);
|
||||
if (reencoded) {
|
||||
free(msg);
|
||||
msg = reencoded;
|
||||
msglen = strlen(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* we will end the annotation by a newline anyway */
|
||||
if (msglen && msg[msglen - 1] == '\n')
|
||||
msglen--;
|
||||
|
||||
strbuf_addstr(sb, "\nNotes:\n");
|
||||
|
||||
for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
|
||||
linelen = strchrnul(msg_p, '\n') - msg_p;
|
||||
|
||||
strbuf_addstr(sb, " ");
|
||||
strbuf_add(sb, msg_p, linelen);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
free(msg);
|
||||
}
|
||||
7
notes.h
Normal file
7
notes.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef NOTES_H
|
||||
#define NOTES_H
|
||||
|
||||
void get_commit_notes(const struct commit *commit, struct strbuf *sb,
|
||||
const char *output_encoding);
|
||||
|
||||
#endif
|
||||
9
pager.c
9
pager.c
@@ -1,5 +1,6 @@
|
||||
#include "cache.h"
|
||||
#include "run-command.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
/*
|
||||
* This is split up from the rest of git so that we can do
|
||||
@@ -38,6 +39,13 @@ static void wait_for_pager(void)
|
||||
finish_command(&pager_process);
|
||||
}
|
||||
|
||||
static void wait_for_pager_signal(int signo)
|
||||
{
|
||||
wait_for_pager();
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
void setup_pager(void)
|
||||
{
|
||||
const char *pager = getenv("GIT_PAGER");
|
||||
@@ -75,6 +83,7 @@ void setup_pager(void)
|
||||
close(pager_process.in);
|
||||
|
||||
/* this makes sure that the parent terminates after the pager */
|
||||
sigchain_push_common(wait_for_pager_signal);
|
||||
atexit(wait_for_pager);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "git-compat-util.h"
|
||||
#include "parse-options.h"
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
|
||||
#define OPT_SHORT 1
|
||||
#define OPT_UNSET 2
|
||||
@@ -506,6 +507,22 @@ int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct commit *commit;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
if (get_sha1(arg, sha1))
|
||||
return error("malformed object name %s", arg);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit)
|
||||
return error("no such commit %s", arg);
|
||||
commit_list_insert(commit, opt->value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This should really be OPTION_FILENAME type as a part of
|
||||
* parse_options that take prefix to do this while parsing.
|
||||
|
||||
@@ -151,6 +151,7 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx);
|
||||
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_with_commit(const struct option *, const char *, int);
|
||||
|
||||
#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
|
||||
#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")
|
||||
|
||||
@@ -73,7 +73,7 @@ static void generate_id_list(void)
|
||||
flush_current_id(patchlen, sha1, &ctx);
|
||||
}
|
||||
|
||||
static const char patch_id_usage[] = "git-patch-id < patch";
|
||||
static const char patch_id_usage[] = "git patch-id < patch";
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
4
path.c
4
path.c
@@ -154,7 +154,7 @@ int validate_headref(const char *path)
|
||||
/* Make sure it is a "refs/.." symlink */
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
len = readlink(path, buffer, sizeof(buffer)-1);
|
||||
if (len >= 5 && !memcmp("refs/", buffer, 5))
|
||||
if (len >= 11 && !memcmp("refs/heads/", buffer, 11))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ int validate_headref(const char *path)
|
||||
len -= 4;
|
||||
while (len && isspace(*buf))
|
||||
buf++, len--;
|
||||
if (len >= 5 && !memcmp("refs/", buf, 5))
|
||||
if (len >= 11 && !memcmp("refs/heads/", buf, 11))
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
5
pretty.c
5
pretty.c
@@ -6,6 +6,7 @@
|
||||
#include "string-list.h"
|
||||
#include "mailmap.h"
|
||||
#include "log-tree.h"
|
||||
#include "notes.h"
|
||||
#include "color.h"
|
||||
|
||||
static char *user_format;
|
||||
@@ -920,5 +921,9 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
|
||||
*/
|
||||
if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
|
||||
strbuf_addch(sb, '\n');
|
||||
|
||||
if (fmt != CMIT_FMT_ONELINE)
|
||||
get_commit_notes(commit, sb, encoding);
|
||||
|
||||
free(reencoded);
|
||||
}
|
||||
|
||||
20
read-cache.c
20
read-cache.c
@@ -1574,6 +1574,26 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
default:
|
||||
die("unexpected diff status %c", p->status);
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
/*
|
||||
* ADD_CACHE_IGNORE_REMOVAL is unset if "git
|
||||
* add -u" is calling us, In such a case, a
|
||||
* missing work tree file needs to be removed
|
||||
* if there is an unmerged entry at stage #2,
|
||||
* but such a diff record is followed by
|
||||
* another with DIFF_STATUS_DELETED (and if
|
||||
* there is no stage #2, we won't see DELETED
|
||||
* nor MODIFIED). We can simply continue
|
||||
* either way.
|
||||
*/
|
||||
if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL))
|
||||
continue;
|
||||
/*
|
||||
* Otherwise, it is "git add path" is asking
|
||||
* to explicitly add it; we fall through. A
|
||||
* missing work tree file is an error and is
|
||||
* caught by add_file_to_index() in such a
|
||||
* case.
|
||||
*/
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
if (add_file_to_index(&the_index, path, data->flags)) {
|
||||
|
||||
17
refs.c
17
refs.c
@@ -1453,7 +1453,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
|
||||
return 1;
|
||||
}
|
||||
|
||||
int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
|
||||
int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data)
|
||||
{
|
||||
const char *logfile;
|
||||
FILE *logfp;
|
||||
@@ -1464,6 +1464,16 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
|
||||
logfp = fopen(logfile, "r");
|
||||
if (!logfp)
|
||||
return -1;
|
||||
|
||||
if (ofs) {
|
||||
struct stat statbuf;
|
||||
if (fstat(fileno(logfp), &statbuf) ||
|
||||
statbuf.st_size < ofs ||
|
||||
fseek(logfp, -ofs, SEEK_END) ||
|
||||
fgets(buf, sizeof(buf), logfp))
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), logfp)) {
|
||||
unsigned char osha1[20], nsha1[20];
|
||||
char *email_end, *message;
|
||||
@@ -1497,6 +1507,11 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
|
||||
{
|
||||
return for_each_recent_reflog_ent(ref, fn, 0, cb_data);
|
||||
}
|
||||
|
||||
static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
|
||||
{
|
||||
DIR *dir = opendir(git_path("logs/%s", base));
|
||||
|
||||
1
refs.h
1
refs.h
@@ -60,6 +60,7 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned
|
||||
/* iterate over reflog entries */
|
||||
typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
|
||||
int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
|
||||
int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data);
|
||||
|
||||
/*
|
||||
* Calls the specified function for each reflog file until it returns nonzero,
|
||||
|
||||
10
revision.c
10
revision.c
@@ -183,8 +183,11 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
|
||||
if (!tag->tagged)
|
||||
die("bad tag");
|
||||
object = parse_object(tag->tagged->sha1);
|
||||
if (!object)
|
||||
if (!object) {
|
||||
if (flags & UNINTERESTING)
|
||||
return NULL;
|
||||
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -479,9 +482,10 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
|
||||
while (parent) {
|
||||
struct commit *p = parent->item;
|
||||
parent = parent->next;
|
||||
if (p)
|
||||
p->object.flags |= UNINTERESTING;
|
||||
if (parse_commit(p) < 0)
|
||||
return -1;
|
||||
p->object.flags |= UNINTERESTING;
|
||||
continue;
|
||||
if (p->parents)
|
||||
mark_parents_uninteresting(p);
|
||||
if (p->object.flags & SEEN)
|
||||
|
||||
@@ -118,7 +118,9 @@ int start_command(struct child_process *cmd)
|
||||
} else {
|
||||
execvp(cmd->argv[0], (char *const*) cmd->argv);
|
||||
}
|
||||
die("exec %s failed.", cmd->argv[0]);
|
||||
trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0],
|
||||
strerror(errno));
|
||||
exit(127);
|
||||
}
|
||||
#else
|
||||
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
|
||||
@@ -187,6 +189,7 @@ int start_command(struct child_process *cmd)
|
||||
#endif
|
||||
|
||||
if (cmd->pid < 0) {
|
||||
int err = errno;
|
||||
if (need_in)
|
||||
close_pair(fdin);
|
||||
else if (cmd->in)
|
||||
@@ -197,7 +200,9 @@ int start_command(struct child_process *cmd)
|
||||
close(cmd->out);
|
||||
if (need_err)
|
||||
close_pair(fderr);
|
||||
return -ERR_RUN_COMMAND_FORK;
|
||||
return err == ENOENT ?
|
||||
-ERR_RUN_COMMAND_EXEC :
|
||||
-ERR_RUN_COMMAND_FORK;
|
||||
}
|
||||
|
||||
if (need_in)
|
||||
@@ -236,9 +241,14 @@ static int wait_or_whine(pid_t pid)
|
||||
if (!WIFEXITED(status))
|
||||
return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
|
||||
code = WEXITSTATUS(status);
|
||||
if (code)
|
||||
switch (code) {
|
||||
case 127:
|
||||
return -ERR_RUN_COMMAND_EXEC;
|
||||
case 0:
|
||||
return 0;
|
||||
default:
|
||||
return -code;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ enum {
|
||||
ERR_RUN_COMMAND_WAITPID_SIGNAL,
|
||||
ERR_RUN_COMMAND_WAITPID_NOEXIT,
|
||||
};
|
||||
#define IS_RUN_COMMAND_ERR(x) ((x) <= -ERR_RUN_COMMAND_FORK)
|
||||
|
||||
struct child_process {
|
||||
const char **argv;
|
||||
|
||||
@@ -2340,7 +2340,8 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
|
||||
static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
|
||||
void *buf, unsigned long len, time_t mtime)
|
||||
{
|
||||
int fd, size, ret;
|
||||
int fd, ret;
|
||||
size_t size;
|
||||
unsigned char *compressed;
|
||||
z_stream stream;
|
||||
char *filename;
|
||||
|
||||
129
sha1_name.c
129
sha1_name.c
@@ -238,8 +238,28 @@ static int ambiguous_path(const char *path, int len)
|
||||
return slash;
|
||||
}
|
||||
|
||||
/*
|
||||
* *string and *len will only be substituted, and *string returned (for
|
||||
* later free()ing) if the string passed in is of the form @{-<n>}.
|
||||
*/
|
||||
static char *substitute_nth_last_branch(const char **string, int *len)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret = interpret_nth_last_branch(*string, &buf);
|
||||
|
||||
if (ret == *len) {
|
||||
size_t size;
|
||||
*string = strbuf_detach(&buf, &size);
|
||||
*len = size;
|
||||
return (char *)*string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
|
||||
{
|
||||
char *last_branch = substitute_nth_last_branch(&str, &len);
|
||||
const char **p, *r;
|
||||
int refs_found = 0;
|
||||
|
||||
@@ -259,11 +279,13 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(last_branch);
|
||||
return refs_found;
|
||||
}
|
||||
|
||||
int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
|
||||
{
|
||||
char *last_branch = substitute_nth_last_branch(&str, &len);
|
||||
const char **p;
|
||||
int logs_found = 0;
|
||||
|
||||
@@ -294,9 +316,12 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
|
||||
if (!warn_ambiguous_refs)
|
||||
break;
|
||||
}
|
||||
free(last_branch);
|
||||
return logs_found;
|
||||
}
|
||||
|
||||
static int get_sha1_1(const char *name, int len, unsigned char *sha1);
|
||||
|
||||
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
||||
{
|
||||
static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
|
||||
@@ -307,10 +332,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
||||
if (len == 40 && !get_sha1_hex(str, sha1))
|
||||
return 0;
|
||||
|
||||
/* basic@{time or number} format to query ref-log */
|
||||
/* basic@{time or number or -number} format to query ref-log */
|
||||
reflog_len = at = 0;
|
||||
if (str[len-1] == '}') {
|
||||
for (at = 0; at < len - 1; at++) {
|
||||
if (len && str[len-1] == '}') {
|
||||
for (at = len-2; at >= 0; at--) {
|
||||
if (str[at] == '@' && str[at+1] == '{') {
|
||||
reflog_len = (len-1) - (at+2);
|
||||
len = at;
|
||||
@@ -324,6 +349,16 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
||||
return -1;
|
||||
|
||||
if (!len && reflog_len) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret;
|
||||
/* try the @{-N} syntax for n-th checkout */
|
||||
ret = interpret_nth_last_branch(str+at, &buf);
|
||||
if (ret > 0) {
|
||||
/* substitute this branch name and restart */
|
||||
return get_sha1_1(buf.buf, buf.len, sha1);
|
||||
} else if (ret == 0) {
|
||||
return -1;
|
||||
}
|
||||
/* allow "@{...}" to mean the current branch reflog */
|
||||
refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
|
||||
} else if (reflog_len)
|
||||
@@ -379,8 +414,6 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_sha1_1(const char *name, int len, unsigned char *sha1);
|
||||
|
||||
static int get_parent(const char *name, int len,
|
||||
unsigned char *result, int idx)
|
||||
{
|
||||
@@ -674,6 +707,92 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct grab_nth_branch_switch_cbdata {
|
||||
long cnt, alloc;
|
||||
struct strbuf *buf;
|
||||
};
|
||||
|
||||
static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
|
||||
const char *email, unsigned long timestamp, int tz,
|
||||
const char *message, void *cb_data)
|
||||
{
|
||||
struct grab_nth_branch_switch_cbdata *cb = cb_data;
|
||||
const char *match = NULL, *target = NULL;
|
||||
size_t len;
|
||||
int nth;
|
||||
|
||||
if (!prefixcmp(message, "checkout: moving from ")) {
|
||||
match = message + strlen("checkout: moving from ");
|
||||
target = strstr(match, " to ");
|
||||
}
|
||||
|
||||
if (!match || !target)
|
||||
return 0;
|
||||
|
||||
len = target - match;
|
||||
nth = cb->cnt++ % cb->alloc;
|
||||
strbuf_reset(&cb->buf[nth]);
|
||||
strbuf_add(&cb->buf[nth], match, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This reads "@{-N}" syntax, finds the name of the Nth previous
|
||||
* branch we were on, and places the name of the branch in the given
|
||||
* buf and returns the number of characters parsed if successful.
|
||||
*
|
||||
* If the input is not of the accepted format, it returns a negative
|
||||
* number to signal an error.
|
||||
*
|
||||
* If the input was ok but there are not N branch switches in the
|
||||
* reflog, it returns 0.
|
||||
*/
|
||||
int interpret_nth_last_branch(const char *name, struct strbuf *buf)
|
||||
{
|
||||
long nth;
|
||||
int i, retval;
|
||||
struct grab_nth_branch_switch_cbdata cb;
|
||||
const char *brace;
|
||||
char *num_end;
|
||||
|
||||
if (name[0] != '@' || name[1] != '{' || name[2] != '-')
|
||||
return -1;
|
||||
brace = strchr(name, '}');
|
||||
if (!brace)
|
||||
return -1;
|
||||
nth = strtol(name+3, &num_end, 10);
|
||||
if (num_end != brace)
|
||||
return -1;
|
||||
if (nth <= 0)
|
||||
return -1;
|
||||
cb.alloc = nth;
|
||||
cb.buf = xmalloc(nth * sizeof(struct strbuf));
|
||||
for (i = 0; i < nth; i++)
|
||||
strbuf_init(&cb.buf[i], 20);
|
||||
cb.cnt = 0;
|
||||
retval = 0;
|
||||
for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
|
||||
if (cb.cnt < nth) {
|
||||
cb.cnt = 0;
|
||||
for (i = 0; i < nth; i++)
|
||||
strbuf_release(&cb.buf[i]);
|
||||
for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
|
||||
}
|
||||
if (cb.cnt < nth)
|
||||
goto release_return;
|
||||
i = cb.cnt % nth;
|
||||
strbuf_reset(buf);
|
||||
strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
|
||||
retval = brace-name+1;
|
||||
|
||||
release_return:
|
||||
for (i = 0; i < nth; i++)
|
||||
strbuf_release(&cb.buf[i]);
|
||||
free(cb.buf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
|
||||
* notably "xyz^" for "parent of xyz"
|
||||
|
||||
52
sigchain.c
Normal file
52
sigchain.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "sigchain.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define SIGCHAIN_MAX_SIGNALS 32
|
||||
|
||||
struct sigchain_signal {
|
||||
sigchain_fun *old;
|
||||
int n;
|
||||
int alloc;
|
||||
};
|
||||
static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];
|
||||
|
||||
static void check_signum(int sig)
|
||||
{
|
||||
if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)
|
||||
die("BUG: signal out of range: %d", sig);
|
||||
}
|
||||
|
||||
int sigchain_push(int sig, sigchain_fun f)
|
||||
{
|
||||
struct sigchain_signal *s = signals + sig;
|
||||
check_signum(sig);
|
||||
|
||||
ALLOC_GROW(s->old, s->n + 1, s->alloc);
|
||||
s->old[s->n] = signal(sig, f);
|
||||
if (s->old[s->n] == SIG_ERR)
|
||||
return -1;
|
||||
s->n++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigchain_pop(int sig)
|
||||
{
|
||||
struct sigchain_signal *s = signals + sig;
|
||||
check_signum(sig);
|
||||
if (s->n < 1)
|
||||
return 0;
|
||||
|
||||
if (signal(sig, s->old[s->n - 1]) == SIG_ERR)
|
||||
return -1;
|
||||
s->n--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sigchain_push_common(sigchain_fun f)
|
||||
{
|
||||
sigchain_push(SIGINT, f);
|
||||
sigchain_push(SIGHUP, f);
|
||||
sigchain_push(SIGTERM, f);
|
||||
sigchain_push(SIGQUIT, f);
|
||||
sigchain_push(SIGPIPE, f);
|
||||
}
|
||||
11
sigchain.h
Normal file
11
sigchain.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef SIGCHAIN_H
|
||||
#define SIGCHAIN_H
|
||||
|
||||
typedef void (*sigchain_fun)(int);
|
||||
|
||||
int sigchain_push(int sig, sigchain_fun f);
|
||||
int sigchain_pop(int sig);
|
||||
|
||||
void sigchain_push_common(sigchain_fun f);
|
||||
|
||||
#endif /* SIGCHAIN_H */
|
||||
18
t/README
18
t/README
@@ -212,6 +212,24 @@ library for your script to use.
|
||||
is to summarize successes and failures in the test script and
|
||||
exit with an appropriate error code.
|
||||
|
||||
- test_tick
|
||||
|
||||
Make commit and tag names consistent by setting the author and
|
||||
committer times to defined stated. Subsequent calls will
|
||||
advance the times by a fixed amount.
|
||||
|
||||
- test_commit <message> [<filename> [<contents>]]
|
||||
|
||||
Creates a commit with the given message, committing the given
|
||||
file with the given contents (default for both is to reuse the
|
||||
message string), and adds a tag (again reusing the message
|
||||
string as name). Calls test_tick to make the SHA-1s
|
||||
reproducible.
|
||||
|
||||
- test_merge <message> <commit-or-tag>
|
||||
|
||||
Merges the given rev using the given message. Like test_commit,
|
||||
creates a tag and calls test_tick before committing.
|
||||
|
||||
Tips for Writing Tests
|
||||
----------------------
|
||||
|
||||
48
t/lib-rebase.sh
Normal file
48
t/lib-rebase.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
|
||||
# After setting the fake editor with this function, you can
|
||||
#
|
||||
# - override the commit message with $FAKE_COMMIT_MESSAGE,
|
||||
# - amend the commit message with $FAKE_COMMIT_AMEND
|
||||
# - check that non-commit messages have a certain line count with $EXPECT_COUNT
|
||||
# - rewrite a rebase -i script with $FAKE_LINES in the form
|
||||
#
|
||||
# "[<lineno1>] [<lineno2>]..."
|
||||
#
|
||||
# If a line number is prefixed with "squash" or "edit", the respective line's
|
||||
# command will be replaced with the specified one.
|
||||
|
||||
set_fake_editor () {
|
||||
echo "#!$SHELL_PATH" >fake-editor.sh
|
||||
cat >> fake-editor.sh <<\EOF
|
||||
case "$1" in
|
||||
*/COMMIT_EDITMSG)
|
||||
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
|
||||
test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
test -z "$EXPECT_COUNT" ||
|
||||
test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
|
||||
exit
|
||||
test -z "$FAKE_LINES" && exit
|
||||
grep -v '^#' < "$1" > "$1".tmp
|
||||
rm -f "$1"
|
||||
cat "$1".tmp
|
||||
action=pick
|
||||
for line in $FAKE_LINES; do
|
||||
case $line in
|
||||
squash|edit)
|
||||
action="$line";;
|
||||
*)
|
||||
echo sed -n "${line}s/^pick/$action/p"
|
||||
sed -n "${line}p" < "$1".tmp
|
||||
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
|
||||
action=pick;;
|
||||
esac
|
||||
done
|
||||
EOF
|
||||
|
||||
test_set_editor "$(pwd)/fake-editor.sh"
|
||||
chmod a+x fake-editor.sh
|
||||
}
|
||||
22
t/t0005-signals.sh
Executable file
22
t/t0005-signals.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='signals work as we expect'
|
||||
. ./test-lib.sh
|
||||
|
||||
cat >expect <<EOF
|
||||
three
|
||||
two
|
||||
one
|
||||
EOF
|
||||
|
||||
test_expect_success 'sigchain works' '
|
||||
test-sigchain >actual
|
||||
case "$?" in
|
||||
143) true ;; # POSIX w/ SIGTERM=15
|
||||
3) true ;; # Windows
|
||||
*) false ;;
|
||||
esac &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
41
t/t1401-symbolic-ref.sh
Executable file
41
t/t1401-symbolic-ref.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='basic symbolic-ref tests'
|
||||
. ./test-lib.sh
|
||||
|
||||
# If the tests munging HEAD fail, they can break detection of
|
||||
# the git repo, meaning that further tests will operate on
|
||||
# the surrounding git repo instead of the trash directory.
|
||||
reset_to_sane() {
|
||||
echo ref: refs/heads/foo >.git/HEAD
|
||||
}
|
||||
|
||||
test_expect_success 'symbolic-ref writes HEAD' '
|
||||
git symbolic-ref HEAD refs/heads/foo &&
|
||||
echo ref: refs/heads/foo >expect &&
|
||||
test_cmp expect .git/HEAD
|
||||
'
|
||||
|
||||
test_expect_success 'symbolic-ref reads HEAD' '
|
||||
echo refs/heads/foo >expect &&
|
||||
git symbolic-ref HEAD >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
|
||||
test_must_fail git symbolic-ref HEAD foo
|
||||
'
|
||||
reset_to_sane
|
||||
|
||||
test_expect_success 'symbolic-ref refuses non-branch for HEAD' '
|
||||
test_must_fail git symbolic-ref HEAD refs/foo
|
||||
'
|
||||
reset_to_sane
|
||||
|
||||
test_expect_success 'symbolic-ref refuses bare sha1' '
|
||||
echo content >file && git add file && git commit -m one
|
||||
test_must_fail git symbolic-ref HEAD `git rev-parse HEAD`
|
||||
'
|
||||
reset_to_sane
|
||||
|
||||
test_done
|
||||
31
t/t1450-fsck.sh
Executable file
31
t/t1450-fsck.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git fsck random collection of tests'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
test_commit A fileA one &&
|
||||
git checkout HEAD^0 &&
|
||||
test_commit B fileB two &&
|
||||
git tag -d A B &&
|
||||
git reflog expire --expire=now --all
|
||||
'
|
||||
|
||||
test_expect_success 'HEAD is part of refs' '
|
||||
test 0 = $(git fsck | wc -l)
|
||||
'
|
||||
|
||||
test_expect_success 'loose objects borrowed from alternate are not missing' '
|
||||
mkdir another &&
|
||||
(
|
||||
cd another &&
|
||||
git init &&
|
||||
echo ../../../.git/objects >.git/objects/info/alternates &&
|
||||
test_commit C fileC one &&
|
||||
git fsck >out &&
|
||||
! grep "missing blob" out
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
69
t/t1505-rev-parse-last.sh
Executable file
69
t/t1505-rev-parse-last.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test @{-N} syntax'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
|
||||
make_commit () {
|
||||
echo "$1" > "$1" &&
|
||||
git add "$1" &&
|
||||
git commit -m "$1"
|
||||
}
|
||||
|
||||
|
||||
test_expect_success 'setup' '
|
||||
|
||||
make_commit 1 &&
|
||||
git branch side &&
|
||||
make_commit 2 &&
|
||||
make_commit 3 &&
|
||||
git checkout side &&
|
||||
make_commit 4 &&
|
||||
git merge master &&
|
||||
git checkout master
|
||||
|
||||
'
|
||||
|
||||
# 1 -- 2 -- 3 master
|
||||
# \ \
|
||||
# \ \
|
||||
# --- 4 --- 5 side
|
||||
#
|
||||
# and 'side' should be the last branch
|
||||
|
||||
test_rev_equivalent () {
|
||||
|
||||
git rev-parse "$1" > expect &&
|
||||
git rev-parse "$2" > output &&
|
||||
test_cmp expect output
|
||||
|
||||
}
|
||||
|
||||
test_expect_success '@{-1} works' '
|
||||
test_rev_equivalent side @{-1}
|
||||
'
|
||||
|
||||
test_expect_success '@{-1}~2 works' '
|
||||
test_rev_equivalent side~2 @{-1}~2
|
||||
'
|
||||
|
||||
test_expect_success '@{-1}^2 works' '
|
||||
test_rev_equivalent side^2 @{-1}^2
|
||||
'
|
||||
|
||||
test_expect_success '@{-1}@{1} works' '
|
||||
test_rev_equivalent side@{1} @{-1}@{1}
|
||||
'
|
||||
|
||||
test_expect_success '@{-2} works' '
|
||||
test_rev_equivalent master @{-2}
|
||||
'
|
||||
|
||||
test_expect_success '@{-3} fails' '
|
||||
test_must_fail git rev-parse @{-3}
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
|
||||
94
t/t2012-checkout-last.sh
Executable file
94
t/t2012-checkout-last.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='checkout can switch to last branch'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo hello >world &&
|
||||
git add world &&
|
||||
git commit -m initial &&
|
||||
git branch other &&
|
||||
echo "hello again" >>world &&
|
||||
git add world &&
|
||||
git commit -m second
|
||||
'
|
||||
|
||||
test_expect_success '"checkout -" does not work initially' '
|
||||
test_must_fail git checkout -
|
||||
'
|
||||
|
||||
test_expect_success 'first branch switch' '
|
||||
git checkout other
|
||||
'
|
||||
|
||||
test_expect_success '"checkout -" switches back' '
|
||||
git checkout - &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
|
||||
'
|
||||
|
||||
test_expect_success '"checkout -" switches forth' '
|
||||
git checkout - &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
|
||||
'
|
||||
|
||||
test_expect_success 'detach HEAD' '
|
||||
git checkout $(git rev-parse HEAD)
|
||||
'
|
||||
|
||||
test_expect_success '"checkout -" attaches again' '
|
||||
git checkout - &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
|
||||
'
|
||||
|
||||
test_expect_success '"checkout -" detaches again' '
|
||||
git checkout - &&
|
||||
test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" &&
|
||||
test_must_fail git symbolic-ref HEAD
|
||||
'
|
||||
|
||||
test_expect_success 'more switches' '
|
||||
for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
|
||||
do
|
||||
git checkout -b branch$i
|
||||
done
|
||||
'
|
||||
|
||||
more_switches () {
|
||||
for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
|
||||
do
|
||||
git checkout branch$i
|
||||
done
|
||||
}
|
||||
|
||||
test_expect_success 'switch to the last' '
|
||||
more_switches &&
|
||||
git checkout @{-1} &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2"
|
||||
'
|
||||
|
||||
test_expect_success 'switch to second from the last' '
|
||||
more_switches &&
|
||||
git checkout @{-2} &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3"
|
||||
'
|
||||
|
||||
test_expect_success 'switch to third from the last' '
|
||||
more_switches &&
|
||||
git checkout @{-3} &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4"
|
||||
'
|
||||
|
||||
test_expect_success 'switch to fourth from the last' '
|
||||
more_switches &&
|
||||
git checkout @{-4} &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5"
|
||||
'
|
||||
|
||||
test_expect_success 'switch to twelfth from the last' '
|
||||
more_switches &&
|
||||
git checkout @{-12} &&
|
||||
test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -12,7 +12,7 @@ and issues a git add -u with path limiting on "dir" to add
|
||||
only the updates to dir/sub.
|
||||
|
||||
Also tested are "git add -u" without limiting, and "git add -u"
|
||||
without contents changes.'
|
||||
without contents changes, and other conditions'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
@@ -128,4 +128,52 @@ test_expect_success 'add -n -u should not add but just report' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'add -u resolves unmerged paths' '
|
||||
git reset --hard &&
|
||||
one=$(echo 1 | git hash-object -w --stdin) &&
|
||||
two=$(echo 2 | git hash-object -w --stdin) &&
|
||||
three=$(echo 3 | git hash-object -w --stdin) &&
|
||||
{
|
||||
for path in path1 path2
|
||||
do
|
||||
echo "100644 $one 1 $path"
|
||||
echo "100644 $two 2 $path"
|
||||
echo "100644 $three 3 $path"
|
||||
done
|
||||
echo "100644 $one 1 path3"
|
||||
echo "100644 $one 1 path4"
|
||||
echo "100644 $one 3 path5"
|
||||
echo "100644 $one 3 path6"
|
||||
} |
|
||||
git update-index --index-info &&
|
||||
echo 3 >path1 &&
|
||||
echo 2 >path3 &&
|
||||
echo 2 >path5 &&
|
||||
git add -u &&
|
||||
git ls-files -s "path?" >actual &&
|
||||
{
|
||||
echo "100644 $three 0 path1"
|
||||
echo "100644 $one 1 path3"
|
||||
echo "100644 $one 1 path4"
|
||||
echo "100644 $one 3 path5"
|
||||
echo "100644 $one 3 path6"
|
||||
} >expect &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
# Bonus tests. Explicit resolving
|
||||
git add path3 path5 &&
|
||||
test_must_fail git add path4 &&
|
||||
test_must_fail git add path6 &&
|
||||
git rm path4 &&
|
||||
git rm path6 &&
|
||||
|
||||
git ls-files -s "path?" >actual &&
|
||||
{
|
||||
echo "100644 $three 0 path1"
|
||||
echo "100644 $two 0 path3"
|
||||
echo "100644 $two 0 path5"
|
||||
} >expect
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
95
t/t3301-notes.sh
Executable file
95
t/t3301-notes.sh
Executable file
@@ -0,0 +1,95 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Johannes E. Schindelin
|
||||
#
|
||||
|
||||
test_description='Test commit notes'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
cat > fake_editor.sh << \EOF
|
||||
echo "$MSG" > "$1"
|
||||
echo "$MSG" >& 2
|
||||
EOF
|
||||
chmod a+x fake_editor.sh
|
||||
VISUAL=./fake_editor.sh
|
||||
export VISUAL
|
||||
|
||||
test_expect_success 'cannot annotate non-existing HEAD' '
|
||||
! MSG=3 git notes edit
|
||||
'
|
||||
|
||||
test_expect_success setup '
|
||||
: > a1 &&
|
||||
git add a1 &&
|
||||
test_tick &&
|
||||
git commit -m 1st &&
|
||||
: > a2 &&
|
||||
git add a2 &&
|
||||
test_tick &&
|
||||
git commit -m 2nd
|
||||
'
|
||||
|
||||
test_expect_success 'need valid notes ref' '
|
||||
! MSG=1 GIT_NOTES_REF='/' git notes edit &&
|
||||
! MSG=2 GIT_NOTES_REF='/' git notes show
|
||||
'
|
||||
|
||||
test_expect_success 'create notes' '
|
||||
git config core.notesRef refs/notes/commits &&
|
||||
MSG=b1 git notes edit &&
|
||||
test ! -f .git/new-notes &&
|
||||
test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
|
||||
test b1 = $(git notes show) &&
|
||||
git show HEAD^ &&
|
||||
! git notes show HEAD^
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 268048bfb8a1fb38e703baceb8ab235421bf80c5
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:14:13 2005 -0700
|
||||
|
||||
2nd
|
||||
|
||||
Notes:
|
||||
b1
|
||||
EOF
|
||||
|
||||
test_expect_success 'show notes' '
|
||||
! (git cat-file commit HEAD | grep b1) &&
|
||||
git log -1 > output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
test_expect_success 'create multi-line notes (setup)' '
|
||||
: > a3 &&
|
||||
git add a3 &&
|
||||
test_tick &&
|
||||
git commit -m 3rd &&
|
||||
MSG="b3
|
||||
c3c3c3c3
|
||||
d3d3d3" git notes edit
|
||||
'
|
||||
|
||||
cat > expect-multiline << EOF
|
||||
commit 1584215f1d29c65e99c6c6848626553fdd07fd75
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:15:13 2005 -0700
|
||||
|
||||
3rd
|
||||
|
||||
Notes:
|
||||
b3
|
||||
c3c3c3c3
|
||||
d3d3d3
|
||||
EOF
|
||||
|
||||
printf "\n" >> expect-multiline
|
||||
cat expect >> expect-multiline
|
||||
|
||||
test_expect_success 'show multi-line notes' '
|
||||
git log -2 > output &&
|
||||
test_cmp expect-multiline output
|
||||
'
|
||||
|
||||
test_done
|
||||
98
t/t3302-notes-index-expensive.sh
Executable file
98
t/t3302-notes-index-expensive.sh
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Johannes E. Schindelin
|
||||
#
|
||||
|
||||
test_description='Test commit notes index (expensive!)'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test -z "$GIT_NOTES_TIMING_TESTS" && {
|
||||
say Skipping timing tests
|
||||
test_done
|
||||
exit
|
||||
}
|
||||
|
||||
create_repo () {
|
||||
number_of_commits=$1
|
||||
nr=0
|
||||
parent=
|
||||
test -d .git || {
|
||||
git init &&
|
||||
tree=$(git write-tree) &&
|
||||
while [ $nr -lt $number_of_commits ]; do
|
||||
test_tick &&
|
||||
commit=$(echo $nr | git commit-tree $tree $parent) ||
|
||||
return
|
||||
parent="-p $commit"
|
||||
nr=$(($nr+1))
|
||||
done &&
|
||||
git update-ref refs/heads/master $commit &&
|
||||
{
|
||||
export GIT_INDEX_FILE=.git/temp;
|
||||
git rev-list HEAD | cat -n | sed "s/^[ ][ ]*/ /g" |
|
||||
while read nr sha1; do
|
||||
blob=$(echo note $nr | git hash-object -w --stdin) &&
|
||||
echo $sha1 | sed "s/^/0644 $blob 0 /"
|
||||
done | git update-index --index-info &&
|
||||
tree=$(git write-tree) &&
|
||||
test_tick &&
|
||||
commit=$(echo notes | git commit-tree $tree) &&
|
||||
git update-ref refs/notes/commits $commit
|
||||
} &&
|
||||
git config core.notesRef refs/notes/commits
|
||||
}
|
||||
}
|
||||
|
||||
test_notes () {
|
||||
count=$1 &&
|
||||
git config core.notesRef refs/notes/commits &&
|
||||
git log | grep "^ " > output &&
|
||||
i=1 &&
|
||||
while [ $i -le $count ]; do
|
||||
echo " $(($count-$i))" &&
|
||||
echo " note $i" &&
|
||||
i=$(($i+1));
|
||||
done > expect &&
|
||||
git diff expect output
|
||||
}
|
||||
|
||||
cat > time_notes << \EOF
|
||||
mode=$1
|
||||
i=1
|
||||
while [ $i -lt $2 ]; do
|
||||
case $1 in
|
||||
no-notes)
|
||||
export GIT_NOTES_REF=non-existing
|
||||
;;
|
||||
notes)
|
||||
unset GIT_NOTES_REF
|
||||
;;
|
||||
esac
|
||||
git log >/dev/null
|
||||
i=$(($i+1))
|
||||
done
|
||||
EOF
|
||||
|
||||
time_notes () {
|
||||
for mode in no-notes notes
|
||||
do
|
||||
echo $mode
|
||||
/usr/bin/time sh ../time_notes $mode $1
|
||||
done
|
||||
}
|
||||
|
||||
for count in 10 100 1000 10000; do
|
||||
|
||||
mkdir $count
|
||||
(cd $count;
|
||||
|
||||
test_expect_success "setup $count" "create_repo $count"
|
||||
|
||||
test_expect_success 'notes work' "test_notes $count"
|
||||
|
||||
test_expect_success 'notes timing' "time_notes 100"
|
||||
)
|
||||
done
|
||||
|
||||
test_done
|
||||
@@ -10,6 +10,10 @@ that the result still makes sense.
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
. ../lib-rebase.sh
|
||||
|
||||
set_fake_editor
|
||||
|
||||
# set up two branches like this:
|
||||
#
|
||||
# A - B - C - D - E
|
||||
@@ -61,39 +65,6 @@ test_expect_success 'setup' '
|
||||
git tag I
|
||||
'
|
||||
|
||||
echo "#!$SHELL_PATH" >fake-editor.sh
|
||||
cat >> fake-editor.sh <<\EOF
|
||||
case "$1" in
|
||||
*/COMMIT_EDITMSG)
|
||||
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
|
||||
test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
test -z "$EXPECT_COUNT" ||
|
||||
test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
|
||||
exit
|
||||
test -z "$FAKE_LINES" && exit
|
||||
grep -v '^#' < "$1" > "$1".tmp
|
||||
rm -f "$1"
|
||||
cat "$1".tmp
|
||||
action=pick
|
||||
for line in $FAKE_LINES; do
|
||||
case $line in
|
||||
squash|edit)
|
||||
action="$line";;
|
||||
*)
|
||||
echo sed -n "${line}s/^pick/$action/p"
|
||||
sed -n "${line}p" < "$1".tmp
|
||||
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
|
||||
action=pick;;
|
||||
esac
|
||||
done
|
||||
EOF
|
||||
|
||||
test_set_editor "$(pwd)/fake-editor.sh"
|
||||
chmod a+x fake-editor.sh
|
||||
|
||||
test_expect_success 'no changes are a nop' '
|
||||
git rebase -i F &&
|
||||
test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
|
||||
@@ -462,4 +433,30 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'submodule rebase setup' '
|
||||
git checkout A &&
|
||||
mkdir sub &&
|
||||
(
|
||||
cd sub && git init && >elif &&
|
||||
git add elif && git commit -m "submodule initial"
|
||||
) &&
|
||||
echo 1 >file1 &&
|
||||
git add file1 sub
|
||||
test_tick &&
|
||||
git commit -m "One" &&
|
||||
echo 2 >file1 &&
|
||||
test_tick &&
|
||||
git commit -a -m "Two" &&
|
||||
(
|
||||
cd sub && echo 3 >elif &&
|
||||
git commit -a -m "submodule second"
|
||||
) &&
|
||||
test_tick &&
|
||||
git commit -a -m "Three changes submodule"
|
||||
'
|
||||
|
||||
test_expect_success 'submodule rebase -i' '
|
||||
FAKE_LINES="1 squash 2 3" git rebase -i A
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -22,47 +22,17 @@ rewritten.
|
||||
# where B, D and G touch the same file.
|
||||
|
||||
test_expect_success 'setup' '
|
||||
: > file1 &&
|
||||
git add file1 &&
|
||||
test_tick &&
|
||||
git commit -m A &&
|
||||
git tag A &&
|
||||
echo 1 > file1 &&
|
||||
test_tick &&
|
||||
git commit -m B file1 &&
|
||||
: > file2 &&
|
||||
git add file2 &&
|
||||
test_tick &&
|
||||
git commit -m C &&
|
||||
echo 2 > file1 &&
|
||||
test_tick &&
|
||||
git commit -m D file1 &&
|
||||
: > file3 &&
|
||||
git add file3 &&
|
||||
test_tick &&
|
||||
git commit -m E &&
|
||||
git tag E &&
|
||||
git checkout -b branch1 A &&
|
||||
: > file4 &&
|
||||
git add file4 &&
|
||||
test_tick &&
|
||||
git commit -m F &&
|
||||
git tag F &&
|
||||
echo 3 > file1 &&
|
||||
test_tick &&
|
||||
git commit -m G file1 &&
|
||||
git tag G &&
|
||||
: > file5 &&
|
||||
git add file5 &&
|
||||
test_tick &&
|
||||
git commit -m H &&
|
||||
git tag H &&
|
||||
git checkout -b branch2 F &&
|
||||
: > file6 &&
|
||||
git add file6 &&
|
||||
test_tick &&
|
||||
git commit -m I &&
|
||||
git tag I
|
||||
test_commit A file1 &&
|
||||
test_commit B file1 1 &&
|
||||
test_commit C file2 &&
|
||||
test_commit D file1 2 &&
|
||||
test_commit E file3 &&
|
||||
git checkout A &&
|
||||
test_commit F file4 &&
|
||||
test_commit G file1 3 &&
|
||||
test_commit H file5 &&
|
||||
git checkout F &&
|
||||
test_commit I file6
|
||||
'
|
||||
|
||||
# A - B - C - D - E
|
||||
@@ -72,68 +42,44 @@ test_expect_success 'setup' '
|
||||
# I -- G2 -- J -- K I -- K
|
||||
# G2 = same changes as G
|
||||
test_expect_success 'skip same-resolution merges with -p' '
|
||||
git checkout branch1 &&
|
||||
git checkout H &&
|
||||
! git merge E &&
|
||||
echo 23 > file1 &&
|
||||
git add file1 &&
|
||||
git commit -m L &&
|
||||
git checkout branch2 &&
|
||||
echo 3 > file1 &&
|
||||
git commit -a -m G2 &&
|
||||
test_commit L file1 23 &&
|
||||
git checkout I &&
|
||||
test_commit G2 file1 3 &&
|
||||
! git merge E &&
|
||||
echo 23 > file1 &&
|
||||
git add file1 &&
|
||||
git commit -m J &&
|
||||
echo file7 > file7 &&
|
||||
git add file7 &&
|
||||
git commit -m K &&
|
||||
GIT_EDITOR=: git rebase -i -p branch1 &&
|
||||
test $(git rev-parse branch2^^) = $(git rev-parse branch1) &&
|
||||
test_commit J file1 23 &&
|
||||
test_commit K file7 file7 &&
|
||||
git rebase -i -p L &&
|
||||
test $(git rev-parse HEAD^^) = $(git rev-parse L) &&
|
||||
test "23" = "$(cat file1)" &&
|
||||
test "" = "$(cat file6)" &&
|
||||
test "file7" = "$(cat file7)" &&
|
||||
|
||||
git checkout branch1 &&
|
||||
git reset --hard H &&
|
||||
git checkout branch2 &&
|
||||
git reset --hard I
|
||||
test "I" = "$(cat file6)" &&
|
||||
test "file7" = "$(cat file7)"
|
||||
'
|
||||
|
||||
# A - B - C - D - E
|
||||
# \ \ \
|
||||
# F - G - H -- L \ --> L
|
||||
# \ | \
|
||||
# I -- G2 -- J -- K I -- G2 -- K
|
||||
# F - G - H -- L2 \ --> L2
|
||||
# \ | \
|
||||
# I -- G3 --- J2 -- K2 I -- G3 -- K2
|
||||
# G2 = different changes as G
|
||||
test_expect_success 'keep different-resolution merges with -p' '
|
||||
git checkout branch1 &&
|
||||
git checkout H &&
|
||||
! git merge E &&
|
||||
echo 23 > file1 &&
|
||||
git add file1 &&
|
||||
git commit -m L &&
|
||||
git checkout branch2 &&
|
||||
echo 4 > file1 &&
|
||||
git commit -a -m G2 &&
|
||||
test_commit L2 file1 23 &&
|
||||
git checkout I &&
|
||||
test_commit G3 file1 4 &&
|
||||
! git merge E &&
|
||||
echo 24 > file1 &&
|
||||
git add file1 &&
|
||||
git commit -m J &&
|
||||
echo file7 > file7 &&
|
||||
git add file7 &&
|
||||
git commit -m K &&
|
||||
! GIT_EDITOR=: git rebase -i -p branch1 &&
|
||||
test_commit J2 file1 24 &&
|
||||
test_commit K2 file7 file7 &&
|
||||
test_must_fail git rebase -i -p L2 &&
|
||||
echo 234 > file1 &&
|
||||
git add file1 &&
|
||||
GIT_EDITOR=: git rebase --continue &&
|
||||
test $(git rev-parse branch2^^^) = $(git rev-parse branch1) &&
|
||||
git rebase --continue &&
|
||||
test $(git rev-parse HEAD^^^) = $(git rev-parse L2) &&
|
||||
test "234" = "$(cat file1)" &&
|
||||
test "" = "$(cat file6)" &&
|
||||
test "file7" = "$(cat file7)" &&
|
||||
|
||||
git checkout branch1 &&
|
||||
git reset --hard H &&
|
||||
git checkout branch2 &&
|
||||
git reset --hard I
|
||||
test "I" = "$(cat file6)" &&
|
||||
test "file7" = "$(cat file7)"
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -5,44 +5,14 @@
|
||||
|
||||
test_description='git rebase preserve merges
|
||||
|
||||
This test runs git rebase with and tries to squash a commit from after a merge
|
||||
to before the merge.
|
||||
This test runs git rebase with -p and tries to squash a commit from after
|
||||
a merge to before the merge.
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
# Copy/paste from t3404-rebase-interactive.sh
|
||||
echo "#!$SHELL_PATH" >fake-editor.sh
|
||||
cat >> fake-editor.sh <<\EOF
|
||||
case "$1" in
|
||||
*/COMMIT_EDITMSG)
|
||||
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
|
||||
test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
test -z "$EXPECT_COUNT" ||
|
||||
test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
|
||||
exit
|
||||
test -z "$FAKE_LINES" && exit
|
||||
grep -v '^#' < "$1" > "$1".tmp
|
||||
rm -f "$1"
|
||||
cat "$1".tmp
|
||||
action=pick
|
||||
for line in $FAKE_LINES; do
|
||||
case $line in
|
||||
squash|edit)
|
||||
action="$line";;
|
||||
*)
|
||||
echo sed -n "${line}s/^pick/$action/p"
|
||||
sed -n "${line}p" < "$1".tmp
|
||||
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
|
||||
action=pick;;
|
||||
esac
|
||||
done
|
||||
EOF
|
||||
. ../lib-rebase.sh
|
||||
|
||||
test_set_editor "$(pwd)/fake-editor.sh"
|
||||
chmod a+x fake-editor.sh
|
||||
set_fake_editor
|
||||
|
||||
# set up two branches like this:
|
||||
#
|
||||
@@ -51,27 +21,13 @@ chmod a+x fake-editor.sh
|
||||
# -- C1 --
|
||||
|
||||
test_expect_success 'setup' '
|
||||
touch a &&
|
||||
touch b &&
|
||||
git add a &&
|
||||
git commit -m A1 &&
|
||||
git tag A1
|
||||
git add b &&
|
||||
git commit -m B1 &&
|
||||
git tag B1 &&
|
||||
git checkout -b branch &&
|
||||
touch c &&
|
||||
git add c &&
|
||||
git commit -m C1 &&
|
||||
git checkout master &&
|
||||
touch d &&
|
||||
git add d &&
|
||||
git commit -m D1 &&
|
||||
git merge branch &&
|
||||
touch f &&
|
||||
git add f &&
|
||||
git commit -m F1 &&
|
||||
git tag F1
|
||||
test_commit A1 &&
|
||||
test_commit B1 &&
|
||||
test_commit C1 &&
|
||||
git reset --hard B1 &&
|
||||
test_commit D1 &&
|
||||
test_merge E1 C1 &&
|
||||
test_commit F1
|
||||
'
|
||||
|
||||
# Should result in:
|
||||
@@ -82,7 +38,7 @@ test_expect_success 'setup' '
|
||||
#
|
||||
test_expect_success 'squash F1 into D1' '
|
||||
FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
|
||||
test "$(git rev-parse HEAD^2)" = "$(git rev-parse branch)" &&
|
||||
test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
|
||||
test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
|
||||
git tag E2
|
||||
'
|
||||
@@ -100,32 +56,15 @@ test_expect_success 'squash F1 into D1' '
|
||||
# And rebase G1..M1 onto E2
|
||||
|
||||
test_expect_success 'rebase two levels of merge' '
|
||||
git checkout -b branch2 A1 &&
|
||||
touch g &&
|
||||
git add g &&
|
||||
git commit -m G1 &&
|
||||
git checkout -b branch3 &&
|
||||
touch h
|
||||
git add h &&
|
||||
git commit -m H1 &&
|
||||
git checkout -b branch4 &&
|
||||
touch i &&
|
||||
git add i &&
|
||||
git commit -m I1 &&
|
||||
git tag I1 &&
|
||||
git checkout branch3 &&
|
||||
touch j &&
|
||||
git add j &&
|
||||
git commit -m J1 &&
|
||||
git merge I1 --no-commit &&
|
||||
git commit -m K1 &&
|
||||
git tag K1 &&
|
||||
git checkout branch2 &&
|
||||
touch l &&
|
||||
git add l &&
|
||||
git commit -m L1 &&
|
||||
git merge K1 --no-commit &&
|
||||
git commit -m M1 &&
|
||||
test_commit G1 &&
|
||||
test_commit H1 &&
|
||||
test_commit I1 &&
|
||||
git checkout -b branch3 H1 &&
|
||||
test_commit J1 &&
|
||||
test_merge K1 I1 &&
|
||||
git checkout -b branch2 G1 &&
|
||||
test_commit L1 &&
|
||||
test_merge M1 K1 &&
|
||||
GIT_EDITOR=: git rebase -i -p E2 &&
|
||||
test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" &&
|
||||
test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" &&
|
||||
|
||||
@@ -6,24 +6,20 @@ Tests if git rebase --root --onto <newparent> can rebase the root commit.
|
||||
'
|
||||
. ./test-lib.sh
|
||||
|
||||
log_with_names () {
|
||||
git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
|
||||
git name-rev --stdin --name-only --refs=refs/heads/$1
|
||||
}
|
||||
|
||||
|
||||
test_expect_success 'prepare repository' '
|
||||
echo 1 > A &&
|
||||
git add A &&
|
||||
git commit -m 1 &&
|
||||
echo 2 > A &&
|
||||
git add A &&
|
||||
git commit -m 2 &&
|
||||
test_commit 1 A &&
|
||||
test_commit 2 A &&
|
||||
git symbolic-ref HEAD refs/heads/other &&
|
||||
rm .git/index &&
|
||||
echo 3 > B &&
|
||||
git add B &&
|
||||
git commit -m 3 &&
|
||||
echo 1 > A &&
|
||||
git add A &&
|
||||
git commit -m 1b &&
|
||||
echo 4 > B &&
|
||||
git add B &&
|
||||
git commit -m 4
|
||||
test_commit 3 B &&
|
||||
test_commit 1b A 1 &&
|
||||
test_commit 4 B
|
||||
'
|
||||
|
||||
test_expect_success 'rebase --root expects --onto' '
|
||||
@@ -69,7 +65,7 @@ test_expect_success 'pre-rebase got correct input (2)' '
|
||||
|
||||
test_expect_success 'rebase -i --root --onto <newbase>' '
|
||||
git checkout -b work3 other &&
|
||||
GIT_EDITOR=: git rebase -i --root --onto master &&
|
||||
git rebase -i --root --onto master &&
|
||||
git log --pretty=tformat:"%s" > rebased3 &&
|
||||
test_cmp expect rebased3
|
||||
'
|
||||
@@ -80,7 +76,7 @@ test_expect_success 'pre-rebase got correct input (3)' '
|
||||
|
||||
test_expect_success 'rebase -i --root --onto <newbase> <branch>' '
|
||||
git branch work4 other &&
|
||||
GIT_EDITOR=: git rebase -i --root --onto master work4 &&
|
||||
git rebase -i --root --onto master work4 &&
|
||||
git log --pretty=tformat:"%s" > rebased4 &&
|
||||
test_cmp expect rebased4
|
||||
'
|
||||
@@ -91,7 +87,7 @@ test_expect_success 'pre-rebase got correct input (4)' '
|
||||
|
||||
test_expect_success 'rebase -i -p with linear history' '
|
||||
git checkout -b work5 other &&
|
||||
GIT_EDITOR=: git rebase -i -p --root --onto master &&
|
||||
git rebase -i -p --root --onto master &&
|
||||
git log --pretty=tformat:"%s" > rebased5 &&
|
||||
test_cmp expect rebased5
|
||||
'
|
||||
@@ -103,28 +99,30 @@ test_expect_success 'pre-rebase got correct input (5)' '
|
||||
test_expect_success 'set up merge history' '
|
||||
git checkout other^ &&
|
||||
git checkout -b side &&
|
||||
echo 5 > C &&
|
||||
git add C &&
|
||||
git commit -m 5 &&
|
||||
test_commit 5 C &&
|
||||
git checkout other &&
|
||||
git merge side
|
||||
'
|
||||
|
||||
sed 's/#/ /g' > expect-side <<'EOF'
|
||||
* Merge branch 'side' into other
|
||||
|\##
|
||||
| * 5
|
||||
* | 4
|
||||
|/##
|
||||
* 3
|
||||
* 2
|
||||
* 1
|
||||
cat > expect-side <<'EOF'
|
||||
commit work6 work6~1 work6^2
|
||||
Merge branch 'side' into other
|
||||
commit work6^2 work6~2
|
||||
5
|
||||
commit work6~1 work6~2
|
||||
4
|
||||
commit work6~2 work6~3
|
||||
3
|
||||
commit work6~3 work6~4
|
||||
2
|
||||
commit work6~4
|
||||
1
|
||||
EOF
|
||||
|
||||
test_expect_success 'rebase -i -p with merge' '
|
||||
git checkout -b work6 other &&
|
||||
GIT_EDITOR=: git rebase -i -p --root --onto master &&
|
||||
git log --graph --topo-order --pretty=tformat:"%s" > rebased6 &&
|
||||
git rebase -i -p --root --onto master &&
|
||||
log_with_names work6 > rebased6 &&
|
||||
test_cmp expect-side rebased6
|
||||
'
|
||||
|
||||
@@ -132,32 +130,34 @@ test_expect_success 'set up second root and merge' '
|
||||
git symbolic-ref HEAD refs/heads/third &&
|
||||
rm .git/index &&
|
||||
rm A B C &&
|
||||
echo 6 > D &&
|
||||
git add D &&
|
||||
git commit -m 6 &&
|
||||
test_commit 6 D &&
|
||||
git checkout other &&
|
||||
git merge third
|
||||
'
|
||||
|
||||
sed 's/#/ /g' > expect-third <<'EOF'
|
||||
* Merge branch 'third' into other
|
||||
|\##
|
||||
| * 6
|
||||
* | Merge branch 'side' into other
|
||||
|\ \##
|
||||
| * | 5
|
||||
* | | 4
|
||||
|/ /##
|
||||
* | 3
|
||||
|/##
|
||||
* 2
|
||||
* 1
|
||||
cat > expect-third <<'EOF'
|
||||
commit work7 work7~1 work7^2
|
||||
Merge branch 'third' into other
|
||||
commit work7^2 work7~4
|
||||
6
|
||||
commit work7~1 work7~2 work7~1^2
|
||||
Merge branch 'side' into other
|
||||
commit work7~1^2 work7~3
|
||||
5
|
||||
commit work7~2 work7~3
|
||||
4
|
||||
commit work7~3 work7~4
|
||||
3
|
||||
commit work7~4 work7~5
|
||||
2
|
||||
commit work7~5
|
||||
1
|
||||
EOF
|
||||
|
||||
test_expect_success 'rebase -i -p with two roots' '
|
||||
git checkout -b work7 other &&
|
||||
GIT_EDITOR=: git rebase -i -p --root --onto master &&
|
||||
git log --graph --topo-order --pretty=tformat:"%s" > rebased7 &&
|
||||
git rebase -i -p --root --onto master &&
|
||||
log_with_names work7 > rebased7 &&
|
||||
test_cmp expect-third rebased7
|
||||
'
|
||||
|
||||
@@ -172,22 +172,14 @@ EOF
|
||||
|
||||
test_expect_success 'pre-rebase hook stops rebase' '
|
||||
git checkout -b stops1 other &&
|
||||
(
|
||||
GIT_EDITOR=:
|
||||
export GIT_EDITOR
|
||||
test_must_fail git rebase --root --onto master
|
||||
) &&
|
||||
test_must_fail git rebase --root --onto master &&
|
||||
test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1
|
||||
test 0 = $(git rev-list other...stops1 | wc -l)
|
||||
'
|
||||
|
||||
test_expect_success 'pre-rebase hook stops rebase -i' '
|
||||
git checkout -b stops2 other &&
|
||||
(
|
||||
GIT_EDITOR=:
|
||||
export GIT_EDITOR
|
||||
test_must_fail git rebase --root --onto master
|
||||
) &&
|
||||
test_must_fail git rebase --root --onto master &&
|
||||
test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2
|
||||
test 0 = $(git rev-list other...stops2 | wc -l)
|
||||
'
|
||||
@@ -232,11 +224,7 @@ test_expect_success 'rebase --root with conflict (second part)' '
|
||||
|
||||
test_expect_success 'rebase -i --root with conflict (first part)' '
|
||||
git checkout -b conflict2 other &&
|
||||
(
|
||||
GIT_EDITOR=:
|
||||
export GIT_EDITOR
|
||||
test_must_fail git rebase -i --root --onto master
|
||||
) &&
|
||||
test_must_fail git rebase -i --root --onto master &&
|
||||
git ls-files -u | grep "B$"
|
||||
'
|
||||
|
||||
@@ -274,11 +262,7 @@ EOF
|
||||
|
||||
test_expect_success 'rebase -i -p --root with conflict (first part)' '
|
||||
git checkout -b conflict3 other &&
|
||||
(
|
||||
GIT_EDITOR=:
|
||||
export GIT_EDITOR
|
||||
test_must_fail git rebase -i -p --root --onto master
|
||||
) &&
|
||||
test_must_fail git rebase -i -p --root --onto master &&
|
||||
git ls-files -u | grep "B$"
|
||||
'
|
||||
|
||||
@@ -289,8 +273,7 @@ test_expect_success 'fix the conflict' '
|
||||
|
||||
test_expect_success 'rebase -i -p --root with conflict (second part)' '
|
||||
git rebase --continue &&
|
||||
git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
|
||||
git name-rev --stdin --name-only --refs=refs/heads/conflict3 >out &&
|
||||
log_with_names conflict3 >out &&
|
||||
test_cmp expect-conflict-p out
|
||||
'
|
||||
|
||||
|
||||
@@ -89,4 +89,11 @@ test_expect_success \
|
||||
git diff-index -M -p $tree > current &&
|
||||
compare_diff_patch current expected'
|
||||
|
||||
test_expect_success \
|
||||
'diff symlinks with non-existing targets' \
|
||||
'ln -s narf pinky &&
|
||||
ln -s take\ over brain &&
|
||||
test_must_fail git diff --no-index pinky brain > output 2> output.err &&
|
||||
grep narf output &&
|
||||
! grep error output.err'
|
||||
test_done
|
||||
|
||||
@@ -104,7 +104,7 @@ cat >expect.typechange <<'EOF'
|
||||
-1
|
||||
diff --git a/file b/file
|
||||
new file mode 120000
|
||||
index ad8b3d2..67be421
|
||||
index 0000000..67be421
|
||||
--- /dev/null
|
||||
+++ b/file
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user