Merge commit 'junio/master' into devel

Conflicts:
	Makefile
This commit is contained in:
Steffen Prohaska
2009-02-06 23:57:41 +01:00
139 changed files with 3184 additions and 833 deletions

2
.gitignore vendored
View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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