Merge v1.5.0.

This commit is contained in:
Johannes Sixt
2007-02-14 09:33:53 +01:00
139 changed files with 13048 additions and 1022 deletions

2
.gitignore vendored
View File

@@ -36,6 +36,7 @@ git-diff-index
git-diff-stages
git-diff-tree
git-describe
git-fast-import
git-fetch
git-fetch-pack
git-findtags
@@ -71,7 +72,6 @@ git-merge-tree
git-merge-octopus
git-merge-one-file
git-merge-ours
git-merge-recur
git-merge-recursive
git-merge-resolve
git-merge-stupid

View File

@@ -31,6 +31,8 @@ man1dir=$(mandir)/man1
man7dir=$(mandir)/man7
# DESTDIR=
ASCIIDOC=asciidoc
ASCIIDOC_EXTRA =
INSTALL?=install
DOC_REF = origin/man
@@ -91,16 +93,16 @@ clean:
rm -f $(cmds_txt)
%.html : %.txt
asciidoc -b xhtml11 -d manpage -f asciidoc.conf $<
$(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf $(ASCIIDOC_EXTRA) $<
%.1 %.7 : %.xml
xmlto -m callouts.xsl man $<
%.xml : %.txt
asciidoc -b docbook -d manpage -f asciidoc.conf $<
$(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf $<
user-manual.xml: user-manual.txt user-manual.conf
asciidoc -b docbook -d book $<
$(ASCIIDOC) -b docbook -d book $<
user-manual.html: user-manual.xml
xmlto html-nochunks $<
@@ -108,7 +110,7 @@ user-manual.html: user-manual.xml
glossary.html : glossary.txt sort_glossary.pl
cat $< | \
perl sort_glossary.pl | \
asciidoc -b xhtml11 - > glossary.html
$(ASCIIDOC) -b xhtml11 - > glossary.html
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
rm -f $@+ $@
@@ -116,13 +118,13 @@ howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
mv $@+ $@
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
asciidoc -b xhtml11 $*.txt
$(ASCIIDOC) -b xhtml11 $*.txt
WEBDOC_DEST = /pub/software/scm/git/docs
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
rm -f $@+ $@
sed -e '1,/^$$/d' $< | asciidoc -b xhtml11 - >$@+
sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+
mv $@+ $@
install-webdoc : html

View File

@@ -0,0 +1,468 @@
GIT v1.5.0 Release Notes
========================
Old news
--------
This section is for people who are upgrading from ancient
versions of git. Although all of the changes in this section
happened before the current v1.4.4 release, they are summarized
here in the v1.5.0 release notes for people who skipped earlier
versions.
As of git v1.5.0 there are some optional features that changes
the repository to allow data to be stored and transferred more
efficiently. These features are not enabled by default, as they
will make the repository unusable with older versions of git.
Specifically, the available options are:
- There is a configuration variable core.legacyheaders that
changes the format of loose objects so that they are more
efficient to pack and to send out of the repository over git
native protocol, since v1.4.2. However, loose objects
written in the new format cannot be read by git older than
that version; people fetching from your repository using
older clients over dumb transports (e.g. http) using older
versions of git will also be affected.
- Since v1.4.3, configuration repack.usedeltabaseoffset allows
packfile to be created in more space efficient format, which
cannot be read by git older than that version.
The above two are not enabled by default and you explicitly have
to ask for them, because these two features make repositories
unreadable by older versions of git, and in v1.5.0 we still do
not enable them by default for the same reason. We will change
this default probably 1 year after 1.4.2's release, when it is
reasonable to expect everybody to have new enough version of
git.
- 'git pack-refs' appeared in v1.4.4; this command allows tags
to be accessed much more efficiently than the traditional
'one-file-per-tag' format. Older git-native clients can
still fetch from a repository that packed and pruned refs
(the server side needs to run the up-to-date version of git),
but older dumb transports cannot. Packing of refs is done by
an explicit user action, either by use of "git pack-refs
--prune" command or by use of "git gc" command.
- 'git -p' to paginate anything -- many commands do pagination
by default on a tty. Introduced between v1.4.1 and v1.4.2;
this may surprise old timers.
- 'git archive' superseded 'git tar-tree' in v1.4.3;
- 'git cvsserver' was new invention in v1.3.0;
- 'git repo-config', 'git grep', 'git rebase' and 'gitk' were
seriously enhanced during v1.4.0 timeperiod.
- 'gitweb' became part of git.git during v1.4.0 timeperiod and
seriously modified since then.
- reflog is an v1.4.0 invention. This allows you to name a
revision that a branch used to be at (e.g. "git diff
master@{yesterday} master" allows you to see changes since
yesterday's tip of the branch).
Updates in v1.5.0 since v1.4.4 series
-------------------------------------
* Index manipulation
- git-add is to add contents to the index (aka "staging area"
for the next commit), whether the file the contents happen to
be is an existing one or a newly created one.
- git-add without any argument does not add everything
anymore. Use 'git-add .' instead. Also you can add
otherwise ignored files with an -f option.
- git-add tries to be more friendly to users by offering an
interactive mode ("git-add -i").
- git-commit <path> used to refuse to commit if <path> was
different between HEAD and the index (i.e. update-index was
used on it earlier). This check was removed.
- git-rm is much saner and safer. It is used to remove paths
from both the index file and the working tree, and makes sure
you are not losing any local modification before doing so.
- git-reset <tree> <paths>... can be used to revert index
entries for selected paths.
- git-update-index is much less visible. Many suggestions to
use the command in git output and documentation have now been
replaced by simpler commands such as "git add" or "git rm".
* Repository layout and objects transfer
- The data for origin repository is stored in the configuration
file $GIT_DIR/config, not in $GIT_DIR/remotes/, for newly
created clones. The latter is still supported and there is
no need to convert your existing repository if you are
already comfortable with your workflow with the layout.
- git-clone always uses what is known as "separate remote"
layout for a newly created repository with a working tree.
A repository with the separate remote layout starts with only
one default branch, 'master', to be used for your own
development. Unlike the traditional layout that copied all
the upstream branches into your branch namespace (while
renaming their 'master' to your 'origin'), the new layout
puts upstream branches into local "remote-tracking branches"
with their own namespace. These can be referenced with names
such as "origin/$upstream_branch_name" and are stored in
.git/refs/remotes rather than .git/refs/heads where normal
branches are stored.
This layout keeps your own branch namespace less cluttered,
avoids name collision with your upstream, makes it possible
to automatically track new branches created at the remote
after you clone from it, and makes it easier to interact with
more than one remote repository (you can use "git remote" to
add other repositories to track). There might be some
surprises:
* 'git branch' does not show the remote tracking branches.
It only lists your own branches. Use '-r' option to view
the tracking branches.
* If you are forking off of a branch obtained from the
upstream, you would have done something like 'git branch
my-next next', because traditional layout dropped the
tracking branch 'next' into your own branch namespace.
With the separate remote layout, you say 'git branch next
origin/next', which allows you to use the matching name
'next' for your own branch. It also allows you to track a
remote other than 'origin' (i.e. where you initially cloned
from) and fork off of a branch from there the same way
(e.g. "git branch mingw j6t/master").
Repositories initialized with the traditional layout continue
to work.
- New branches that appear on the origin side after a clone is
made are also tracked automatically. This is done with an
wildcard refspec "refs/heads/*:refs/remotes/origin/*", which
older git does not understand, so if you clone with 1.5.0,
you would need to downgrade remote.*.fetch in the
configuration file to specify each branch you are interested
in individually if you plan to fetch into the repository with
older versions of git (but why would you?).
- Similarly, wildcard refspec "refs/heads/*:refs/remotes/me/*"
can be given to "git-push" command to update the tracking
branches that is used to track the repository you are pushing
from on the remote side.
- git-branch and git-show-branch know remote tracking branches
(use the command line switch "-r" to list only tracked branches).
- git-push can now be used to delete a remote branch or a tag.
This requires the updated git on the remote side (use "git
push <remote> :refs/heads/<branch>" to delete "branch").
- git-push more aggressively keeps the transferred objects
packed. Earlier we recommended to monitor amount of loose
objects and repack regularly, but you should repack when you
accumulated too many small packs this way as well. Updated
git-count-objects helps you with this.
- git-fetch also more aggressively keeps the transferred objects
packed. This behavior of git-push and git-fetch can be
tweaked with a single configuration transfer.unpacklimit (but
usually there should not be any need for a user to tweak it).
- A new command, git-remote, can help you manage your remote
tracking branch definitions.
- You may need to specify explicit paths for upload-pack and/or
receive-pack due to your ssh daemon configuration on the
other end. This can now be done via remote.*.uploadpack and
remote.*.receivepack configuration.
* Bare repositories
- Certain commands change their behavior in a bare repository
(i.e. a repository without associated working tree). We use
a fairly conservative heuristic (if $GIT_DIR is ".git", or
ends with "/.git", the repository is not bare) to decide if a
repository is bare, but "core.bare" configuration variable
can be used to override the heuristic when it misidentifies
your repository.
- git-fetch used to complain updating the current branch but
this is now allowed for a bare repository. So is the use of
'git-branch -f' to update the current branch.
- Porcelain-ish commands that require a working tree refuses to
work in a bare repository.
* Reflog
- Reflog records the history from the view point of the local
repository. In other words, regardless of the real history,
the reflog shows the history as seen by one particular
repository (this enables you to ask "what was the current
revision in _this_ repository, yesterday at 1pm?"). This
facility is enabled by default for repositories with working
trees, and can be accessed with the "branch@{time}" and
"branch@{Nth}" notation.
- "git show-branch" learned showing the reflog data with the
new -g option. "git log" has -s option to view reflog
entries in a more verbose manner.
- git-branch knows how to rename branches and moves existing
reflog data from the old branch to the new one.
- In addition to the reflog support in v1.4.4 series, HEAD
reference maintains its own log. "HEAD@{5.minutes.ago}"
means the commit you were at 5 minutes ago, which takes
branch switching into account. If you want to know where the
tip of your current branch was at 5 minutes ago, you need to
explicitly say its name (e.g. "master@{5.minutes.ago}") or
omit the refname altogether i.e. "@{5.minutes.ago}".
- The commits referred to by reflog entries are now protected
against pruning. The new command "git reflog expire" can be
used to truncate older reflog entries and entries that refer
to commits that have been pruned away previously with older
versions of git.
Existing repositories that have been using reflog may get
complaints from fsck-objects and may not be able to run
git-repack, if you had run git-prune from older git; please
run "git reflog expire --stale-fix --all" first to remove
reflog entries that refer to commits that are no longer in
the repository when that happens.
* Crufts removal
- We used to say "old commits are retrievable using reflog and
'master@{yesterday}' syntax as long as you haven't run
git-prune". We no longer have to say the latter half of the
above sentence, as git-prune does not remove things reachable
from reflog entries.
- 'git-prune' by default does not remove _everything_
unreachable, as there is a one-day grace period built-in.
- There is a toplevel garbage collector script, 'git-gc', that
runs periodic cleanup functions, including 'git-repack -a -d',
'git-reflog expire', 'git-pack-refs --prune', and 'git-rerere
gc'.
- The output from fsck ("fsck-objects" is called just "fsck"
now, but the old name continues to work) was needlessly
alarming in that it warned missing objects that are reachable
only from dangling objects. This has been corrected and the
output is much more useful.
* Detached HEAD
- You can use 'git-checkout' to check out an arbitrary revision
or a tag as well, instead of named branches. This will
dissociate your HEAD from the branch you are currently on.
A typical use of this feature is to "look around". E.g.
$ git checkout v2.6.16
... compile, test, etc.
$ git checkout v2.6.17
... compile, test, etc.
- After detaching your HEAD, you can go back to an existing
branch with usual "git checkout $branch". Also you can
start a new branch using "git checkout -b $newbranch" to
start a new branch at that commit.
- You can even pull from other repositories, make merges and
commits while your HEAD is detached. Also you can use "git
reset" to jump to arbitrary commit, while still keeping your
HEAD detached.
Going back to attached state (i.e. on a particular branch) by
"git checkout $branch" can lose the current stat you arrived
in these ways, and "git checkout" refuses when the detached
HEAD is not pointed by any existing ref (an existing branch,
a remote tracking branch or a tag). This safety can be
overridden with "git checkout -f $branch".
* Packed refs
- Repositories with hundreds of tags have been paying large
overhead, both in storage and in runtime, due to the
traditional one-ref-per-file format. A new command,
git-pack-refs, can be used to "pack" them in more efficient
representation (you can let git-gc do this for you).
- Clones and fetches over dumb transports are now aware of
packed refs and can download from repositories that use
them.
* Configuration
- configuration related to color setting are consolidated under
color.* namespace (older diff.color.*, status.color.* are
still supported).
- 'git-repo-config' command is accessible as 'git-config' now.
* Updated features
- git-describe uses better criteria to pick a base ref. It
used to pick the one with the newest timestamp, but now it
picks the one that is topologically the closest (that is,
among ancestors of commit C, the ref T that has the shortest
output from "git-rev-list T..C" is chosen).
- git-describe gives the number of commits since the base ref
between the refname and the hash suffix. E.g. the commit one
before v2.6.20-rc6 in the kernel repository is:
v2.6.20-rc5-306-ga21b069
which tells you that its object name begins with a21b069,
v2.6.20-rc5 is an ancestor of it (meaning, the commit
contains everything -rc5 has), and there are 306 commits
since v2.6.20-rc5.
- git-describe with --abbrev=0 can be used to show only the
name of the base ref.
- git-blame learned a new option, --incremental, that tells it
to output the blames as they are assigned. A sample script
to use it is also included as contrib/blameview.
- git-blame starts annotating from the working tree by default.
* Less external dependency
- We no longer require the "merge" program from the RCS suite.
All 3-way file-level merges are now done internally.
- The original implementation of git-merge-recursive which was
in Python has been removed; we have a C implementation of it
now.
- git-shortlog is no longer a Perl script. It no longer
requires output piped from git-log; it can accept revision
parameters directly on the command line.
* I18n
- We have always encouraged the commit message to be encoded in
UTF-8, but the users are allowed to use legacy encoding as
appropriate for their projects. This will continue to be the
case. However, a non UTF-8 commit encoding _must_ be
explicitly set with i18n.commitencoding in the repository
where a commit is made; otherwise git-commit-tree will
complain if the log message does not look like a valid UTF-8
string.
- The value of i18n.commitencoding in the originating
repository is recorded in the commit object on the "encoding"
header, if it is not UTF-8. git-log and friends notice this,
and reencodes the message to the log output encoding when
displaying, if they are different. The log output encoding
is determined by "git log --encoding=<encoding>",
i18n.logoutputencoding configuration, or i18n.commitencoding
configuration, in the decreasing order of preference, and
defaults to UTF-8.
- Tools for e-mailed patch application now default to -u
behavior; i.e. it always re-codes from the e-mailed encoding
to the encoding specified with i18n.commitencoding. This
unfortunately forces projects that have happily been using a
legacy encoding without setting i18n.commitencoding to set
the configuration, but taken with other improvement, please
excuse us for this very minor one-time inconvenience.
* e-mailed patches
- See the above I18n section.
- git-format-patch now enables --binary without being asked.
git-am does _not_ default to it, as sending binary patch via
e-mail is unusual and is harder to review than textual
patches and it is prudent to require the person who is
applying the patch to explicitly ask for it.
- The default suffix for git-format-patch output is now ".patch",
not ".txt". This can be changed with --suffix=.txt option,
or setting the config variable "format.suffix" to ".txt".
* Foreign SCM interfaces
- git-svn now requires the Perl SVN:: libraries, the
command-line backend was too slow and limited.
- the 'commit' subcommand of git-svn has been renamed to
'set-tree', and 'dcommit' is the recommended replacement for
day-to-day work.
- git fast-import backend.
* User support
- Quite a lot of documentation updates.
- Bash completion scripts have been updated heavily.
- Better error messages for often used Porcelainish commands.
- Git GUI. This is a simple Tk based graphical interface for
common Git operations.
* Sliding mmap
- We used to assume that we can mmap the whole packfile while
in use, but with a large project this consumes huge virtual
memory space and truly huge ones would not fit in the
userland address space on 32-bit platforms. We now mmap huge
packfile in pieces to avoid this problem.
* Shallow clones
- There is a partial support for 'shallow' repositories that
keeps only recent history. A 'shallow clone' is created by
specifying how deep that truncated history should be
(e.g. "git clone --depth=5 git://some.where/repo.git").
Currently a shallow repository has number of limitations:
- Cloning and fetching _from_ a shallow clone are not
supported (nor tested -- so they might work by accident but
they are not expected to).
- Pushing from nor into a shallow clone are not expected to
work.
- Merging inside a shallow repository would work as long as a
merge base is found in the recent history, but otherwise it
will be like merging unrelated histories and may result in
huge conflicts.
but this would be more than adequate for people who want to
look at near the tip of a big project with a deep history and
send patches in e-mail format.

View File

@@ -316,7 +316,6 @@ settings but I haven't tried, yet.
mail.identity.id?.compose_html => false
Gnus
----
@@ -331,3 +330,20 @@ whitespaces (fatal in patches). Running 'C-u g' to display the
message in raw form before using '|' to run the pipe can work
this problem around.
KMail
-----
This should help you to submit patches inline using KMail.
1) Prepare the patch as a text file.
2) Click on New Mail.
3) Go under "Options" in the Composer window and be sure that
"Word wrap" is not set.
4) Use Message -> Insert file... and insert the patch.
5) Back in the compose window: add whatever other text you wish to the
message, complete the addressing and subject fields, and press send.

View File

@@ -92,6 +92,7 @@ git-diff-index plumbinginterrogators
git-diff mainporcelain
git-diff-stages plumbinginterrogators
git-diff-tree plumbinginterrogators
git-fast-import ancillarymanipulators
git-fetch mainporcelain
git-fetch-pack synchingrepositories
git-fmt-merge-msg purehelpers

View File

@@ -39,7 +39,7 @@ in the section header, like in example below:
Subsection names can contain any characters except newline (doublequote
'`"`' and backslash have to be escaped as '`\"`' and '`\\`',
respecitvely) and are case sensitive. Section header cannot span multiple
respectively) and are case sensitive. Section header cannot span multiple
lines. Variables may belong directly to a section or to a given subsection.
You can have `[section]` if you have `[section "subsection"]`, but you
don't need to.
@@ -222,6 +222,12 @@ alias.*::
spaces, the usual shell quoting and escaping is supported.
quote pair and a backslash can be used to quote them.
If the alias expansion is prefixed with an exclamation point,
it will be treated as a shell command. For example, defining
"alias.new = !gitk --all --not ORIG_HEAD", the invocation
"git new" is equivalent to running the shell command
"gitk --all --not ORIG_HEAD".
apply.whitespace::
Tells `git-apply` how to handle whitespaces, in the same way
as the '--whitespace' option. See gitlink:git-apply[1].
@@ -315,6 +321,17 @@ format.headers::
Additional email headers to include in a patch to be submitted
by mail. See gitlink:git-format-patch[1].
gc.packrefs::
`git gc` does not run `git pack-refs` in a bare repository by
default so that older dumb-transport clients can still fetch
from the repository. Setting this to `true` lets `git
gc` to run `git pack-refs`. Setting this to `false` tells
`git gc` never to run `git pack-refs`. The default setting is
`notbare`. Enable it only when you know you do not have to
support such clients. The default setting will change to `true`
at some stage, and setting this to `false` will continue to
prevent `git pack-refs` from being run from `git gc`.
gc.reflogexpire::
`git reflog expire` removes reflog entries older than
this time; defaults to 90 days.

View File

@@ -624,7 +624,7 @@ name for the state at that point.
Copying repositories
--------------------
git repositories are normally totally self-sufficient and relocatable
git repositories are normally totally self-sufficient and relocatable.
Unlike CVS, for example, there is no separate notion of
"repository" and "working tree". A git repository normally *is* the
working tree, with the local git information hidden in the `.git`
@@ -1118,7 +1118,7 @@ You could do without using any branches at all, by
keeping as many local repositories as you would like to have
branches, and merging between them with `git pull`, just like
you merge between branches. The advantage of this approach is
that it lets you keep set of files for each `branch` checked
that it lets you keep a set of files for each `branch` checked
out and you may find it easier to switch back and forth if you
juggle multiple lines of development simultaneously. Of
course, you will pay the price of more disk usage to hold
@@ -1300,7 +1300,7 @@ differences since stage 2 (i.e. your version).
Publishing your work
--------------------
So we can use somebody else's work from a remote repository; but
So, we can use somebody else's work from a remote repository, but
how can *you* prepare a repository to let other people pull from
it?
@@ -1469,8 +1469,8 @@ Working with Others
Although git is a truly distributed system, it is often
convenient to organize your project with an informal hierarchy
of developers. Linux kernel development is run this way. There
is a nice illustration (page 17, "Merges to Mainline") in Randy
Dunlap's presentation (`http://tinyurl.com/a2jdg`).
is a nice illustration (page 17, "Merges to Mainline") in
link:http://tinyurl.com/a2jdg[Randy Dunlap's presentation].
It should be stressed that this hierarchy is purely *informal*.
There is nothing fundamental in git that enforces the "chain of

View File

@@ -159,7 +159,7 @@ or like this (when '--cc' option is used):
deleted file mode <mode>,<mode>
+
The `mode <mode>,<mode>..<mode>` line appears only if at least one of
the <mode> is diferent from the rest. Extended headers with
the <mode> is different from the rest. Extended headers with
information about detected contents movement (renames and
copying detection) are designed to work with diff of two
<tree-ish> and are not used by combined diff format.

View File

@@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git-am' [--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way]
[--interactive] [--whitespace=<option>] <mbox>...
[--interactive] [--whitespace=<option>] [-C<n>] [-p<n>]
<mbox>...
'git-am' [--skip | --resolved]
DESCRIPTION
@@ -21,6 +22,10 @@ current branch.
OPTIONS
-------
<mbox>...::
The list of mailbox files to read patches from. If you do not
supply this argument, reads from the standard input.
--signoff::
Add `Signed-off-by:` line to the commit message, using
the committer identity of yourself.
@@ -64,6 +69,10 @@ default. You could use `--no-utf8` to override this.
This flag is passed to the `git-apply` program that applies
the patch.
-C<n>, -p<n>::
These flag are passed to the `git-apply` program that applies
the patch.
--interactive::
Run interactively, just like git-applymbox.

View File

@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] [-S <revs-file>]
[-M] [-C] [-C] [--since=<date>] [<rev>] [--] <file>
[-M] [-C] [-C] [--since=<date>] [<rev> | --contents <file>] [--] <file>
DESCRIPTION
-----------
@@ -67,6 +67,13 @@ OPTIONS
Show the result incrementally in a format designed for
machine consumption.
--contents <file>::
When <rev> is not specified, the command annotates the
changes starting backwards from the working tree copy.
This flag makes the command pretend as if the working
tree copy has the contents of he named file (specify
`-` to make the command read from the standard input).
-M::
Detect moving lines in the file as well. When a commit
moves a block of lines in a file (e.g. the original file

View File

@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch
SYNOPSIS
--------
[verse]
'git-checkout' [-f] [-b <new_branch> [-l]] [-m] [<branch>]
'git-checkout' [-q] [-f] [-b <new_branch> [-l]] [-m] [<branch>]
'git-checkout' [<tree-ish>] <paths>...
DESCRIPTION
@@ -33,6 +33,9 @@ working tree.
OPTIONS
-------
-q::
Quiet, supress feedback messages.
-f::
Force a re-read of everything.
@@ -100,22 +103,12 @@ by any branch (which is natural --- you are not on any branch).
What this means is that you can discard your temporary commits
and merges by switching back to an existing branch (e.g. `git
checkout master`), and a later `git prune` or `git gc` would
garbage-collect them.
garbage-collect them. If you did this by mistake, you can ask
the reflog for HEAD where you were, e.g.
The command would refuse to switch back to make sure that you do
not discard your temporary state by mistake when your detached
HEAD is not pointed at by any existing ref. If you did want to
save your state (e.g. "I was interested in the fifth commit from
the top of 'master' branch", or "I made two commits to fix minor
bugs while on a detached HEAD" -- and if you do not want to lose
these facts), you can create a new branch and switch to it with
`git checkout -b newbranch` so that you can keep building on
that state, or tag it first so that you can come back to it
later and switch to the branch you wanted to switch to with `git
tag that_state; git checkout master`. On the other hand, if you
did want to discard the temporary state, you can give `-f`
option (e.g. `git checkout -f master`) to override this
behaviour.
------------
$ git log -g -2 HEAD
------------
EXAMPLES

View File

@@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout
SYNOPSIS
--------
'git-cvsexportcommit' [-h] [-v] [-c] [-p] [-a] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
'git-cvsexportcommit' [-h] [-v] [-c] [-P] [-p] [-a] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
DESCRIPTION
@@ -46,6 +46,9 @@ OPTIONS
-f::
Force the merge even if the files are not up to date.
-P::
Force the parent commit, even if it is not a direct parent.
-m::
Prepend the commit message with the provided prefix.
Useful for patch series and the like.

View File

@@ -12,6 +12,8 @@ SYNOPSIS
DESCRIPTION
-----------
DEPRECATED and will be removed in 1.5.1.
Compares the content and mode of the blobs in two stages in an
unmerged index file.

View File

@@ -0,0 +1,901 @@
git-fast-import(1)
==================
NAME
----
git-fast-import - Backend for fast Git data importers.
SYNOPSIS
--------
frontend | 'git-fast-import' [options]
DESCRIPTION
-----------
This program is usually not what the end user wants to run directly.
Most end users want to use one of the existing frontend programs,
which parses a specific type of foreign source and feeds the contents
stored there to git-fast-import.
fast-import reads a mixed command/data stream from standard input and
writes one or more packfiles directly into the current repository.
When EOF is received on standard input, fast import writes out
updated branch and tag refs, fully updating the current repository
with the newly imported data.
The fast-import backend itself can import into an empty repository (one that
has already been initialized by gitlink:git-init[1]) or incrementally
update an existing populated repository. Whether or not incremental
imports are supported from a particular foreign source depends on
the frontend program in use.
OPTIONS
-------
--date-format=<fmt>::
Specify the type of dates the frontend will supply to
fast-import within `author`, `committer` and `tagger` commands.
See ``Date Formats'' below for details about which formats
are supported, and their syntax.
--force::
Force updating modified existing branches, even if doing
so would cause commits to be lost (as the new commit does
not contain the old commit).
--max-pack-size=<n>::
Maximum size of each output packfile, expressed in MiB.
The default is 4096 (4 GiB) as that is the maximum allowed
packfile size (due to file format limitations). Some
importers may wish to lower this, such as to ensure the
resulting packfiles fit on CDs.
--depth=<n>::
Maximum delta depth, for blob and tree deltification.
Default is 10.
--active-branches=<n>::
Maximum number of branches to maintain active at once.
See ``Memory Utilization'' below for details. Default is 5.
--export-marks=<file>::
Dumps the internal marks table to <file> when complete.
Marks are written one per line as `:markid SHA-1`.
Frontends can use this file to validate imports after they
have been completed.
--export-pack-edges=<file>::
After creating a packfile, print a line of data to
<file> listing the filename of the packfile and the last
commit on each branch that was written to that packfile.
This information may be useful after importing projects
whose total object set exceeds the 4 GiB packfile limit,
as these commits can be used as edge points during calls
to gitlink:git-pack-objects[1].
--quiet::
Disable all non-fatal output, making fast-import silent when it
is successful. This option disables the output shown by
\--stats.
--stats::
Display some basic statistics about the objects fast-import has
created, the packfiles they were stored into, and the
memory used by fast-import during this run. Showing this output
is currently the default, but can be disabled with \--quiet.
Performance
-----------
The design of fast-import allows it to import large projects in a minimum
amount of memory usage and processing time. Assuming the frontend
is able to keep up with fast-import and feed it a constant stream of data,
import times for projects holding 10+ years of history and containing
100,000+ individual commits are generally completed in just 1-2
hours on quite modest (~$2,000 USD) hardware.
Most bottlenecks appear to be in foreign source data access (the
source just cannot extract revisions fast enough) or disk IO (fast-import
writes as fast as the disk will take the data). Imports will run
faster if the source data is stored on a different drive than the
destination Git repository (due to less IO contention).
Development Cost
----------------
A typical frontend for fast-import tends to weigh in at approximately 200
lines of Perl/Python/Ruby code. Most developers have been able to
create working importers in just a couple of hours, even though it
is their first exposure to fast-import, and sometimes even to Git. This is
an ideal situation, given that most conversion tools are throw-away
(use once, and never look back).
Parallel Operation
------------------
Like `git-push` or `git-fetch`, imports handled by fast-import are safe to
run alongside parallel `git repack -a -d` or `git gc` invocations,
or any other Git operation (including `git prune`, as loose objects
are never used by fast-import).
fast-import does not lock the branch or tag refs it is actively importing.
After the import, during its ref update phase, fast-import tests each
existing branch ref to verify the update will be a fast-forward
update (the commit stored in the ref is contained in the new
history of the commit to be written). If the update is not a
fast-forward update, fast-import will skip updating that ref and instead
prints a warning message. fast-import will always attempt to update all
branch refs, and does not stop on the first failure.
Branch updates can be forced with \--force, but its recommended that
this only be used on an otherwise quiet repository. Using \--force
is not necessary for an initial import into an empty repository.
Technical Discussion
--------------------
fast-import tracks a set of branches in memory. Any branch can be created
or modified at any point during the import process by sending a
`commit` command on the input stream. This design allows a frontend
program to process an unlimited number of branches simultaneously,
generating commits in the order they are available from the source
data. It also simplifies the frontend programs considerably.
fast-import does not use or alter the current working directory, or any
file within it. (It does however update the current Git repository,
as referenced by `GIT_DIR`.) Therefore an import frontend may use
the working directory for its own purposes, such as extracting file
revisions from the foreign source. This ignorance of the working
directory also allows fast-import to run very quickly, as it does not
need to perform any costly file update operations when switching
between branches.
Input Format
------------
With the exception of raw file data (which Git does not interpret)
the fast-import input format is text (ASCII) based. This text based
format simplifies development and debugging of frontend programs,
especially when a higher level language such as Perl, Python or
Ruby is being used.
fast-import is very strict about its input. Where we say SP below we mean
*exactly* one space. Likewise LF means one (and only one) linefeed.
Supplying additional whitespace characters will cause unexpected
results, such as branch names or file names with leading or trailing
spaces in their name, or early termination of fast-import when it encounters
unexpected input.
Date Formats
~~~~~~~~~~~~
The following date formats are supported. A frontend should select
the format it will use for this import by passing the format name
in the \--date-format=<fmt> command line option.
`raw`::
This is the Git native format and is `<time> SP <offutc>`.
It is also fast-import's default format, if \--date-format was
not specified.
+
The time of the event is specified by `<time>` as the number of
seconds since the UNIX epoch (midnight, Jan 1, 1970, UTC) and is
written as an ASCII decimal integer.
+
The local offset is specified by `<offutc>` as a positive or negative
offset from UTC. For example EST (which is 5 hours behind UTC)
would be expressed in `<tz>` by ``-0500'' while UTC is ``+0000''.
The local offset does not affect `<time>`; it is used only as an
advisement to help formatting routines display the timestamp.
+
If the local offset is not available in the source material, use
``+0000'', or the most common local offset. For example many
organizations have a CVS repository which has only ever been accessed
by users who are located in the same location and timezone. In this
case a reasonable offset from UTC could be assumed.
+
Unlike the `rfc2822` format, this format is very strict. Any
variation in formatting will cause fast-import to reject the value.
`rfc2822`::
This is the standard email format as described by RFC 2822.
+
An example value is ``Tue Feb 6 11:22:18 2007 -0500''. The Git
parser is accurate, but a little on the lenient side. It is the
same parser used by gitlink:git-am[1] when applying patches
received from email.
+
Some malformed strings may be accepted as valid dates. In some of
these cases Git will still be able to obtain the correct date from
the malformed string. There are also some types of malformed
strings which Git will parse wrong, and yet consider valid.
Seriously malformed strings will be rejected.
+
Unlike the `raw` format above, the timezone/UTC offset information
contained in an RFC 2822 date string is used to adjust the date
value to UTC prior to storage. Therefore it is important that
this information be as accurate as possible.
+
If the source material uses RFC 2822 style dates,
the frontend should let fast-import handle the parsing and conversion
(rather than attempting to do it itself) as the Git parser has
been well tested in the wild.
+
Frontends should prefer the `raw` format if the source material
already uses UNIX-epoch format, can be coaxed to give dates in that
format, or its format is easiliy convertible to it, as there is no
ambiguity in parsing.
`now`::
Always use the current time and timezone. The literal
`now` must always be supplied for `<when>`.
+
This is a toy format. The current time and timezone of this system
is always copied into the identity string at the time it is being
created by fast-import. There is no way to specify a different time or
timezone.
+
This particular format is supplied as its short to implement and
may be useful to a process that wants to create a new commit
right now, without needing to use a working directory or
gitlink:git-update-index[1].
+
If separate `author` and `committer` commands are used in a `commit`
the timestamps may not match, as the system clock will be polled
twice (once for each command). The only way to ensure that both
author and committer identity information has the same timestamp
is to omit `author` (thus copying from `committer`) or to use a
date format other than `now`.
Commands
~~~~~~~~
fast-import accepts several commands to update the current repository
and control the current import process. More detailed discussion
(with examples) of each command follows later.
`commit`::
Creates a new branch or updates an existing branch by
creating a new commit and updating the branch to point at
the newly created commit.
`tag`::
Creates an annotated tag object from an existing commit or
branch. Lightweight tags are not supported by this command,
as they are not recommended for recording meaningful points
in time.
`reset`::
Reset an existing branch (or a new branch) to a specific
revision. This command must be used to change a branch to
a specific revision without making a commit on it.
`blob`::
Convert raw file data into a blob, for future use in a
`commit` command. This command is optional and is not
needed to perform an import.
`checkpoint`::
Forces fast-import to close the current packfile, generate its
unique SHA-1 checksum and index, and start a new packfile.
This command is optional and is not needed to perform
an import.
`commit`
~~~~~~~~
Create or update a branch with a new commit, recording one logical
change to the project.
....
'commit' SP <ref> LF
mark?
('author' SP <name> SP LT <email> GT SP <when> LF)?
'committer' SP <name> SP LT <email> GT SP <when> LF
data
('from' SP <committish> LF)?
('merge' SP <committish> LF)?
(filemodify | filedelete | filedeleteall)*
LF
....
where `<ref>` is the name of the branch to make the commit on.
Typically branch names are prefixed with `refs/heads/` in
Git, so importing the CVS branch symbol `RELENG-1_0` would use
`refs/heads/RELENG-1_0` for the value of `<ref>`. The value of
`<ref>` must be a valid refname in Git. As `LF` is not valid in
a Git refname, no quoting or escaping syntax is supported here.
A `mark` command may optionally appear, requesting fast-import to save a
reference to the newly created commit for future use by the frontend
(see below for format). It is very common for frontends to mark
every commit they create, thereby allowing future branch creation
from any imported commit.
The `data` command following `committer` must supply the commit
message (see below for `data` command syntax). To import an empty
commit message use a 0 length data. Commit messages are free-form
and are not interpreted by Git. Currently they must be encoded in
UTF-8, as fast-import does not permit other encodings to be specified.
Zero or more `filemodify`, `filedelete` and `filedeleteall` commands
may be included to update the contents of the branch prior to
creating the commit. These commands may be supplied in any order.
However it is recommended that a `filedeleteall` command preceed
all `filemodify` commands in the same commit, as `filedeleteall`
wipes the branch clean (see below).
`author`
^^^^^^^^
An `author` command may optionally appear, if the author information
might differ from the committer information. If `author` is omitted
then fast-import will automatically use the committer's information for
the author portion of the commit. See below for a description of
the fields in `author`, as they are identical to `committer`.
`committer`
^^^^^^^^^^^
The `committer` command indicates who made this commit, and when
they made it.
Here `<name>` is the person's display name (for example
``Com M Itter'') and `<email>` is the person's email address
(``cm@example.com''). `LT` and `GT` are the literal less-than (\x3c)
and greater-than (\x3e) symbols. These are required to delimit
the email address from the other fields in the line. Note that
`<name>` is free-form and may contain any sequence of bytes, except
`LT` and `LF`. It is typically UTF-8 encoded.
The time of the change is specified by `<when>` using the date format
that was selected by the \--date-format=<fmt> command line option.
See ``Date Formats'' above for the set of supported formats, and
their syntax.
`from`
^^^^^^
The `from` command is used to specify the commit to initialize
this branch from. This revision will be the first ancestor of the
new commit.
Omitting the `from` command in the first commit of a new branch
will cause fast-import to create that commit with no ancestor. This
tends to be desired only for the initial commit of a project.
Omitting the `from` command on existing branches is usually desired,
as the current commit on that branch is automatically assumed to
be the first ancestor of the new commit.
As `LF` is not valid in a Git refname or SHA-1 expression, no
quoting or escaping syntax is supported within `<committish>`.
Here `<committish>` is any of the following:
* The name of an existing branch already in fast-import's internal branch
table. If fast-import doesn't know the name, its treated as a SHA-1
expression.
* A mark reference, `:<idnum>`, where `<idnum>` is the mark number.
+
The reason fast-import uses `:` to denote a mark reference is this character
is not legal in a Git branch name. The leading `:` makes it easy
to distingush between the mark 42 (`:42`) and the branch 42 (`42`
or `refs/heads/42`), or an abbreviated SHA-1 which happened to
consist only of base-10 digits.
+
Marks must be declared (via `mark`) before they can be used.
* A complete 40 byte or abbreviated commit SHA-1 in hex.
* Any valid Git SHA-1 expression that resolves to a commit. See
``SPECIFYING REVISIONS'' in gitlink:git-rev-parse[1] for details.
The special case of restarting an incremental import from the
current branch value should be written as:
----
from refs/heads/branch^0
----
The `{caret}0` suffix is necessary as fast-import does not permit a branch to
start from itself, and the branch is created in memory before the
`from` command is even read from the input. Adding `{caret}0` will force
fast-import to resolve the commit through Git's revision parsing library,
rather than its internal branch table, thereby loading in the
existing value of the branch.
`merge`
^^^^^^^
Includes one additional ancestor commit, and makes the current
commit a merge commit. An unlimited number of `merge` commands per
commit are permitted by fast-import, thereby establishing an n-way merge.
However Git's other tools never create commits with more than 15
additional ancestors (forming a 16-way merge). For this reason
it is suggested that frontends do not use more than 15 `merge`
commands per commit.
Here `<committish>` is any of the commit specification expressions
also accepted by `from` (see above).
`filemodify`
^^^^^^^^^^^^
Included in a `commit` command to add a new file or change the
content of an existing file. This command has two different means
of specifying the content of the file.
External data format::
The data content for the file was already supplied by a prior
`blob` command. The frontend just needs to connect it.
+
....
'M' SP <mode> SP <dataref> SP <path> LF
....
+
Here `<dataref>` can be either a mark reference (`:<idnum>`)
set by a prior `blob` command, or a full 40-byte SHA-1 of an
existing Git blob object.
Inline data format::
The data content for the file has not been supplied yet.
The frontend wants to supply it as part of this modify
command.
+
....
'M' SP <mode> SP 'inline' SP <path> LF
data
....
+
See below for a detailed description of the `data` command.
In both formats `<mode>` is the type of file entry, specified
in octal. Git only supports the following modes:
* `100644` or `644`: A normal (not-executable) file. The majority
of files in most projects use this mode. If in doubt, this is
what you want.
* `100755` or `755`: A normal, but executable, file.
* `120000`: A symlink, the content of the file will be the link target.
In both formats `<path>` is the complete path of the file to be added
(if not already existing) or modified (if already existing).
A `<path>` string must use UNIX-style directory seperators (forward
slash `/`), may contain any byte other than `LF`, and must not
start with double quote (`"`).
If an `LF` or double quote must be encoded into `<path>` shell-style
quoting should be used, e.g. `"path/with\n and \" in it"`.
The value of `<path>` must be in canoncial form. That is it must not:
* contain an empty directory component (e.g. `foo//bar` is invalid),
* end with a directory seperator (e.g. `foo/` is invalid),
* start with a directory seperator (e.g. `/foo` is invalid),
* contain the special component `.` or `..` (e.g. `foo/./bar` and
`foo/../bar` are invalid).
It is recommended that `<path>` always be encoded using UTF-8.
`filedelete`
^^^^^^^^^^^^
Included in a `commit` command to remove a file from the branch.
If the file removal makes its directory empty, the directory will
be automatically removed too. This cascades up the tree until the
first non-empty directory or the root is reached.
....
'D' SP <path> LF
....
here `<path>` is the complete path of the file to be removed.
See `filemodify` above for a detailed description of `<path>`.
`filedeleteall`
^^^^^^^^^^^^^^^
Included in a `commit` command to remove all files (and also all
directories) from the branch. This command resets the internal
branch structure to have no files in it, allowing the frontend
to subsequently add all interesting files from scratch.
....
'deleteall' LF
....
This command is extremely useful if the frontend does not know
(or does not care to know) what files are currently on the branch,
and therefore cannot generate the proper `filedelete` commands to
update the content.
Issuing a `filedeleteall` followed by the needed `filemodify`
commands to set the correct content will produce the same results
as sending only the needed `filemodify` and `filedelete` commands.
The `filedeleteall` approach may however require fast-import to use slightly
more memory per active branch (less than 1 MiB for even most large
projects); so frontends that can easily obtain only the affected
paths for a commit are encouraged to do so.
`mark`
~~~~~~
Arranges for fast-import to save a reference to the current object, allowing
the frontend to recall this object at a future point in time, without
knowing its SHA-1. Here the current object is the object creation
command the `mark` command appears within. This can be `commit`,
`tag`, and `blob`, but `commit` is the most common usage.
....
'mark' SP ':' <idnum> LF
....
where `<idnum>` is the number assigned by the frontend to this mark.
The value of `<idnum>` is expressed as an ASCII decimal integer.
The value 0 is reserved and cannot be used as
a mark. Only values greater than or equal to 1 may be used as marks.
New marks are created automatically. Existing marks can be moved
to another object simply by reusing the same `<idnum>` in another
`mark` command.
`tag`
~~~~~
Creates an annotated tag referring to a specific commit. To create
lightweight (non-annotated) tags see the `reset` command below.
....
'tag' SP <name> LF
'from' SP <committish> LF
'tagger' SP <name> SP LT <email> GT SP <when> LF
data
LF
....
where `<name>` is the name of the tag to create.
Tag names are automatically prefixed with `refs/tags/` when stored
in Git, so importing the CVS branch symbol `RELENG-1_0-FINAL` would
use just `RELENG-1_0-FINAL` for `<name>`, and fast-import will write the
corresponding ref as `refs/tags/RELENG-1_0-FINAL`.
The value of `<name>` must be a valid refname in Git and therefore
may contain forward slashes. As `LF` is not valid in a Git refname,
no quoting or escaping syntax is supported here.
The `from` command is the same as in the `commit` command; see
above for details.
The `tagger` command uses the same format as `committer` within
`commit`; again see above for details.
The `data` command following `tagger` must supply the annotated tag
message (see below for `data` command syntax). To import an empty
tag message use a 0 length data. Tag messages are free-form and are
not interpreted by Git. Currently they must be encoded in UTF-8,
as fast-import does not permit other encodings to be specified.
Signing annotated tags during import from within fast-import is not
supported. Trying to include your own PGP/GPG signature is not
recommended, as the frontend does not (easily) have access to the
complete set of bytes which normally goes into such a signature.
If signing is required, create lightweight tags from within fast-import with
`reset`, then create the annotated versions of those tags offline
with the standard gitlink:git-tag[1] process.
`reset`
~~~~~~~
Creates (or recreates) the named branch, optionally starting from
a specific revision. The reset command allows a frontend to issue
a new `from` command for an existing branch, or to create a new
branch from an existing commit without creating a new commit.
....
'reset' SP <ref> LF
('from' SP <committish> LF)?
LF
....
For a detailed description of `<ref>` and `<committish>` see above
under `commit` and `from`.
The `reset` command can also be used to create lightweight
(non-annotated) tags. For example:
====
reset refs/tags/938
from :938
====
would create the lightweight tag `refs/tags/938` referring to
whatever commit mark `:938` references.
`blob`
~~~~~~
Requests writing one file revision to the packfile. The revision
is not connected to any commit; this connection must be formed in
a subsequent `commit` command by referencing the blob through an
assigned mark.
....
'blob' LF
mark?
data
....
The mark command is optional here as some frontends have chosen
to generate the Git SHA-1 for the blob on their own, and feed that
directly to `commit`. This is typically more work than its worth
however, as marks are inexpensive to store and easy to use.
`data`
~~~~~~
Supplies raw data (for use as blob/file content, commit messages, or
annotated tag messages) to fast-import. Data can be supplied using an exact
byte count or delimited with a terminating line. Real frontends
intended for production-quality conversions should always use the
exact byte count format, as it is more robust and performs better.
The delimited format is intended primarily for testing fast-import.
Exact byte count format::
The frontend must specify the number of bytes of data.
+
....
'data' SP <count> LF
<raw> LF
....
+
where `<count>` is the exact number of bytes appearing within
`<raw>`. The value of `<count>` is expressed as an ASCII decimal
integer. The `LF` on either side of `<raw>` is not
included in `<count>` and will not be included in the imported data.
Delimited format::
A delimiter string is used to mark the end of the data.
fast-import will compute the length by searching for the delimiter.
This format is primarly useful for testing and is not
recommended for real data.
+
....
'data' SP '<<' <delim> LF
<raw> LF
<delim> LF
....
+
where `<delim>` is the chosen delimiter string. The string `<delim>`
must not appear on a line by itself within `<raw>`, as otherwise
fast-import will think the data ends earlier than it really does. The `LF`
immediately trailing `<raw>` is part of `<raw>`. This is one of
the limitations of the delimited format, it is impossible to supply
a data chunk which does not have an LF as its last byte.
`checkpoint`
~~~~~~~~~~~~
Forces fast-import to close the current packfile, start a new one, and to
save out all current branch refs, tags and marks.
....
'checkpoint' LF
LF
....
Note that fast-import automatically switches packfiles when the current
packfile reaches \--max-pack-size, or 4 GiB, whichever limit is
smaller. During an automatic packfile switch fast-import does not update
the branch refs, tags or marks.
As a `checkpoint` can require a significant amount of CPU time and
disk IO (to compute the overall pack SHA-1 checksum, generate the
corresponding index file, and update the refs) it can easily take
several minutes for a single `checkpoint` command to complete.
Frontends may choose to issue checkpoints during extremely large
and long running imports, or when they need to allow another Git
process access to a branch. However given that a 30 GiB Subversion
repository can be loaded into Git through fast-import in about 3 hours,
explicit checkpointing may not be necessary.
Tips and Tricks
---------------
The following tips and tricks have been collected from various
users of fast-import, and are offered here as suggestions.
Use One Mark Per Commit
~~~~~~~~~~~~~~~~~~~~~~~
When doing a repository conversion, use a unique mark per commit
(`mark :<n>`) and supply the \--export-marks option on the command
line. fast-import will dump a file which lists every mark and the Git
object SHA-1 that corresponds to it. If the frontend can tie
the marks back to the source repository, it is easy to verify the
accuracy and completeness of the import by comparing each Git
commit to the corresponding source revision.
Coming from a system such as Perforce or Subversion this should be
quite simple, as the fast-import mark can also be the Perforce changeset
number or the Subversion revision number.
Freely Skip Around Branches
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Don't bother trying to optimize the frontend to stick to one branch
at a time during an import. Although doing so might be slightly
faster for fast-import, it tends to increase the complexity of the frontend
code considerably.
The branch LRU builtin to fast-import tends to behave very well, and the
cost of activating an inactive branch is so low that bouncing around
between branches has virtually no impact on import performance.
Handling Renames
~~~~~~~~~~~~~~~~
When importing a renamed file or directory, simply delete the old
name(s) and modify the new name(s) during the corresponding commit.
Git performs rename detection after-the-fact, rather than explicitly
during a commit.
Use Tag Fixup Branches
~~~~~~~~~~~~~~~~~~~~~~
Some other SCM systems let the user create a tag from multiple
files which are not from the same commit/changeset. Or to create
tags which are a subset of the files available in the repository.
Importing these tags as-is in Git is impossible without making at
least one commit which ``fixes up'' the files to match the content
of the tag. Use fast-import's `reset` command to reset a dummy branch
outside of your normal branch space to the base commit for the tag,
then commit one or more file fixup commits, and finally tag the
dummy branch.
For example since all normal branches are stored under `refs/heads/`
name the tag fixup branch `TAG_FIXUP`. This way it is impossible for
the fixup branch used by the importer to have namespace conflicts
with real branches imported from the source (the name `TAG_FIXUP`
is not `refs/heads/TAG_FIXUP`).
When committing fixups, consider using `merge` to connect the
commit(s) which are supplying file revisions to the fixup branch.
Doing so will allow tools such as gitlink:git-blame[1] to track
through the real commit history and properly annotate the source
files.
After fast-import terminates the frontend will need to do `rm .git/TAG_FIXUP`
to remove the dummy branch.
Import Now, Repack Later
~~~~~~~~~~~~~~~~~~~~~~~~
As soon as fast-import completes the Git repository is completely valid
and ready for use. Typicallly this takes only a very short time,
even for considerably large projects (100,000+ commits).
However repacking the repository is necessary to improve data
locality and access performance. It can also take hours on extremely
large projects (especially if -f and a large \--window parameter is
used). Since repacking is safe to run alongside readers and writers,
run the repack in the background and let it finish when it finishes.
There is no reason to wait to explore your new Git project!
If you choose to wait for the repack, don't try to run benchmarks
or performance tests until repacking is completed. fast-import outputs
suboptimal packfiles that are simply never seen in real use
situations.
Repacking Historical Data
~~~~~~~~~~~~~~~~~~~~~~~~~
If you are repacking very old imported data (e.g. older than the
last year), consider expending some extra CPU time and supplying
\--window=50 (or higher) when you run gitlink:git-repack[1].
This will take longer, but will also produce a smaller packfile.
You only need to expend the effort once, and everyone using your
project will benefit from the smaller repository.
Packfile Optimization
---------------------
When packing a blob fast-import always attempts to deltify against the last
blob written. Unless specifically arranged for by the frontend,
this will probably not be a prior version of the same file, so the
generated delta will not be the smallest possible. The resulting
packfile will be compressed, but will not be optimal.
Frontends which have efficient access to all revisions of a
single file (for example reading an RCS/CVS ,v file) can choose
to supply all revisions of that file as a sequence of consecutive
`blob` commands. This allows fast-import to deltify the different file
revisions against each other, saving space in the final packfile.
Marks can be used to later identify individual file revisions during
a sequence of `commit` commands.
The packfile(s) created by fast-import do not encourage good disk access
patterns. This is caused by fast-import writing the data in the order
it is received on standard input, while Git typically organizes
data within packfiles to make the most recent (current tip) data
appear before historical data. Git also clusters commits together,
speeding up revision traversal through better cache locality.
For this reason it is strongly recommended that users repack the
repository with `git repack -a -d` after fast-import completes, allowing
Git to reorganize the packfiles for faster data access. If blob
deltas are suboptimal (see above) then also adding the `-f` option
to force recomputation of all deltas can significantly reduce the
final packfile size (30-50% smaller can be quite typical).
Memory Utilization
------------------
There are a number of factors which affect how much memory fast-import
requires to perform an import. Like critical sections of core
Git, fast-import uses its own memory allocators to ammortize any overheads
associated with malloc. In practice fast-import tends to ammoritize any
malloc overheads to 0, due to its use of large block allocations.
per object
~~~~~~~~~~
fast-import maintains an in-memory structure for every object written in
this execution. On a 32 bit system the structure is 32 bytes,
on a 64 bit system the structure is 40 bytes (due to the larger
pointer sizes). Objects in the table are not deallocated until
fast-import terminates. Importing 2 million objects on a 32 bit system
will require approximately 64 MiB of memory.
The object table is actually a hashtable keyed on the object name
(the unique SHA-1). This storage configuration allows fast-import to reuse
an existing or already written object and avoid writing duplicates
to the output packfile. Duplicate blobs are surprisingly common
in an import, typically due to branch merges in the source.
per mark
~~~~~~~~
Marks are stored in a sparse array, using 1 pointer (4 bytes or 8
bytes, depending on pointer size) per mark. Although the array
is sparse, frontends are still strongly encouraged to use marks
between 1 and n, where n is the total number of marks required for
this import.
per branch
~~~~~~~~~~
Branches are classified as active and inactive. The memory usage
of the two classes is significantly different.
Inactive branches are stored in a structure which uses 96 or 120
bytes (32 bit or 64 bit systems, respectively), plus the length of
the branch name (typically under 200 bytes), per branch. fast-import will
easily handle as many as 10,000 inactive branches in under 2 MiB
of memory.
Active branches have the same overhead as inactive branches, but
also contain copies of every tree that has been recently modified on
that branch. If subtree `include` has not been modified since the
branch became active, its contents will not be loaded into memory,
but if subtree `src` has been modified by a commit since the branch
became active, then its contents will be loaded in memory.
As active branches store metadata about the files contained on that
branch, their in-memory storage size can grow to a considerable size
(see below).
fast-import automatically moves active branches to inactive status based on
a simple least-recently-used algorithm. The LRU chain is updated on
each `commit` command. The maximum number of active branches can be
increased or decreased on the command line with \--active-branches=.
per active tree
~~~~~~~~~~~~~~~
Trees (aka directories) use just 12 bytes of memory on top of the
memory required for their entries (see ``per active file'' below).
The cost of a tree is virtually 0, as its overhead ammortizes out
over the individual file entries.
per active file entry
~~~~~~~~~~~~~~~~~~~~~
Files (and pointers to subtrees) within active trees require 52 or 64
bytes (32/64 bit platforms) per entry. To conserve space, file and
tree names are pooled in a common string table, allowing the filename
``Makefile'' to use just 16 bytes (after including the string header
overhead) no matter how many times it occurs within the project.
The active branch LRU, when coupled with the filename string pool
and lazy loading of subtrees, allows fast-import to efficiently import
projects with 2,000+ branches and 45,114+ files in a very limited
memory footprint (less than 2.7 MiB per active branch).
Author
------
Written by Shawn O. Pearce <spearce@spearce.org>.
Documentation
--------------
Documentation by Shawn O. Pearce <spearce@spearce.org>.
GIT
---
Part of the gitlink:git[7] suite

View File

@@ -20,6 +20,14 @@ The ref names and their object names of fetched refs are stored
in `.git/FETCH_HEAD`. This information is left for a later merge
operation done by "git merge".
When <refspec> stores the fetched result in tracking branches,
the tags that point at these branches are automatically
followed. This is done by first fetching from the remote using
the given <refspec>s, and if the repository has objects that are
pointed by remote tags that it does not yet have, then fetch
those missing tags. If the other end has tags that point at
branches you are not interested in, you will not get them.
OPTIONS
-------

View File

@@ -66,7 +66,7 @@ keys.
For all objects, the following names can be used:
refname::
The name of the ref (the part after $GIT_DIR/refs/).
The name of the ref (the part after $GIT_DIR/).
objecttype::
The type of the object (`blob`, `tree`, `commit`, `tag`).

View File

@@ -62,6 +62,10 @@ The optional configuration variable 'gc.rerereunresolved' indicates
how long records of conflicted merge you have not resolved are
kept. This defaults to 15 days.
The optional configuration variable 'gc.packrefs' determines if
`git gc` runs `git-pack-refs`. Without the configuration, `git-pack-refs`
is not run in bare repositories by default, to allow older dumb-transport
clients fetch from the repository, but this will change in the future.
See Also
--------

View File

@@ -16,7 +16,7 @@ Shows the commit logs.
The command takes options applicable to the gitlink:git-rev-list[1]
command to control what is shown and how, and options applicable to
the gitlink:git-diff-tree[1] commands to control how the change
the gitlink:git-diff-tree[1] commands to control how the changes
each commit introduces are shown.
This manual page describes only the most frequently used options.

View File

@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git-merge' [-n] [--no-commit] [--squash] [-s <strategy>]...
-m=<msg> <remote> <remote>...
[-m <msg>] <remote> <remote>...
DESCRIPTION
-----------

View File

@@ -33,6 +33,60 @@ include::urls.txt[]
include::merge-strategies.txt[]
DEFAULT BEHAVIOUR
-----------------
Often people use `git pull` without giving any parameter.
Traditionally, this has been equivalent to saying `git pull
origin`. However, when configuration `branch.<name>.remote` is
present while on branch `<name>`, that value is used instead of
`origin`.
In order to determine what URL to use to fetch from, the value
of the configuration `remote.<origin>.url` is consulted
and if there is not any such variable, the value on `URL: ` line
in `$GIT_DIR/remotes/<origin>` file is used.
In order to determine what remote branches to fetch (and
optionally store in the tracking branches) when the command is
run without any refspec parameters on the command line, values
of the configuration variable `remote.<origin>.fetch` are
consulted, and if there aren't any, `$GIT_DIR/remotes/<origin>`
file is consulted and its `Pull: ` lines are used.
In addition to the refspec formats described in the OPTIONS
section, you can have a globbing refspec that looks like this:
------------
refs/heads/*:refs/remotes/origin/*
------------
A globbing refspec must have a non-empty RHS (i.e. must store
what were fetched in tracking branches), and its LHS and RHS
must end with `/*`. The above specifies that all remote
branches are tracked using tracking branches in
`refs/remotes/origin/` hierarchy under the same name.
The rule to determine which remote branch to merge after
fetching is a bit involved, in order not to break backward
compatibility.
If explicit refspecs were given on the command
line of `git pull`, they are all merged.
When no refspec was given on the command line, then `git pull`
uses the refspec from the configuration or
`$GIT_DIR/remotes/<origin>`. In such cases, the following
rules apply:
. If `branch.<name>.merge` configuration for the current
branch `<name>` exists, that is the name of the branch at the
remote site that is merged.
. If the refspec is a globbing one, nothing is merged.
. Otherwise the remote branch of the first refspec is merged.
EXAMPLES
--------

View File

@@ -7,7 +7,7 @@ git-rebase - Forward-port local commits to the updated upstream head
SYNOPSIS
--------
'git-rebase' [-v] [--merge] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' [-v] [--merge] [-C<n>] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
@@ -114,6 +114,27 @@ would result in:
This is useful when topicB does not depend on topicA.
A range of commits could also be removed with rebase. If we have
the following situation:
------------
E---F---G---H---I---J topicA
------------
then the command
git-rebase --onto topicA~5 topicA~2 topicA
would result in the removal of commits F and G:
------------
E---H'---I'---J' topicA
------------
This is useful if F and G were flawed in some way, or should not be
part of topicA. Note that the argument to --onto and the <upstream>
parameter can be any valid commit-ish.
In case of conflict, git-rebase will stop at the first problematic commit
and leave conflict markers in the tree. You can use git diff to locate
the markers (<<<<<<) and make edits to resolve the conflict. For each
@@ -141,10 +162,12 @@ OPTIONS
<newbase>::
Starting point at which to create the new commits. If the
--onto option is not specified, the starting point is
<upstream>.
<upstream>. May be any valid commit, and not just an
existing branch name.
<upstream>::
Upstream branch to compare against.
Upstream branch to compare against. May be any valid commit,
not just an existing branch name.
<branch>::
Working branch; defaults to HEAD.
@@ -173,6 +196,12 @@ OPTIONS
-v, \--verbose::
Display a diffstat of what changed upstream since the last rebase.
-C<n>::
Ensure at least <n> lines of surrounding context match before
and after each change. When fewer lines of surrounding
context exist they all must match. By default no context is
ever ignored.
include::merge-strategies.txt[]
NOTES

View File

@@ -8,13 +8,18 @@ git-reflog - Manage reflog information
SYNOPSIS
--------
[verse]
'git-reflog' expire [--dry-run] [--stale-fix]
[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
'git reflog' <subcommand> <options>
DESCRIPTION
-----------
The command takes various subcommands, and different options
depending on the subcommand:
[verse]
git reflog expire [--dry-run] [--stale-fix]
[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
git reflog [show] [log-options]
Reflog is a mechanism to record when the tip of branches are
updated. This command is to manage the information recorded in it.
@@ -25,6 +30,10 @@ Entries older than `expire` time, or entries older than
tip, are removed from the reflog. This is typically not used
directly by the end users -- instead, see gitlink:git-gc[1].
The subcommand "show" (which is also the default, in the absense of any
subcommands) will take all the normal log options, and show the log of
the current branch. It is basically an alias for 'git log -g --abbrev-commit
--pretty=oneline', see gitlink:git-log[1].
OPTIONS

View File

@@ -12,19 +12,39 @@ SYNOPSIS
'git-remote'
'git-remote' add <name> <url>
'git-remote' show <name>
'git-remote' prune <name>
DESCRIPTION
-----------
Manage the set of repositories ("remotes") whose branches you track.
With no arguments, shows a list of existing remotes.
In the second form, adds a remote named <name> for the repository at
COMMANDS
--------
With no arguments, shows a list of existing remotes. Several
subcommands are available to perform operations on the remotes.
'add'::
Adds a remote named <name> for the repository at
<url>. The command `git fetch <name>` can then be used to create and
update remote-tracking branches <name>/<branch>.
In the third form, gives some information about the remote <name>.
'show'::
Gives some information about the remote <name>.
'prune'::
Deletes all stale tracking branches under <name>.
These stale branches have already been removed from the remote repository
referenced by <name>, but are still locally available in "remotes/<name>".
DISCUSSION
----------
The remote configuration is achieved using the `remote.origin.url` and
`remote.origin.fetch` configuration variables. (See

View File

@@ -30,9 +30,9 @@ OPTIONS
Instead of incrementally packing the unpacked objects,
pack everything available into a single pack.
Especially useful when packing a repository that is used
for a private development and there no need to worry
about people fetching via dumb protocols from it. Use
with '-d'.
for private development and there is no need to worry
about people fetching via dumb file transfer protocols
from it. Use with '-d'.
-d::
After packing, if the newly created packs make some

View File

@@ -12,7 +12,7 @@ SYNOPSIS
DESCRIPTION
-----------
DEPRECATED. Use `git-merge` instead.
DEPRECATED and will be removed in 1.5.1. Use `git-merge` instead.
Given two commits and a merge message, merge the <merged> commit
into <current> commit, with the commit log message <message>.

View File

@@ -160,6 +160,10 @@ blobs contained in a commit.
immediately following a ref name and the ref must have an existing
log ($GIT_DIR/logs/<ref>).
* You can use the '@' construct with an empty ref part to get at a
reflog of the current branch. For example, if you are on the
branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
* 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

@@ -3,7 +3,7 @@ git-send-pack(1)
NAME
----
git-send-pack - Push objects over git protocol to another reposiotory
git-send-pack - Push objects over git protocol to another repository
SYNOPSIS

View File

@@ -11,7 +11,7 @@ SYNOPSIS
'git-show-branch' [--all] [--remotes] [--topo-order] [--current]
[--more=<n> | --list | --independent | --merge-base]
[--no-name | --sha1-name] [--topics] [<rev> | <glob>]...
'git-show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] <ref>
'git-show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
DESCRIPTION
-----------
@@ -97,11 +97,13 @@ OPTIONS
will show the revisions given by "git rev-list {caret}master
topic1 topic2"
--reflog[=<n>[,<base>]] <ref>::
--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.
`-g` can be used as a short-hand for this option. 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
are mutually exclusive.

View File

@@ -25,6 +25,9 @@ with \--name-only).
For plain blobs, it shows the plain contents.
The command takes options applicable to the gitlink:git-diff-tree[1] command to
control how the changes the commit introduces are shown.
This manual page describes only the most frequently used options.

View File

@@ -133,7 +133,7 @@ manually joining branches on commit.
'multi-init'::
This command supports git-svnimport-like command-line syntax for
importing repositories that are layed out as recommended by the
importing repositories that are laid out as recommended by the
SVN folks. This is a bit more tolerant than the git-svnimport
command-line syntax and doesn't require the user to figure out
where the repository URL ends and where the repository path

View File

@@ -80,6 +80,137 @@ it in the repository configuration as follows:
[user]
signingkey = <gpg-key-id>
DISCUSSION
----------
On Re-tagging
~~~~~~~~~~~~~
What should you do when you tag a wrong commit and you would
want to re-tag?
If you never pushed anything out, just re-tag it. Use "-f" to
replace the old one. And you're done.
But if you have pushed things out (or others could just read
your repository directly), then others will have already seen
the old tag. In that case you can do one of two things:
. The sane thing.
Just admit you screwed up, and use a different name. Others have
already seen one tag-name, and if you keep the same name, you
may be in the situation that two people both have "version X",
but they actually have 'different' "X"'s. So just call it "X.1"
and be done with it.
. The insane thing.
You really want to call the new version "X" too, 'even though'
others have already seen the old one. So just use "git tag -f"
again, as if you hadn't already published the old one.
However, Git does *not* (and it should not)change tags behind
users back. So if somebody already got the old tag, doing a "git
pull" on your tree shouldn't just make them overwrite the old
one.
If somebody got a release tag from you, you cannot just change
the tag for them by updating your own one. This is a big
security issue, in that people MUST be able to trust their
tag-names. If you really want to do the insane thing, you need
to just fess up to it, and tell people that you messed up. You
can do that by making a very public announcement saying:
------------
Ok, I messed up, and I pushed out an earlier version tagged as X. I
then fixed something, and retagged the *fixed* tree as X again.
If you got the wrong tag, and want the new one, please delete
the old one and fetch the new one by doing:
git tag -d X
git fetch origin tag X
to get my updated tag.
You can test which tag you have by doing
git rev-parse X
which should return 0123456789abcdef.. if you have the new version.
Sorry for inconvenience.
------------
Does this seem a bit complicated? It *should* be. There is no
way that it would be correct to just "fix" it behind peoples
backs. People need to know that their tags might have been
changed.
On Automatic following
~~~~~~~~~~~~~~~~~~~~~~
If you are following somebody else's tree, you are most likely
using tracking branches (`refs/heads/origin` in traditional
layout, or `refs/remotes/origin/master` in the separate-remote
layout). You usually want the tags from the other end.
On the other hand, if you are fetching because you would want a
one-shot merge from somebody else, you typically do not want to
get tags from there. This happens more often for people near
the toplevel but not limited to them. Mere mortals when pulling
from each other do not necessarily want to automatically get
private anchor point tags from the other person.
You would notice "please pull" messages on the mailing list says
repo URL and branch name alone. This is designed to be easily
cut&pasted to "git fetch" command line:
------------
Linus, please pull from
git://git..../proj.git master
to get the following updates...
------------
becomes:
------------
$ git pull git://git..../proj.git master
------------
In such a case, you do not want to automatically follow other's
tags.
One important aspect of git is it is distributed, and being
distributed largely means there is no inherent "upstream" or
"downstream" in the system. On the face of it, the above
example might seem to indicate that the tag namespace is owned
by upper echelon of people and tags only flow downwards, but
that is not the case. It only shows that the usage pattern
determines who are interested in whose tags.
A one-shot pull is a sign that a commit history is now crossing
the boundary between one circle of people (e.g. "people who are
primarily interested in networking part of the kernel") who may
have their own set of tags (e.g. "this is the third release
candidate from the networking group to be proposed for general
consumption with 2.6.21 release") to another circle of people
(e.g. "people who integrate various subsystem improvements").
The latter are usually not interested in the detailed tags used
internally in the former group (that is what "internal" means).
That is why it is desirable not to follow tags automatically in
this case.
It may well be that among networking people, they may want to
exchange the tags internal to their group, but in that workflow
they are most likely tracking with each other's progress by
having tracking branches. Again, the heuristic to automatically
follow such tags is a good thing.
Author
------
Written by Linus Torvalds <torvalds@osdl.org>,

View File

@@ -86,6 +86,14 @@ Foreign SCM interface
series in git back and forth.
- *hg-to-git* (contrib/)
hg-to-git converts a Mercurial repository into a git one, and
preserves the full branch history in the process. hg-to-git can
also be used in an incremental way to keep the git repository
in sync with the master Mercurial repository.
Others
------

View File

@@ -29,6 +29,24 @@ in a coherent way to git enlightenment ;-).
The COMMAND is either a name of a Git command (see below) or an alias
as defined in the configuration file (see gitlink:git-config[1]).
ifdef::stalenotes[]
[NOTE]
============
You are reading the documentation for the latest version of git.
Documentation for older releases are available here:
* link:v1.4.4.4/git.html[documentation for release 1.4.4.4]
* link:v1.3.3/git.html[documentation for release 1.3.3]
* link:v1.2.6/git.html[documentation for release 1.2.6]
* link:v1.0.13/git.html[documentation for release 1.0.13]
============
endif::stalenotes[]
OPTIONS
-------
--version::

View File

@@ -54,7 +54,7 @@ frequently used options.
Limit commits to the ones touching files in the given paths. Note, to
avoid ambiguity wrt. revision names use "--" to separate the paths
from any preceeding options.
from any preceding options.
Examples
--------

View File

@@ -90,9 +90,6 @@ parameter, and is invoked after a commit is made.
This hook is meant primarily for notification, and cannot affect
the outcome of `git-commit`.
The default 'post-commit' hook, when enabled, demonstrates how to
send out a commit notification e-mail.
update
------
@@ -130,6 +127,8 @@ The standard output of this hook is sent to `stderr`, so if you
want to report something to the `git-send-pack` on the other end,
you can simply `echo` your messages.
The default 'update' hook, when enabled, demonstrates how to
send out a notification e-mail.
post-update
-----------

View File

@@ -2,7 +2,7 @@
T="$1"
for h in *.html *.txt howto/*.txt howto/*.html
for h in *.html *.txt howto/*.txt howto/*.html RelNotes-*.txt
do
if test -f "$T/$h" &&
diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"

View File

@@ -133,14 +133,14 @@ info::
in this directory.
info/refs::
This file is to help dumb transports to discover what
refs are available in this repository. Whenever you
create/delete a new branch or a new tag, `git
update-server-info` should be run to keep this file
up-to-date if the repository is published for dumb
transports. The `git-receive-pack` command, which is
run on a remote repository when you `git push` into it,
runs `hooks/update` hook to help you achieve this.
This file helps dumb transports discover what refs are
available in this repository. If the repository is
published for dumb transports, this file should be
regenerated by `git update-server-info` every time a tag
or branch is created or modified. This is normally done
from the `hooks/update` hook, which is run by the
`git-receive-pack` command when you `git push` into the
repository.
info/grafts::
This file records fake commit ancestry information, to

View File

@@ -352,24 +352,23 @@ situation:
------------------------------------------------
$ git status
#
# Added but not yet committed:
# (will commit)
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: closing.txt
#
#
# Changed but not added:
# (use "git add file1 file2" to include for commit)
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: file.txt
#
------------------------------------------------
Since the current state of closing.txt is cached in the index file,
it is listed as "added but not yet committed". Since file.txt has
it is listed as "Changes to be committed". Since file.txt has
changes in the working directory that aren't reflected in the index,
it is marked "changed but not added". At this point, running "git
it is marked "changed but not updated". At this point, running "git
commit" would create a commit that added closing.txt (with its new
contents), but that didn't modify file.txt.

View File

@@ -101,27 +101,27 @@ want to commit together. This can be done in a few different ways:
1) By using 'git add <file_spec>...'
This can be performed multiple times before a commit. Note that this
is not only for adding new files. Even modified files must be
added to the set of changes about to be committed. The "git status"
command gives you a summary of what is included so far for the
next commit. When done you should use the 'git commit' command to
make it real.
This can be performed multiple times before a commit. Note that this
is not only for adding new files. Even modified files must be
added to the set of changes about to be committed. The "git status"
command gives you a summary of what is included so far for the
next commit. When done you should use the 'git commit' command to
make it real.
Note: don't forget to 'add' a file again if you modified it after the
first 'add' and before 'commit'. Otherwise only the previous added
state of that file will be committed. This is because git tracks
content, so what you're really 'add'ing to the commit is the *content*
of the file in the state it is in when you 'add' it.
Note: don't forget to 'add' a file again if you modified it after the
first 'add' and before 'commit'. Otherwise only the previous added
state of that file will be committed. This is because git tracks
content, so what you're really 'add'ing to the commit is the *content*
of the file in the state it is in when you 'add' it.
2) By using 'git commit -a' directly
This is a quick way to automatically 'add' the content from all files
that were modified since the previous commit, and perform the actual
commit without having to separately 'add' them beforehand. This will
not add content from new files i.e. files that were never added before.
Those files still have to be added explicitly before performing a
commit.
This is a quick way to automatically 'add' the content from all files
that were modified since the previous commit, and perform the actual
commit without having to separately 'add' them beforehand. This will
not add content from new files i.e. files that were never added before.
Those files still have to be added explicitly before performing a
commit.
But here's a twist. If you do 'git commit <file1> <file2> ...' then only
the changes belonging to those explicitly specified files will be
@@ -458,9 +458,11 @@ $ git reset --hard HEAD^ # reset your current branch and working
Be careful with that last command: in addition to losing any changes
in the working directory, it will also remove all later commits from
this branch. If this branch is the only branch containing those
commits, they will be lost. (Also, don't use "git reset" on a
publicly-visible branch that other developers pull from, as git will
be confused by history that disappears in this way.)
commits, they will be lost. Also, don't use "git reset" on a
publicly-visible branch that other developers pull from, as it will
force needless merges on other developers to clean up the history.
If you need to undo changes that you have pushed, use gitlink:git-revert[1]
instead.
The git grep command can search for strings in any version of your
project, so

View File

@@ -398,7 +398,7 @@ branch name, but this longer name can also be useful. Most
importantly, it is a globally unique name for this commit: so if you
tell somebody else the object name (for example in email), then you are
guaranteed that name will refer to the same commit in their repository
that you it does in yours (assuming their repository has that commit at
that it does in yours (assuming their repository has that commit at
all).
Understanding history: commits, parents, and reachability
@@ -425,8 +425,8 @@ if commit X is an ancestor of commit Y. Equivalently, you could say
that Y is a descendent of X, or that there is a chain of parents
leading from commit Y to commit X.
Undestanding history: History diagrams
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Understanding history: History diagrams
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We will sometimes represent git history using diagrams like the one
below. Commits are shown as "o", and the links between them with
@@ -617,7 +617,7 @@ the relationships between these snapshots.
Git provides extremely flexible and fast tools for exploring the
history of a project.
We start with one specialized tool which is useful for finding the
We start with one specialized tool that is useful for finding the
commit that introduced a bug into a project.
How to use bisect to find a regression
@@ -806,7 +806,7 @@ display options.
Note that git log starts with the most recent commit and works
backwards through the parents; however, since git history can contain
multiple independant lines of development, the particular order that
multiple independent lines of development, the particular order that
commits are listed in may be somewhat arbitrary.
Generating diffs
@@ -1075,7 +1075,7 @@ $ git commit
-------------------------------------------------
and git will prompt you for a commit message and then create the new
commmit. Check to make sure it looks like what you expected with
commit. Check to make sure it looks like what you expected with
-------------------------------------------------
$ git show
@@ -1492,7 +1492,7 @@ dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5
...
-------------------------------------------------
and watch for output that mentions "dangling commits". You can examine
You can examine
one of those dangling commits with, for example,
------------------------------------------------
@@ -2923,6 +2923,8 @@ Think about how to create a clear chapter dependency graph that will
allow people to get to important topics without necessarily reading
everything in between.
Say something about .gitignore.
Scan Documentation/ for other stuff left out; in particular:
howto's
some of technical/?
@@ -2951,7 +2953,7 @@ Include cross-references to the glossary, where appropriate.
Document shallow clones? See draft 1.5.0 release notes for some
documentation.
Add a sectin on working with other version control systems, including
Add a section on working with other version control systems, including
CVS, Subversion, and just imports of series of release tarballs.
More details on gitweb?

View File

@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
DEF_VER=v1.5.0-rc3.GIT
DEF_VER=v1.5.0.GIT
LF='
'

View File

@@ -195,6 +195,7 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
PROGRAMS = \
git-fetch-pack$X git-fsck$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \
git-fast-import$X \
git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
git-peek-remote$X git-receive-pack$X \
git-send-pack$X git-shell$X \
@@ -217,8 +218,7 @@ BUILT_INS = \
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) \
git-merge-recur$X
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
# Backward compatibility -- to be removed after 1.0
PROGRAMS += git-ssh-pull$X git-ssh-push$X
@@ -626,7 +626,7 @@ LIB_OBJS += $(COMPAT_OBJS)
ALL_CFLAGS += $(BASIC_CFLAGS)
ALL_LDFLAGS += $(BASIC_LDFLAGS)
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
export prefix gitexecdir TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules
@@ -637,6 +637,7 @@ ifneq (,$X)
endif
all::
$(MAKE) -C git-gui all
$(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
$(MAKE) -C templates NOEXECTEMPL='$(NOEXECTEMPL)'
@@ -650,9 +651,6 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
help.o: common-cmds.h
git-merge-recur$X: git-merge-recursive$X
rm -f $@ && ln git-merge-recursive$X $@
$(BUILT_INS): git$X
rm -f $@ && ln git$X $@
@@ -871,6 +869,7 @@ install: all
$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
$(MAKE) -C perl prefix='$(prefix_SQ)' install
$(MAKE) -C git-gui install
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
then \
ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
@@ -904,8 +903,11 @@ dist: git.spec git-archive
@mkdir -p $(GIT_TARNAME)
@cp git.spec $(GIT_TARNAME)
@echo $(GIT_VERSION) > $(GIT_TARNAME)/version
@$(MAKE) -C git-gui TARDIR=../$(GIT_TARNAME)/git-gui dist-version
$(TAR) rf $(GIT_TARNAME).tar \
$(GIT_TARNAME)/git.spec $(GIT_TARNAME)/version
$(GIT_TARNAME)/git.spec \
$(GIT_TARNAME)/version \
$(GIT_TARNAME)/git-gui/version
@rm -rf $(GIT_TARNAME)
gzip -f -9 $(GIT_TARNAME).tar
@@ -946,6 +948,7 @@ clean:
rm -f gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean
$(MAKE) -C perl clean
$(MAKE) -C git-gui clean
$(MAKE) -C templates/ clean
$(MAKE) -C t/ clean
rm -f GIT-VERSION-FILE GIT-CFLAGS
@@ -960,7 +963,7 @@ check-docs::
do \
case "$$v" in \
git-merge-octopus | git-merge-ours | git-merge-recursive | \
git-merge-resolve | git-merge-stupid | git-merge-recur | \
git-merge-resolve | git-merge-stupid | \
git-ssh-pull | git-ssh-push ) continue ;; \
esac ; \
test -f "Documentation/$$v.txt" || \

1
RelNotes Symbolic link
View File

@@ -0,0 +1 @@
Documentation/RelNotes-1.5.0.txt

View File

@@ -12,7 +12,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix)
int i;
nargv = xmalloc(sizeof(char *) * (argc + 2));
nargv[0] = "blame";
nargv[0] = "annotate";
nargv[1] = "-c";
for (i = 1; i < argc; i++) {

View File

@@ -15,9 +15,10 @@
#include "revision.h"
#include "quote.h"
#include "xdiff-interface.h"
#include "cache-tree.h"
static char blame_usage[] =
"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [commit] [--] file\n"
"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
" -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
" -b Show blank SHA-1 for boundary commits (Default: off)\n"
" -l, --long Show long commit SHA1 (Default: off)\n"
@@ -29,6 +30,7 @@ static char blame_usage[] =
" -L n,m Process only line range n,m, counting from 1\n"
" -M, -C Find line movements within and across files\n"
" --incremental Show blame entries as we find them, incrementally\n"
" --contents file Use <file>'s contents as the final image\n"
" -S revs-file Use revisions from revs-file instead of calling git-rev-list\n";
static int longest_file;
@@ -39,6 +41,7 @@ static int max_score_digits;
static int show_root;
static int blank_boundary;
static int incremental;
static int cmd_is_annotate;
#ifndef DEBUG
#define DEBUG 0
@@ -333,9 +336,13 @@ static struct origin *find_origin(struct scoreboard *sb,
diff_tree_setup_paths(paths, &diff_opts);
if (diff_setup_done(&diff_opts) < 0)
die("diff-setup");
diff_tree_sha1(parent->tree->object.sha1,
origin->commit->tree->object.sha1,
"", &diff_opts);
if (is_null_sha1(origin->commit->object.sha1))
do_diff_cache(parent->tree->object.sha1, &diff_opts);
else
diff_tree_sha1(parent->tree->object.sha1,
origin->commit->tree->object.sha1,
"", &diff_opts);
diffcore_std(&diff_opts);
/* It is either one entry that says "modified", or "created",
@@ -402,9 +409,13 @@ static struct origin *find_rename(struct scoreboard *sb,
diff_tree_setup_paths(paths, &diff_opts);
if (diff_setup_done(&diff_opts) < 0)
die("diff-setup");
diff_tree_sha1(parent->tree->object.sha1,
origin->commit->tree->object.sha1,
"", &diff_opts);
if (is_null_sha1(origin->commit->object.sha1))
do_diff_cache(parent->tree->object.sha1, &diff_opts);
else
diff_tree_sha1(parent->tree->object.sha1,
origin->commit->tree->object.sha1,
"", &diff_opts);
diffcore_std(&diff_opts);
for (i = 0; i < diff_queued_diff.nr; i++) {
@@ -550,7 +561,7 @@ static void free_patch(struct patch *p)
}
/*
* Link in a new blame entry to the scorebord. Entries that cover the
* Link in a new blame entry to the scoreboard. Entries that cover the
* same line range have been removed from the scoreboard previously.
*/
static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
@@ -1047,9 +1058,12 @@ static int find_copy_in_parent(struct scoreboard *sb,
(!porigin || strcmp(target->path, porigin->path)))
diff_opts.find_copies_harder = 1;
diff_tree_sha1(parent->tree->object.sha1,
target->commit->tree->object.sha1,
"", &diff_opts);
if (is_null_sha1(target->commit->object.sha1))
do_diff_cache(parent->tree->object.sha1, &diff_opts);
else
diff_tree_sha1(parent->tree->object.sha1,
target->commit->tree->object.sha1,
"", &diff_opts);
if (!diff_opts.find_copies_harder)
diffcore_std(&diff_opts);
@@ -1336,9 +1350,9 @@ static void get_commit_info(struct commit *commit,
tmp += 2;
endp = strchr(tmp, '\n');
if (!endp)
goto error_out;
endp = tmp + strlen(tmp);
len = endp - tmp;
if (len >= sizeof(summary_buf))
if (len >= sizeof(summary_buf) || len == 0)
goto error_out;
memcpy(summary_buf, tmp, len);
summary_buf[len] = 0;
@@ -1392,7 +1406,7 @@ static void found_guilty_entry(struct blame_entry *ent)
/*
* The main loop -- while the scoreboard has lines whose true origin
* is still unknown, pick one brame_entry, and allow its current
* is still unknown, pick one blame_entry, and allow its current
* suspect to pass blames to its parents.
*/
static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
@@ -1541,12 +1555,12 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;
if (suspect->commit->object.flags & UNINTERESTING) {
if (!blank_boundary) {
if (blank_boundary)
memset(hex, ' ', length);
else if (!cmd_is_annotate) {
length--;
putchar('^');
}
else
memset(hex, ' ', length);
}
printf("%.*s", length, hex);
@@ -1910,6 +1924,137 @@ static int git_blame_config(const char *var, const char *value)
return git_default_config(var, value);
}
static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
{
struct commit *commit;
struct origin *origin;
unsigned char head_sha1[20];
char *buf;
const char *ident;
int fd;
time_t now;
unsigned long fin_size;
int size, len;
struct cache_entry *ce;
unsigned mode;
if (get_sha1("HEAD", head_sha1))
die("No such ref: HEAD");
time(&now);
commit = xcalloc(1, sizeof(*commit));
commit->parents = xcalloc(1, sizeof(*commit->parents));
commit->parents->item = lookup_commit_reference(head_sha1);
commit->object.parsed = 1;
commit->date = now;
commit->object.type = OBJ_COMMIT;
origin = make_origin(commit, path);
if (!contents_from || strcmp("-", contents_from)) {
struct stat st;
const char *read_from;
if (contents_from) {
if (stat(contents_from, &st) < 0)
die("Cannot stat %s", contents_from);
read_from = contents_from;
}
else {
if (lstat(path, &st) < 0)
die("Cannot lstat %s", path);
read_from = path;
}
fin_size = st.st_size;
buf = xmalloc(fin_size+1);
mode = canon_mode(st.st_mode);
switch (st.st_mode & S_IFMT) {
case S_IFREG:
fd = open(read_from, O_RDONLY);
if (fd < 0)
die("cannot open %s", read_from);
if (read_in_full(fd, buf, fin_size) != fin_size)
die("cannot read %s", read_from);
break;
case S_IFLNK:
if (readlink(read_from, buf, fin_size+1) != fin_size)
die("cannot readlink %s", read_from);
break;
default:
die("unsupported file type %s", read_from);
}
}
else {
/* Reading from stdin */
contents_from = "standard input";
buf = NULL;
fin_size = 0;
mode = 0;
while (1) {
ssize_t cnt = 8192;
buf = xrealloc(buf, fin_size + cnt);
cnt = xread(0, buf + fin_size, cnt);
if (cnt < 0)
die("read error %s from stdin",
strerror(errno));
if (!cnt)
break;
fin_size += cnt;
}
buf = xrealloc(buf, fin_size + 1);
}
buf[fin_size] = 0;
origin->file.ptr = buf;
origin->file.size = fin_size;
pretend_sha1_file(buf, fin_size, blob_type, origin->blob_sha1);
commit->util = origin;
/*
* Read the current index, replace the path entry with
* origin->blob_sha1 without mucking with its mode or type
* bits; we are not going to write this index out -- we just
* want to run "diff-index --cached".
*/
discard_cache();
read_cache();
len = strlen(path);
if (!mode) {
int pos = cache_name_pos(path, len);
if (0 <= pos)
mode = ntohl(active_cache[pos]->ce_mode);
else
/* Let's not bother reading from HEAD tree */
mode = S_IFREG | 0644;
}
size = cache_entry_size(len);
ce = xcalloc(1, size);
hashcpy(ce->sha1, origin->blob_sha1);
memcpy(ce->name, path, len);
ce->ce_flags = create_ce_flags(len, 0);
ce->ce_mode = create_ce_mode(mode);
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
/*
* We are not going to write this out, so this does not matter
* right now, but someday we might optimize diff-index --cached
* with cache-tree information.
*/
cache_tree_invalidate_path(active_cache_tree, path);
commit->buffer = xmalloc(400);
ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
sprintf(commit->buffer,
"tree 0000000000000000000000000000000000000000\n"
"parent %s\n"
"author %s\n"
"committer %s\n\n"
"Version of %s from %s\n",
sha1_to_hex(head_sha1),
ident, ident, path, contents_from ? contents_from : path);
return commit;
}
int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -1924,6 +2069,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
const char *final_commit_name = NULL;
char type[10];
const char *bottomtop = NULL;
const char *contents_from = NULL;
cmd_is_annotate = !strcmp(argv[0], "annotate");
git_config(git_blame_config);
save_commit_buffer = 0;
@@ -1968,6 +2116,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
die("More than one '-L n,m' option given");
bottomtop = arg;
}
else if (!strcmp("--contents", arg)) {
if (++i >= argc)
usage(blame_usage);
contents_from = argv[i];
}
else if (!strcmp("--incremental", arg))
incremental = 1;
else if (!strcmp("--score-debug", arg))
@@ -2001,7 +2154,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
/*
* We have collected options unknown to us in argv[1..unk]
* which are to be passed to revision machinery if we are
* going to do the "bottom" procesing.
* going to do the "bottom" processing.
*
* The remaining are:
*
@@ -2087,7 +2240,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
argv[unk] = NULL;
init_revisions(&revs, NULL);
setup_revisions(unk, argv, &revs, "HEAD");
setup_revisions(unk, argv, &revs, NULL);
memset(&sb, 0, sizeof(sb));
/*
@@ -2114,16 +2267,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
if (!sb.final) {
/*
* "--not A B -- path" without anything positive;
* default to HEAD.
* do not default to HEAD, but use the working tree
* or "--contents".
*/
unsigned char head_sha1[20];
final_commit_name = "HEAD";
if (get_sha1(final_commit_name, head_sha1))
die("No such ref: HEAD");
sb.final = lookup_commit_reference(head_sha1);
add_pending_object(&revs, &(sb.final->object), "HEAD");
sb.final = fake_working_tree_commit(path, contents_from);
add_pending_object(&revs, &(sb.final->object), ":");
}
else if (contents_from)
die("Cannot use --contents with final commit object name");
/*
* If we have bottom, this will mark the ancestors of the
@@ -2132,11 +2283,22 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
*/
prepare_revision_walk(&revs);
o = get_origin(&sb, sb.final, path);
if (fill_blob_sha1(o))
die("no such path %s in %s", path, final_commit_name);
if (is_null_sha1(sb.final->object.sha1)) {
char *buf;
o = sb.final->util;
buf = xmalloc(o->file.size + 1);
memcpy(buf, o->file.ptr, o->file.size + 1);
sb.final_buf = buf;
sb.final_buf_size = o->file.size;
}
else {
o = get_origin(&sb, sb.final, path);
if (fill_blob_sha1(o))
die("no such path %s in %s", path, final_commit_name);
sb.final_buf = read_sha1_file(o->blob_sha1, type, &sb.final_buf_size);
sb.final_buf = read_sha1_file(o->blob_sha1, type,
&sb.final_buf_size);
}
num_read_blob++;
lno = prepare_lines(&sb);

View File

@@ -316,6 +316,7 @@ static void create_branch(const char *name, const char *start_name,
struct commit *commit;
unsigned char sha1[20];
char ref[PATH_MAX], msg[PATH_MAX + 20];
int forcing = 0;
snprintf(ref, sizeof ref, "refs/heads/%s", name);
if (check_ref_format(ref))
@@ -326,6 +327,7 @@ static void create_branch(const char *name, const char *start_name,
die("A branch named '%s' already exists.", name);
else if (!is_bare_repository() && !strcmp(head, name))
die("Cannot force update the current branch.");
forcing = 1;
}
if (start_sha1)
@@ -342,11 +344,15 @@ static void create_branch(const char *name, const char *start_name,
if (!lock)
die("Failed to lock ref for update: %s.", strerror(errno));
if (reflog) {
if (reflog)
log_all_ref_updates = 1;
if (forcing)
snprintf(msg, sizeof msg, "branch: Reset from %s",
start_name);
else
snprintf(msg, sizeof msg, "branch: Created from %s",
start_name);
}
if (write_ref_sha1(lock, sha1, msg) < 0)
die("Failed to write ref: %s.", strerror(errno));
@@ -358,7 +364,7 @@ static void rename_branch(const char *oldname, const char *newname, int force)
unsigned char sha1[20];
if (!oldname)
die("cannot rename the curren branch while not on any.");
die("cannot rename the current branch while not on any.");
if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
die("Old branchname too long");

View File

@@ -135,7 +135,7 @@ static const char *find_next(const char *cp)
while (*cp) {
if (*cp == '%') {
/* %( is the start of an atom;
* %% is a quoteed per-cent.
* %% is a quoted per-cent.
*/
if (cp[1] == '(')
return cp;

View File

@@ -117,7 +117,7 @@ static void check_unreachable_object(struct object *obj)
/*
* "!used" means that nothing at all points to it, including
* other unreacahble objects. In other words, it's the "tip"
* other unreachable objects. In other words, it's the "tip"
* of some set of unreachable objects, usually a commit that
* got dropped.
*
@@ -477,6 +477,12 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
return 0;
}
static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
{
for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);
return 0;
}
static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *obj;
@@ -495,14 +501,13 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
obj->used = 1;
mark_reachable(obj, REACHABLE);
for_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);
return 0;
}
static void get_default_heads(void)
{
for_each_ref(fsck_handle_ref, NULL);
for_each_reflog(fsck_handle_reflog, NULL);
/*
* Not having any default heads isn't really fatal, but

View File

@@ -11,6 +11,7 @@
#include "log-tree.h"
#include "builtin.h"
#include "tag.h"
#include "reflog-walk.h"
static int default_show_root = 1;
@@ -181,6 +182,37 @@ int cmd_show(int argc, const char **argv, const char *prefix)
return ret;
}
/*
* This is equivalent to "git log -g --abbrev-commit --pretty=oneline"
*/
int cmd_log_reflog(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
git_config(git_log_config);
init_revisions(&rev, prefix);
init_reflog_walk(&rev.reflog_info);
rev.abbrev_commit = 1;
rev.verbose_header = 1;
cmd_log_init(argc, argv, prefix, &rev);
/*
* This means that we override whatever commit format the user gave
* on the cmd line. Sad, but cmd_log_init() currently doesn't
* allow us to set a different default.
*/
rev.commit_format = CMIT_FMT_ONELINE;
rev.always_show_header = 1;
/*
* We get called through "git reflog", so unlike the other log
* routines, we need to set up our pager manually..
*/
setup_pager();
return cmd_log_walk(&rev);
}
int cmd_log(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;

View File

@@ -323,7 +323,7 @@ static const char ls_files_usage[] =
int cmd_ls_files(int argc, const char **argv, const char *prefix)
{
int i;
int exc_given = 0;
int exc_given = 0, require_work_tree = 0;
struct dir_struct dir;
memset(&dir, 0, sizeof(dir));
@@ -363,14 +363,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
show_modified = 1;
require_work_tree = 1;
continue;
}
if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
show_others = 1;
require_work_tree = 1;
continue;
}
if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
dir.show_ignored = 1;
require_work_tree = 1;
continue;
}
if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
@@ -379,6 +382,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
show_killed = 1;
require_work_tree = 1;
continue;
}
if (!strcmp(arg, "--directory")) {
@@ -447,6 +451,10 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
break;
}
if (require_work_tree &&
(is_bare_repository() || is_inside_git_dir()))
die("This operation must be run in a work tree");
pathspec = get_pathspec(prefix, argv + i);
/* Verify that the pathspec matches the prefix */

View File

@@ -54,38 +54,84 @@ static void expand_refspecs(void)
for_each_ref(expand_one_ref, NULL);
}
struct wildcard_cb {
const char *from_prefix;
int from_prefix_len;
const char *to_prefix;
int to_prefix_len;
int force;
};
static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
{
struct wildcard_cb *cb = cb_data;
int len = strlen(ref);
char *expanded, *newref;
if (len < cb->from_prefix_len ||
memcmp(cb->from_prefix, ref, cb->from_prefix_len))
return 0;
expanded = xmalloc(len * 2 + cb->force +
(cb->to_prefix_len - cb->from_prefix_len) + 2);
newref = expanded + cb->force;
if (cb->force)
expanded[0] = '+';
memcpy(newref, ref, len);
newref[len] = ':';
memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
strcpy(newref + len + 1 + cb->to_prefix_len,
ref + cb->from_prefix_len);
add_refspec(expanded);
return 0;
}
static int wildcard_ref(const char *ref)
{
int len;
const char *colon;
struct wildcard_cb cb;
memset(&cb, 0, sizeof(cb));
if (ref[0] == '+') {
cb.force = 1;
ref++;
}
len = strlen(ref);
colon = strchr(ref, ':');
if (! (colon && ref < colon &&
colon[-2] == '/' && colon[-1] == '*' &&
/* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
7 <= len &&
ref[len-2] == '/' && ref[len-1] == '*') )
return 0 ;
cb.from_prefix = ref;
cb.from_prefix_len = colon - ref - 1;
cb.to_prefix = colon + 1;
cb.to_prefix_len = len - (colon - ref) - 2;
for_each_ref(expand_wildcard_ref, &cb);
return 1;
}
static void set_refspecs(const char **refs, int nr)
{
if (nr) {
int pass;
for (pass = 0; pass < 2; pass++) {
/* pass 0 counts and allocates, pass 1 fills */
int i, cnt;
for (i = cnt = 0; i < nr; i++) {
if (!strcmp("tag", refs[i])) {
int len;
char *tag;
if (nr <= ++i)
die("tag <tag> shorthand without <tag>");
if (pass) {
len = strlen(refs[i]) + 11;
tag = xmalloc(len);
strcpy(tag, "refs/tags/");
strcat(tag, refs[i]);
refspec[cnt] = tag;
}
cnt++;
continue;
}
if (pass)
refspec[cnt] = refs[i];
cnt++;
}
if (!pass) {
size_t bytes = cnt * sizeof(char *);
refspec_nr = cnt;
refspec = xrealloc(refspec, bytes);
int i;
for (i = 0; i < nr; i++) {
const char *ref = refs[i];
if (!strcmp("tag", ref)) {
char *tag;
int len;
if (nr <= ++i)
die("tag shorthand without <tag>");
len = strlen(refs[i]) + 11;
tag = xmalloc(len);
strcpy(tag, "refs/tags/");
strcat(tag, refs[i]);
ref = tag;
}
else if (wildcard_ref(ref))
continue;
add_refspec(ref);
}
}
expand_refspecs();
@@ -129,8 +175,10 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
else
error("more than %d URL's specified, ignoring the rest", MAX_URI);
}
else if (is_refspec && !has_explicit_refspec)
add_refspec(xstrdup(s));
else if (is_refspec && !has_explicit_refspec) {
if (!wildcard_ref(s))
add_refspec(xstrdup(s));
}
}
fclose(f);
if (!n)
@@ -156,8 +204,10 @@ static int get_remote_config(const char* key, const char* value)
error("more than %d URL's specified, ignoring the rest", MAX_URI);
}
else if (config_get_refspecs &&
!strcmp(key + 7 + config_repo_len, ".push"))
add_refspec(xstrdup(value));
!strcmp(key + 7 + config_repo_len, ".push")) {
if (!wildcard_ref(value))
add_refspec(xstrdup(value));
}
else if (config_get_receivepack &&
!strcmp(key + 7 + config_repo_len, ".receivepack")) {
if (!receivepack) {

View File

@@ -13,7 +13,7 @@
*/
static const char reflog_expire_usage[] =
"git-reflog expire [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
"git-reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
static unsigned long default_reflog_expire;
static unsigned long default_reflog_expire_unreachable;
@@ -245,14 +245,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
char *log_file, *newlog_path = NULL;
int status = 0;
if (strncmp(ref, "refs/", 5))
return error("not a ref '%s'", ref);
memset(&cb, 0, sizeof(cb));
/* we take the lock for the ref itself to prevent it from
* getting updated.
*/
lock = lock_ref_sha1(ref + 5, sha1);
lock = lock_any_ref_for_update(ref, sha1);
if (!lock)
return error("cannot lock ref '%s'", ref);
log_file = xstrdup(git_path("logs/%s", ref));
@@ -359,7 +356,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
}
if (do_all)
status |= for_each_ref(expire_reflog, &cb);
status |= for_each_reflog(expire_reflog, &cb);
while (i < argc) {
const char *ref = argv[i++];
unsigned char sha1[20];
@@ -381,10 +378,16 @@ static const char reflog_usage[] =
int cmd_reflog(int argc, const char **argv, const char *prefix)
{
if (argc < 2)
usage(reflog_usage);
else if (!strcmp(argv[1], "expire"))
/* With no command, we default to showing it. */
if (argc < 2 || *argv[1] == '-')
return cmd_log_reflog(argc, argv, prefix);
if (!strcmp(argv[1], "show"))
return cmd_log_reflog(argc - 1, argv + 1, prefix);
if (!strcmp(argv[1], "expire"))
return cmd_reflog_expire(argc - 1, argv + 1, prefix);
else
usage(reflog_usage);
/* Not a recognized reflog command..*/
usage(reflog_usage);
}

View File

@@ -347,6 +347,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
printf("%s/.git\n", cwd);
continue;
}
if (!strcmp(arg, "--is-inside-git-dir")) {
printf("%s\n", is_inside_git_dir() ? "true"
: "false");
continue;
}
if (!strncmp(arg, "--since=", 8)) {
show_datestring("--max-age=", arg+8);
continue;

View File

@@ -690,7 +690,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
fake_av[0] = "HEAD";
const char *refname;
refname = resolve_ref("HEAD", sha1, 1, NULL);
fake_av[0] = xstrdup(refname);
fake_av[1] = NULL;
av = fake_av;
ac = 1;

View File

@@ -40,6 +40,7 @@ extern int cmd_grep(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);
extern int cmd_init_db(int argc, const char **argv, const char *prefix);
extern int cmd_log(int argc, const char **argv, const char *prefix);
extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);

View File

@@ -258,6 +258,7 @@ extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, uns
extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, const char *, unsigned char *);
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
@@ -302,6 +303,7 @@ extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
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 create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
extern int validate_headref(const char *ref);
@@ -322,6 +324,7 @@ unsigned long approxidate(const char *);
extern const char *git_author_info(int);
extern const char *git_committer_info(int);
extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
struct checkout {
const char *base_dir;

View File

@@ -482,11 +482,11 @@ static int make_hunks(struct sline *sline, unsigned long cnt,
return has_interesting;
}
static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, int n)
static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, int n, unsigned long null_context)
{
l0 = sline[l0].p_lno[n];
l1 = sline[l1].p_lno[n];
printf(" -%lu,%lu", l0, l1-l0);
printf(" -%lu,%lu", l0, l1-l0-null_context);
}
static int hunk_comment_line(const char *bol)
@@ -519,6 +519,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
unsigned long hunk_end;
unsigned long rlines;
const char *hunk_comment = NULL;
unsigned long null_context = 0;
while (lno <= cnt && !(sline[lno].flag & mark)) {
if (hunk_comment_line(sline[lno].bol))
@@ -535,10 +536,28 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
rlines = hunk_end - lno;
if (cnt < hunk_end)
rlines--; /* pointing at the last delete hunk */
if (!context) {
/*
* Even when running with --unified=0, all
* lines in the hunk needs to be processed in
* the loop below in order to show the
* deletion recorded in lost_head. However,
* we do not want to show the resulting line
* with all blank context markers in such a
* case. Compensate.
*/
unsigned long j;
for (j = lno; j < hunk_end; j++)
if (!(sline[j].flag & (mark-1)))
null_context++;
rlines -= null_context;
}
fputs(c_frag, stdout);
for (i = 0; i <= num_parent; i++) putchar(combine_marker);
for (i = 0; i < num_parent; i++)
show_parent_lno(sline, lno, hunk_end, i);
show_parent_lno(sline, lno, hunk_end, i, null_context);
printf(" +%lu,%lu ", lno+1, rlines);
for (i = 0; i <= num_parent; i++) putchar(combine_marker);
@@ -578,8 +597,15 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
if (cnt < lno)
break;
p_mask = 1;
if (!(sl->flag & (mark-1)))
if (!(sl->flag & (mark-1))) {
/*
* This sline was here to hang the
* lost lines in front of it.
*/
if (!context)
continue;
fputs(c_plain, stdout);
}
else
fputs(c_new, stdout);
for (j = 0; j < num_parent; j++) {

View File

@@ -47,7 +47,8 @@ enum cmit_fmt get_commit_format(const char *arg)
if (*arg == '=')
arg++;
for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len))
if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
!strncmp(arg, cmt_fmts[i].n, strlen(arg)))
return cmt_fmts[i].v;
}

View File

@@ -898,7 +898,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
if (buf[i] != old_name[j++])
break;
}
if (buf[i] == ']') {
if (buf[i] == ']' && old_name[j] == 0) {
/* old_name matches */
ret++;
store.baselen = strlen(new_name);

View File

@@ -3,7 +3,17 @@
use Gtk2 -init;
use Gtk2::SimpleList;
my $fn = shift or die "require filename to blame";
my $hash;
my $fn;
if ( @ARGV == 1 ) {
$hash = "HEAD";
$fn = shift;
} elsif ( @ARGV == 2 ) {
$hash = shift;
$fn = shift;
} else {
die "Usage blameview [<rev>] <filename>";
}
Gtk2::Rc->parse_string(<<'EOS');
style "treeview_style"
@@ -15,11 +25,12 @@ EOS
my $window = Gtk2::Window->new('toplevel');
$window->signal_connect(destroy => sub { Gtk2->main_quit });
my $vpan = Gtk2::VPaned->new();
$window->add($vpan);
my $scrolled_window = Gtk2::ScrolledWindow->new;
$window->add($scrolled_window);
$vpan->pack1($scrolled_window, 1, 1);
my $fileview = Gtk2::SimpleList->new(
'Commit' => 'text',
'CommitInfo' => 'text',
'FileLine' => 'text',
'Data' => 'text'
);
@@ -27,17 +38,43 @@ $scrolled_window->add($fileview);
$fileview->get_column(0)->set_spacing(0);
$fileview->set_size_request(1024, 768);
$fileview->set_rules_hint(1);
$fileview->signal_connect (row_activated => sub {
my ($sl, $path, $column) = @_;
my $row_ref = $sl->get_row_data_from_path ($path);
system("blameview @$row_ref[0] $fn &");
});
my $commitwindow = Gtk2::ScrolledWindow->new();
$commitwindow->set_policy ('GTK_POLICY_AUTOMATIC','GTK_POLICY_AUTOMATIC');
$vpan->pack2($commitwindow, 1, 1);
my $commit_text = Gtk2::TextView->new();
my $commit_buffer = Gtk2::TextBuffer->new();
$commit_text->set_buffer($commit_buffer);
$commitwindow->add($commit_text);
$fileview->signal_connect (cursor_changed => sub {
my ($sl) = @_;
my ($path, $focus_column) = $sl->get_cursor();
my $row_ref = $sl->get_row_data_from_path ($path);
my $c_fh;
open($c_fh, '-|', "git cat-file commit @$row_ref[0]")
or die "unable to find commit @$row_ref[0]";
my @buffer = <$c_fh>;
$commit_buffer->set_text("@buffer");
close($c_fh);
});
my $fh;
open($fh, '-|', "git cat-file blob HEAD:$fn")
open($fh, '-|', "git cat-file blob $hash:$fn")
or die "unable to open $fn: $!";
while(<$fh>) {
chomp;
$fileview->{data}->[$.] = ['HEAD', '?', "$fn:$.", $_];
$fileview->{data}->[$.] = ['HEAD', "$fn:$.", $_];
}
my $blame;
open($blame, '-|', qw(git blame --incremental --), $fn)
open($blame, '-|', qw(git blame --incremental --), $fn, $hash)
or die "cannot start git-blame $fn";
Glib::IO->add_watch(fileno($blame), 'in', \&read_blame_line);
@@ -62,8 +99,7 @@ sub flush_blame_line {
for(my $i = 0; $i < $cnt; $i++) {
@{$fileview->{data}->[$lno+$i-1]}[0,1,2] =
(substr($commit, 0, 8), $info,
$filename . ':' . ($s_lno+$i));
(substr($commit, 0, 8), $filename . ':' . ($s_lno+$i));
}
}

View File

@@ -1,2 +0,0 @@
This is "colordiff" (http://colordiff.sourceforge.net/) by Dave
Ewart <davee@sungate.co.uk>, modified specifically for git.

View File

@@ -1,196 +0,0 @@
#!/usr/bin/perl -w
#
# $Id: colordiff.pl,v 1.4.2.10 2004/01/04 15:02:59 daveewart Exp $
########################################################################
# #
# ColorDiff - a wrapper/replacment for 'diff' producing #
# colourful output #
# #
# Copyright (C)2002-2004 Dave Ewart (davee@sungate.co.uk) #
# #
########################################################################
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the Free Software #
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #
# #
########################################################################
use strict;
use Getopt::Long qw(:config pass_through);
use IPC::Open2;
my $app_name = 'colordiff';
my $version = '1.0.4';
my $author = 'Dave Ewart';
my $author_email = 'davee@sungate.co.uk';
my $app_www = 'http://colordiff.sourceforge.net/';
my $copyright = '(C)2002-2004';
my $show_banner = 1;
# ANSI sequences for colours
my %colour;
$colour{white} = "\033[1;37m";
$colour{yellow} = "\033[1;33m";
$colour{green} = "\033[1;32m";
$colour{blue} = "\033[1;34m";
$colour{cyan} = "\033[1;36m";
$colour{red} = "\033[1;31m";
$colour{magenta} = "\033[1;35m";
$colour{black} = "\033[1;30m";
$colour{darkwhite} = "\033[0;37m";
$colour{darkyellow} = "\033[0;33m";
$colour{darkgreen} = "\033[0;32m";
$colour{darkblue} = "\033[0;34m";
$colour{darkcyan} = "\033[0;36m";
$colour{darkred} = "\033[0;31m";
$colour{darkmagenta} = "\033[0;35m";
$colour{darkblack} = "\033[0;30m";
$colour{OFF} = "\033[0;0m";
# Default colours if /etc/colordiffrc or ~/.colordiffrc do not exist
my $plain_text = $colour{OFF};
my $file_old = $colour{red};
my $file_new = $colour{blue};
my $diff_stuff = $colour{magenta};
# Locations for personal and system-wide colour configurations
my $HOME = $ENV{HOME};
my $etcdir = '/etc';
my ($setting, $value);
my @config_files = ("$etcdir/colordiffrc", "$HOME/.colordiffrc");
my $config_file;
foreach $config_file (@config_files) {
if (open(COLORDIFFRC, "<$config_file")) {
while (<COLORDIFFRC>) {
chop;
next if (/^#/ || /^$/);
s/\s+//g;
($setting, $value) = split ('=');
if ($setting eq 'banner') {
if ($value eq 'no') {
$show_banner = 0;
}
next;
}
if (!defined $colour{$value}) {
print "Invalid colour specification ($value) in $config_file\n";
next;
}
if ($setting eq 'plain') {
$plain_text = $colour{$value};
}
elsif ($setting eq 'oldtext') {
$file_old = $colour{$value};
}
elsif ($setting eq 'newtext') {
$file_new = $colour{$value};
}
elsif ($setting eq 'diffstuff') {
$diff_stuff = $colour{$value};
}
else {
print "Unknown option in $etcdir/colordiffrc: $setting\n";
}
}
close COLORDIFFRC;
}
}
# colordiff specific options here. Need to pre-declare if using variables
GetOptions(
"no-banner" => sub { $show_banner = 0 },
"plain-text=s" => \&set_color,
"file-old=s" => \&set_color,
"file-new=s" => \&set_color,
"diff-stuff=s" => \&set_color
);
if ($show_banner == 1) {
print STDERR "$app_name $version ($app_www)\n";
print STDERR "$copyright $author, $author_email\n\n";
}
if (defined $ARGV[0]) {
# More reliable way of pulling in arguments
open2(\*INPUTSTREAM, undef, "git", "diff", @ARGV);
}
else {
*INPUTSTREAM = \*STDIN;
}
my $record;
my $nrecs = 0;
my $inside_file_old = 1;
my $nparents = undef;
while (<INPUTSTREAM>) {
$nrecs++;
if (/^(\@\@+) -[-+0-9, ]+ \1/) {
print "$diff_stuff";
$nparents = length($1) - 1;
}
elsif (/^diff -/ || /^index / ||
/^old mode / || /^new mode / ||
/^deleted file mode / || /^new file mode / ||
/^similarity index / || /^dissimilarity index / ||
/^copy from / || /^copy to / ||
/^rename from / || /^rename to /) {
$nparents = undef;
print "$diff_stuff";
}
elsif (defined $nparents) {
if ($nparents == 1) {
if (/^\+/) {
print $file_new;
}
elsif (/^-/) {
print $file_old;
}
else {
print $plain_text;
}
}
elsif (/^ {$nparents}/) {
print "$plain_text";
}
elsif (/^[+ ]{$nparents}/) {
print "$file_new";
}
elsif (/^[- ]{$nparents}/) {
print "$file_old";
}
else {
print $plain_text;
}
}
elsif (/^--- / || /^\+\+\+ /) {
print $diff_stuff;
}
else {
print "$plain_text";
}
s/$/$colour{OFF}/;
print "$_";
}
close INPUTSTREAM;
sub set_color {
my ($type, $color) = @_;
$type =~ s/-/_/;
eval "\$$type = \$colour{$color}";
}

View File

@@ -1,7 +1,7 @@
#
# bash completion support for core Git.
#
# Copyright (C) 2006 Shawn Pearce
# Copyright (C) 2006,2007 Shawn Pearce
# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
#
# The contained completion routines provide support for completing:
@@ -61,6 +61,25 @@ __git_ps1 ()
fi
}
__gitcomp ()
{
local all c s=$'\n' IFS=' '$'\t'$'\n'
local cur="${COMP_WORDS[COMP_CWORD]}"
if [ $# -gt 2 ]; then
cur="$3"
fi
for c in $1; do
case "$c$4" in
--*=*) all="$all$c$4$s" ;;
*.) all="$all$c$4$s" ;;
*) all="$all$c$4 $s" ;;
esac
done
IFS=$s
COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur"))
return
}
__git_heads ()
{
local cmd i is_hash=y dir="$(__gitdir "$1")"
@@ -200,7 +219,7 @@ __git_complete_file ()
-- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
;;
esac
}
@@ -212,15 +231,18 @@ __git_complete_revlist ()
*...*)
pfx="${cur%...*}..."
cur="${cur#*...}"
COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)" "$pfx" "$cur"
;;
*..*)
pfx="${cur%..*}.."
cur="${cur#*..}"
COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)" "$pfx" "$cur"
;;
*.)
__gitcomp "$cur."
;;
*)
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
;;
esac
}
@@ -235,15 +257,27 @@ __git_commands ()
for i in $(git help -a|egrep '^ ')
do
case $i in
add--interactive) : plumbing;;
applymbox) : ask gittus;;
applypatch) : ask gittus;;
archimport) : import;;
cat-file) : plumbing;;
check-ref-format) : plumbing;;
commit-tree) : plumbing;;
convert-objects) : plumbing;;
cvsexportcommit) : export;;
cvsimport) : import;;
cvsserver) : daemon;;
daemon) : daemon;;
diff-stages) : nobody uses it;;
fast-import) : import;;
fsck-objects) : plumbing;;
fetch-pack) : plumbing;;
fmt-merge-msg) : plumbing;;
hash-object) : plumbing;;
http-*) : transport;;
index-pack) : plumbing;;
init-db) : deprecated;;
local-fetch) : plumbing;;
mailinfo) : plumbing;;
mailsplit) : plumbing;;
@@ -256,9 +290,15 @@ __git_commands ()
parse-remote) : plumbing;;
patch-id) : plumbing;;
peek-remote) : plumbing;;
prune) : plumbing;;
prune-packed) : plumbing;;
quiltimport) : import;;
read-tree) : plumbing;;
receive-pack) : plumbing;;
reflog) : plumbing;;
repo-config) : plumbing;;
rerere) : plumbing;;
resolve) : dead dont use;;
rev-list) : plumbing;;
rev-parse) : plumbing;;
runstatus) : plumbing;;
@@ -268,14 +308,19 @@ __git_commands ()
show-index) : plumbing;;
ssh-*) : transport;;
stripspace) : plumbing;;
svn) : import export;;
svnimport) : import;;
symbolic-ref) : plumbing;;
tar-tree) : deprecated;;
unpack-file) : plumbing;;
unpack-objects) : plumbing;;
update-index) : plumbing;;
update-ref) : plumbing;;
update-server-info) : daemon;;
upload-archive) : plumbing;;
upload-pack) : plumbing;;
write-tree) : plumbing;;
verify-tag) : plumbing;;
*) echo $i;;
esac
done
@@ -314,22 +359,19 @@ _git_am ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
if [ -d .dotest ]; then
COMPREPLY=($(compgen -W "
--skip --resolved
" -- "$cur"))
__gitcomp "--skip --resolved"
return
fi
case "$cur" in
--whitespace=*)
COMPREPLY=($(compgen -W "$__git_whitespacelist" \
-- "${cur##--whitespace=}"))
__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
return
;;
--*)
COMPREPLY=($(compgen -W "
__gitcomp "
--signoff --utf8 --binary --3way --interactive
--whitespace=
" -- "$cur"))
"
return
esac
COMPREPLY=()
@@ -340,48 +382,74 @@ _git_apply ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--whitespace=*)
COMPREPLY=($(compgen -W "$__git_whitespacelist" \
-- "${cur##--whitespace=}"))
__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
return
;;
--*)
COMPREPLY=($(compgen -W "
__gitcomp "
--stat --numstat --summary --check --index
--cached --index-info --reverse --reject --unidiff-zero
--apply --no-add --exclude=
--whitespace= --inaccurate-eof --verbose
" -- "$cur"))
"
return
esac
COMPREPLY=()
}
_git_branch ()
_git_add ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur"))
case "$cur" in
--*)
__gitcomp "--interactive"
return
esac
COMPREPLY=()
}
_git_cat_file ()
_git_bisect ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
case "${COMP_WORDS[0]},$COMP_CWORD" in
git-cat-file*,1)
COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
;;
git,2)
COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
local i c=1 command
while [ $c -lt $COMP_CWORD ]; do
i="${COMP_WORDS[c]}"
case "$i" in
start|bad|good|reset|visualize|replay|log)
command="$i"
break
;;
esac
c=$((++c))
done
if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
__gitcomp "start bad good reset visualize replay log"
return
fi
case "$command" in
bad|good|reset)
__gitcomp "$(__git_refs)"
;;
*)
__git_complete_file
COMPREPLY=()
;;
esac
}
_git_branch ()
{
__gitcomp "$(__git_refs)"
}
_git_checkout ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
}
_git_cherry ()
{
__gitcomp "$(__git_refs)"
}
_git_cherry_pick ()
@@ -389,12 +457,10 @@ _git_cherry_pick ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
COMPREPLY=($(compgen -W "
--edit --no-commit
" -- "$cur"))
__gitcomp "--edit --no-commit"
;;
*)
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
;;
esac
}
@@ -404,10 +470,10 @@ _git_commit ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
COMPREPLY=($(compgen -W "
__gitcomp "
--all --author= --signoff --verify --no-verify
--edit --amend --include --only
" -- "$cur"))
"
return
esac
COMPREPLY=()
@@ -420,8 +486,7 @@ _git_diff ()
_git_diff_tree ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
}
_git_fetch ()
@@ -430,16 +495,15 @@ _git_fetch ()
case "${COMP_WORDS[0]},$COMP_CWORD" in
git-fetch*,1)
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)"
;;
git,2)
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)"
;;
*)
case "$cur" in
*:*)
cur="${cur#*:}"
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)" "" "${cur#*:}"
;;
*)
local remote
@@ -447,7 +511,7 @@ _git_fetch ()
git-fetch) remote="${COMP_WORDS[1]}" ;;
git) remote="${COMP_WORDS[2]}" ;;
esac
COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur"))
__gitcomp "$(__git_refs2 "$remote")"
;;
esac
;;
@@ -459,7 +523,7 @@ _git_format_patch ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
COMPREPLY=($(compgen -W "
__gitcomp "
--stdout --attach --thread
--output-directory
--numbered --start-number
@@ -467,17 +531,29 @@ _git_format_patch ()
--signoff
--in-reply-to=
--full-index --binary
" -- "$cur"))
--not --all
"
return
;;
esac
__git_complete_revlist
}
_git_ls_remote ()
_git_gc ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
case "$cur" in
--*)
__gitcomp "--prune"
return
;;
esac
COMPREPLY=()
}
_git_ls_remote ()
{
__gitcomp "$(__git_remotes)"
}
_git_ls_tree ()
@@ -490,13 +566,13 @@ _git_log ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--pretty=*)
COMPREPLY=($(compgen -W "
__gitcomp "
oneline short medium full fuller email raw
" -- "${cur##--pretty=}"))
" "" "${cur##--pretty=}"
return
;;
--*)
COMPREPLY=($(compgen -W "
__gitcomp "
--max-count= --max-age= --since= --after=
--min-age= --before= --until=
--root --not --topo-order --date-order
@@ -506,7 +582,8 @@ _git_log ()
--author= --committer= --grep=
--all-match
--pretty= --name-status --name-only
" -- "$cur"))
--not --all
"
return
;;
esac
@@ -518,34 +595,31 @@ _git_merge ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "${COMP_WORDS[COMP_CWORD-1]}" in
-s|--strategy)
COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
__gitcomp "$(__git_merge_strategies)"
return
esac
case "$cur" in
--strategy=*)
COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \
-- "${cur##--strategy=}"))
__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
return
;;
--*)
COMPREPLY=($(compgen -W "
__gitcomp "
--no-commit --no-summary --squash --strategy
" -- "$cur"))
"
return
esac
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
}
_git_merge_base ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
}
_git_name_rev ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "--tags --all --stdin" -- "$cur"))
__gitcomp "--tags --all --stdin"
}
_git_pull ()
@@ -554,10 +628,10 @@ _git_pull ()
case "${COMP_WORDS[0]},$COMP_CWORD" in
git-pull*,1)
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)"
;;
git,2)
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)"
;;
*)
local remote
@@ -565,7 +639,7 @@ _git_pull ()
git-pull) remote="${COMP_WORDS[1]}" ;;
git) remote="${COMP_WORDS[2]}" ;;
esac
COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
__gitcomp "$(__git_refs "$remote")"
;;
esac
}
@@ -576,10 +650,10 @@ _git_push ()
case "${COMP_WORDS[0]},$COMP_CWORD" in
git-push*,1)
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)"
;;
git,2)
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)"
;;
*)
case "$cur" in
@@ -589,11 +663,10 @@ _git_push ()
git-push) remote="${COMP_WORDS[1]}" ;;
git) remote="${COMP_WORDS[2]}" ;;
esac
cur="${cur#*:}"
COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
__gitcomp "$(__git_refs "$remote")" "" "${cur#*:}"
;;
*)
COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur"))
__gitcomp "$(__git_refs2)"
;;
esac
;;
@@ -603,30 +676,25 @@ _git_push ()
_git_rebase ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
if [ -d .dotest ]; then
COMPREPLY=($(compgen -W "
--continue --skip --abort
" -- "$cur"))
if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then
__gitcomp "--continue --skip --abort"
return
fi
case "${COMP_WORDS[COMP_CWORD-1]}" in
-s|--strategy)
COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
__gitcomp "$(__git_merge_strategies)"
return
esac
case "$cur" in
--strategy=*)
COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \
-- "${cur##--strategy=}"))
__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
return
;;
--*)
COMPREPLY=($(compgen -W "
--onto --merge --strategy
" -- "$cur"))
__gitcomp "--onto --merge --strategy"
return
esac
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
}
_git_config ()
@@ -635,26 +703,40 @@ _git_config ()
local prv="${COMP_WORDS[COMP_CWORD-1]}"
case "$prv" in
branch.*.remote)
COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)"
return
;;
branch.*.merge)
COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
__gitcomp "$(__git_refs)"
return
;;
remote.*.fetch)
local remote="${prv#remote.}"
remote="${remote%.fetch}"
COMPREPLY=($(compgen -W "$(__git_refs_remotes "$remote")" \
-- "$cur"))
__gitcomp "$(__git_refs_remotes "$remote")"
return
;;
remote.*.push)
local remote="${prv#remote.}"
remote="${remote%.push}"
COMPREPLY=($(compgen -W "$(git --git-dir="$(__gitdir)" \
__gitcomp "$(git --git-dir="$(__gitdir)" \
for-each-ref --format='%(refname):%(refname)' \
refs/heads)" -- "$cur"))
refs/heads)"
return
;;
pull.twohead|pull.octopus)
__gitcomp "$(__git_merge_strategies)"
return
;;
color.branch|color.diff|color.status)
__gitcomp "always never auto"
return
;;
color.*.*)
__gitcomp "
black red green yellow blue magenta cyan white
bold dim ul blink reverse
"
return
;;
*.*)
@@ -664,41 +746,39 @@ _git_config ()
esac
case "$cur" in
--*)
COMPREPLY=($(compgen -W "
__gitcomp "
--global --list --replace-all
--get --get-all --get-regexp
--unset --unset-all
" -- "$cur"))
--add --unset --unset-all
"
return
;;
branch.*.*)
local pfx="${cur%.*}."
cur="${cur##*.}"
COMPREPLY=($(compgen -P "$pfx" -W "remote merge" -- "$cur"))
__gitcomp "remote merge" "$pfx" "$cur"
return
;;
branch.*)
local pfx="${cur%.*}."
cur="${cur#*.}"
COMPREPLY=($(compgen -P "$pfx" -S . \
-W "$(__git_heads)" -- "$cur"))
__gitcomp "$(__git_heads)" "$pfx" "$cur" "."
return
;;
remote.*.*)
local pfx="${cur%.*}."
cur="${cur##*.}"
COMPREPLY=($(compgen -P "$pfx" -W "url fetch push" -- "$cur"))
__gitcomp "url fetch push" "$pfx" "$cur"
return
;;
remote.*)
local pfx="${cur%.*}."
cur="${cur#*.}"
COMPREPLY=($(compgen -P "$pfx" -S . \
-W "$(__git_remotes)" -- "$cur"))
__gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
return
;;
esac
COMPREPLY=($(compgen -W "
__gitcomp "
apply.whitespace
core.fileMode
core.gitProxy
@@ -710,47 +790,105 @@ _git_config ()
core.warnAmbiguousRefs
core.compression
core.legacyHeaders
i18n.commitEncoding
i18n.logOutputEncoding
diff.color
core.packedGitWindowSize
core.packedGitLimit
color.branch
color.branch.current
color.branch.local
color.branch.remote
color.branch.plain
color.diff
color.diff.plain
color.diff.meta
color.diff.frag
color.diff.old
color.diff.new
color.diff.commit
color.diff.whitespace
color.pager
color.status
color.status.header
color.status.added
color.status.changed
color.status.untracked
diff.renameLimit
diff.renames
pager.color
color.pager
status.color
color.status
log.showroot
show.difftree
showbranch.default
whatchanged.difftree
fetch.unpackLimit
format.headers
gitcvs.enabled
gitcvs.logfile
gc.reflogexpire
gc.reflogexpireunreachable
gc.rerereresolved
gc.rerereunresolved
http.sslVerify
http.sslCert
http.sslKey
http.sslCAInfo
http.sslCAPath
http.maxRequests
http.lowSpeedLimit http.lowSpeedTime
http.lowSpeedLimit
http.lowSpeedTime
http.noEPSV
pack.window
repack.useDeltaBaseOffset
pull.octopus pull.twohead
i18n.commitEncoding
i18n.logOutputEncoding
log.showroot
merge.summary
merge.verbosity
pack.window
pull.octopus
pull.twohead
repack.useDeltaBaseOffset
show.difftree
showbranch.default
tar.umask
transfer.unpackLimit
receive.unpackLimit
receive.denyNonFastForwards
user.name user.email
tar.umask
gitcvs.enabled
gitcvs.logfile
user.name
user.email
user.signingkey
whatchanged.difftree
branch. remote.
" -- "$cur"))
"
}
_git_remote ()
{
local i c=1 command
while [ $c -lt $COMP_CWORD ]; do
i="${COMP_WORDS[c]}"
case "$i" in
add|show|prune) command="$i"; break ;;
esac
c=$((++c))
done
if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
__gitcomp "add show prune"
return
fi
case "$command" in
show|prune)
__gitcomp "$(__git_remotes)"
;;
*)
COMPREPLY=()
;;
esac
}
_git_reset ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local opt="--mixed --hard --soft"
COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
case "$cur" in
--*)
__gitcomp "--mixed --hard --soft"
return
;;
esac
__gitcomp "$(__git_refs)"
}
_git_show ()
@@ -758,13 +896,13 @@ _git_show ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--pretty=*)
COMPREPLY=($(compgen -W "
__gitcomp "
oneline short medium full fuller email raw
" -- "${cur##--pretty=}"))
" "" "${cur##--pretty=}"
return
;;
--*)
COMPREPLY=($(compgen -W "--pretty=" -- "$cur"))
__gitcomp "--pretty="
return
;;
esac
@@ -787,12 +925,12 @@ _git ()
done
if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
COMPREPLY=($(compgen -W "
--git-dir= --version --exec-path
$(__git_commands)
$(__git_aliases)
" -- "${COMP_WORDS[COMP_CWORD]}"))
return;
case "${COMP_WORDS[COMP_CWORD]}" in
--*=*) COMPREPLY=() ;;
--*) __gitcomp "--git-dir= --bare --version --exec-path" ;;
*) __gitcomp "$(__git_commands) $(__git_aliases)" ;;
esac
return
fi
local expansion=$(__git_aliased_command "$command")
@@ -800,10 +938,12 @@ _git ()
case "$command" in
am) _git_am ;;
add) _git_add ;;
apply) _git_apply ;;
bisect) _git_bisect ;;
branch) _git_branch ;;
cat-file) _git_cat_file ;;
checkout) _git_checkout ;;
cherry) _git_cherry ;;
cherry-pick) _git_cherry_pick ;;
commit) _git_commit ;;
config) _git_config ;;
@@ -811,6 +951,7 @@ _git ()
diff-tree) _git_diff_tree ;;
fetch) _git_fetch ;;
format-patch) _git_format_patch ;;
gc) _git_gc ;;
log) _git_log ;;
ls-remote) _git_ls_remote ;;
ls-tree) _git_ls_tree ;;
@@ -820,7 +961,7 @@ _git ()
pull) _git_pull ;;
push) _git_push ;;
rebase) _git_rebase ;;
repo-config) _git_config ;;
remote) _git_remote ;;
reset) _git_reset ;;
show) _git_show ;;
show-branch) _git_log ;;
@@ -832,33 +973,42 @@ _git ()
_gitk ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur"))
case "$cur" in
--*)
__gitcomp "--not --all"
return
;;
esac
__git_complete_revlist
}
complete -o default -o nospace -F _git git
complete -o default -F _gitk gitk
complete -o default -F _git_am git-am
complete -o default -F _git_apply git-apply
complete -o default -F _git_branch git-branch
complete -o default -o nospace -F _git_cat_file git-cat-file
complete -o default -F _git_checkout git-checkout
complete -o default -F _git_cherry_pick git-cherry-pick
complete -o default -F _git_commit git-commit
complete -o default -o nospace -F _gitk gitk
complete -o default -o nospace -F _git_am git-am
complete -o default -o nospace -F _git_apply git-apply
complete -o default -o nospace -F _git_bisect git-bisect
complete -o default -o nospace -F _git_branch git-branch
complete -o default -o nospace -F _git_checkout git-checkout
complete -o default -o nospace -F _git_cherry git-cherry
complete -o default -o nospace -F _git_cherry_pick git-cherry-pick
complete -o default -o nospace -F _git_commit git-commit
complete -o default -o nospace -F _git_diff git-diff
complete -o default -F _git_diff_tree git-diff-tree
complete -o default -o nospace -F _git_diff_tree git-diff-tree
complete -o default -o nospace -F _git_fetch git-fetch
complete -o default -o nospace -F _git_format_patch git-format-patch
complete -o default -o nospace -F _git_gc git-gc
complete -o default -o nospace -F _git_log git-log
complete -o default -F _git_ls_remote git-ls-remote
complete -o default -o nospace -F _git_ls_remote git-ls-remote
complete -o default -o nospace -F _git_ls_tree git-ls-tree
complete -o default -F _git_merge git-merge
complete -o default -F _git_merge_base git-merge-base
complete -o default -F _git_name_rev git-name-rev
complete -o default -o nospace -F _git_merge git-merge
complete -o default -o nospace -F _git_merge_base git-merge-base
complete -o default -o nospace -F _git_name_rev git-name-rev
complete -o default -o nospace -F _git_pull git-pull
complete -o default -o nospace -F _git_push git-push
complete -o default -F _git_rebase git-rebase
complete -o default -F _git_config git-config
complete -o default -F _git_reset git-reset
complete -o default -o nospace -F _git_rebase git-rebase
complete -o default -o nospace -F _git_config git-config
complete -o default -o nospace -F _git_remote git-remote
complete -o default -o nospace -F _git_reset git-reset
complete -o default -o nospace -F _git_show git-show
complete -o default -o nospace -F _git_log git-show-branch
complete -o default -o nospace -F _git_log git-whatchanged
@@ -868,19 +1018,20 @@ complete -o default -o nospace -F _git_log git-whatchanged
# included the '.exe' suffix.
#
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
complete -o default -F _git_apply git-apply.exe
complete -o default -o nospace -F _git_add git-add.exe
complete -o default -o nospace -F _git_apply git-apply.exe
complete -o default -o nospace -F _git git.exe
complete -o default -F _git_branch git-branch.exe
complete -o default -o nospace -F _git_cat_file git-cat-file.exe
complete -o default -o nospace -F _git_branch git-branch.exe
complete -o default -o nospace -F _git_cherry git-cherry.exe
complete -o default -o nospace -F _git_diff git-diff.exe
complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
complete -o default -o nospace -F _git_format_patch git-format-patch.exe
complete -o default -o nospace -F _git_log git-log.exe
complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
complete -o default -F _git_merge_base git-merge-base.exe
complete -o default -F _git_name_rev git-name-rev.exe
complete -o default -o nospace -F _git_merge_base git-merge-base.exe
complete -o default -o nospace -F _git_name_rev git-name-rev.exe
complete -o default -o nospace -F _git_push git-push.exe
complete -o default -F _git_config git-config
complete -o default -o nospace -F _git_config git-config
complete -o default -o nospace -F _git_show git-show.exe
complete -o default -o nospace -F _git_log git-show-branch.exe
complete -o default -o nospace -F _git_log git-whatchanged.exe

380
contrib/emacs/git-blame.el Normal file
View File

@@ -0,0 +1,380 @@
;;; git-blame.el --- Minor mode for incremental blame for Git -*- coding: utf-8 -*-
;;
;; Copyright (C) 2007 David Kågedal
;;
;; Authors: David Kågedal <davidk@lysator.liu.se>
;; Created: 31 Jan 2007
;; Message-ID: <87iren2vqx.fsf@morpheus.local>
;; License: GPL
;; Keywords: git, version control, release management
;;
;; Compatibility: Emacs21
;; This file is *NOT* part of GNU Emacs.
;; This file is distributed under the same terms as GNU Emacs.
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2 of
;; the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be
;; useful, but WITHOUT ANY WARRANTY; without even the implied
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE. See the GNU General Public License for more details.
;; You should have received a copy of the GNU General Public
;; License along with this program; if not, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA
;; http://www.fsf.org/copyleft/gpl.html
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;; Here is an Emacs implementation of incremental git-blame. When you
;; turn it on while viewing a file, the editor buffer will be updated by
;; setting the background of individual lines to a color that reflects
;; which commit it comes from. And when you move around the buffer, a
;; one-line summary will be shown in the echo area.
;;; Installation:
;;
;; To use this package, put it somewhere in `load-path' (or add
;; directory with git-blame.el to `load-path'), and add the following
;; line to your .emacs:
;;
;; (require 'git-blame)
;;
;; If you do not want to load this package before it is necessary, you
;; can make use of the `autoload' feature, e.g. by adding to your .emacs
;; the following lines
;;
;; (autoload 'git-blame-mode "git-blame"
;; "Minor mode for incremental blame for Git." t)
;;
;; Then first use of `M-x git-blame-mode' would load the package.
;;; Compatibility:
;;
;; It requires GNU Emacs 21. If you'are using Emacs 20, try
;; changing this:
;;
;; (overlay-put ovl 'face (list :background
;; (cdr (assq 'color (cddddr info)))))
;;
;; to
;;
;; (overlay-put ovl 'face (cons 'background-color
;; (cdr (assq 'color (cddddr info)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:
(require 'cl) ; to use `push', `pop'
(defun color-scale (l)
(let* ((colors ())
r g b)
(setq r l)
(while r
(setq g l)
(while g
(setq b l)
(while b
(push (concat "#" (car r) (car g) (car b)) colors)
(pop b))
(pop g))
(pop r))
colors))
(defvar git-blame-dark-colors
(color-scale '("0c" "04" "24" "1c" "2c" "34" "14" "3c")))
(defvar git-blame-light-colors
(color-scale '("c4" "d4" "cc" "dc" "f4" "e4" "fc" "ec")))
(defvar git-blame-ancient-color "dark green")
(defvar git-blame-autoupdate t
"*Automatically update the blame display while editing")
(defvar git-blame-proc nil
"The running git-blame process")
(make-variable-buffer-local 'git-blame-proc)
(defvar git-blame-overlays nil
"The git-blame overlays used in the current buffer.")
(make-variable-buffer-local 'git-blame-overlays)
(defvar git-blame-cache nil
"A cache of git-blame information for the current buffer")
(make-variable-buffer-local 'git-blame-cache)
(defvar git-blame-idle-timer nil
"An idle timer that updates the blame")
(make-variable-buffer-local 'git-blame-cache)
(defvar git-blame-update-queue nil
"A queue of update requests")
(make-variable-buffer-local 'git-blame-update-queue)
(defvar git-blame-mode nil)
(make-variable-buffer-local 'git-blame-mode)
(unless (assq 'git-blame-mode minor-mode-alist)
(setq minor-mode-alist
(cons (list 'git-blame-mode " blame")
minor-mode-alist)))
;;;###autoload
(defun git-blame-mode (&optional arg)
"Minor mode for displaying Git blame"
(interactive "P")
(if arg
(setq git-blame-mode (eq arg 1))
(setq git-blame-mode (not git-blame-mode)))
(make-local-variable 'git-blame-colors)
(if git-blame-autoupdate
(add-hook 'after-change-functions 'git-blame-after-change nil t)
(remove-hook 'after-change-functions 'git-blame-after-change t))
(git-blame-cleanup)
(if git-blame-mode
(progn
(let ((bgmode (cdr (assoc 'background-mode (frame-parameters)))))
(if (eq bgmode 'dark)
(setq git-blame-colors git-blame-dark-colors)
(setq git-blame-colors git-blame-light-colors)))
(setq git-blame-cache (make-hash-table :test 'equal))
(git-blame-run))
(cancel-timer git-blame-idle-timer)))
;;;###autoload
(defun git-reblame ()
"Recalculate all blame information in the current buffer"
(unless git-blame-mode
(error "git-blame is not active"))
(interactive)
(git-blame-cleanup)
(git-blame-run))
(defun git-blame-run (&optional startline endline)
(if git-blame-proc
;; Should maybe queue up a new run here
(message "Already running git blame")
(let ((display-buf (current-buffer))
(blame-buf (get-buffer-create
(concat " git blame for " (buffer-name))))
(args '("--incremental" "--contents" "-")))
(if startline
(setq args (append args
(list "-L" (format "%d,%d" startline endline)))))
(setq args (append args
(list (file-name-nondirectory buffer-file-name))))
(setq git-blame-proc
(apply 'start-process
"git-blame" blame-buf
"git" "blame"
args))
(with-current-buffer blame-buf
(erase-buffer)
(make-local-variable 'git-blame-file)
(make-local-variable 'git-blame-current)
(setq git-blame-file display-buf)
(setq git-blame-current nil))
(set-process-filter git-blame-proc 'git-blame-filter)
(set-process-sentinel git-blame-proc 'git-blame-sentinel)
(process-send-region git-blame-proc (point-min) (point-max))
(process-send-eof git-blame-proc))))
(defun remove-git-blame-text-properties (start end)
(let ((modified (buffer-modified-p))
(inhibit-read-only t))
(remove-text-properties start end '(point-entered nil))
(set-buffer-modified-p modified)))
(defun git-blame-cleanup ()
"Remove all blame properties"
(mapcar 'delete-overlay git-blame-overlays)
(setq git-blame-overlays nil)
(remove-git-blame-text-properties (point-min) (point-max)))
(defun git-blame-update-region (start end)
"Rerun blame to get updates between START and END"
(let ((overlays (overlays-in start end)))
(while overlays
(let ((overlay (pop overlays)))
(if (< (overlay-start overlay) start)
(setq start (overlay-start overlay)))
(if (> (overlay-end overlay) end)
(setq end (overlay-end overlay)))
(setq git-blame-overlays (delete overlay git-blame-overlays))
(delete-overlay overlay))))
(remove-git-blame-text-properties start end)
;; We can be sure that start and end are at line breaks
(git-blame-run (1+ (count-lines (point-min) start))
(count-lines (point-min) end)))
(defun git-blame-sentinel (proc status)
(with-current-buffer (process-buffer proc)
(with-current-buffer git-blame-file
(setq git-blame-proc nil)
(if git-blame-update-queue
(git-blame-delayed-update))))
;;(kill-buffer (process-buffer proc))
;;(message "git blame finished")
)
(defvar in-blame-filter nil)
(defun git-blame-filter (proc str)
(save-excursion
(set-buffer (process-buffer proc))
(goto-char (process-mark proc))
(insert-before-markers str)
(goto-char 0)
(unless in-blame-filter
(let ((more t)
(in-blame-filter t))
(while more
(setq more (git-blame-parse)))))))
(defun git-blame-parse ()
(cond ((looking-at "\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)\n")
(let ((hash (match-string 1))
(src-line (string-to-number (match-string 2)))
(res-line (string-to-number (match-string 3)))
(num-lines (string-to-number (match-string 4))))
(setq git-blame-current
(if (string= hash "0000000000000000000000000000000000000000")
nil
(git-blame-new-commit
hash src-line res-line num-lines))))
(delete-region (point) (match-end 0))
t)
((looking-at "filename \\(.+\\)\n")
(let ((filename (match-string 1)))
(git-blame-add-info "filename" filename))
(delete-region (point) (match-end 0))
t)
((looking-at "\\([a-z-]+\\) \\(.+\\)\n")
(let ((key (match-string 1))
(value (match-string 2)))
(git-blame-add-info key value))
(delete-region (point) (match-end 0))
t)
((looking-at "boundary\n")
(setq git-blame-current nil)
(delete-region (point) (match-end 0))
t)
(t
nil)))
(defun git-blame-new-commit (hash src-line res-line num-lines)
(save-excursion
(set-buffer git-blame-file)
(let ((info (gethash hash git-blame-cache))
(inhibit-point-motion-hooks t)
(inhibit-modification-hooks t))
(when (not info)
(let ((color (pop git-blame-colors)))
(unless color
(setq color git-blame-ancient-color))
(setq info (list hash src-line res-line num-lines
(git-describe-commit hash)
(cons 'color color))))
(puthash hash info git-blame-cache))
(goto-line res-line)
(while (> num-lines 0)
(if (get-text-property (point) 'git-blame)
(forward-line)
(let* ((start (point))
(end (progn (forward-line 1) (point)))
(ovl (make-overlay start end)))
(push ovl git-blame-overlays)
(overlay-put ovl 'git-blame info)
(overlay-put ovl 'help-echo hash)
(overlay-put ovl 'face (list :background
(cdr (assq 'color (nthcdr 5 info)))))
;; the point-entered property doesn't seem to work in overlays
;;(overlay-put ovl 'point-entered
;; `(lambda (x y) (git-blame-identify ,hash)))
(let ((modified (buffer-modified-p)))
(put-text-property (if (= start 1) start (1- start)) (1- end)
'point-entered
`(lambda (x y) (git-blame-identify ,hash)))
(set-buffer-modified-p modified))))
(setq num-lines (1- num-lines))))))
(defun git-blame-add-info (key value)
(if git-blame-current
(nconc git-blame-current (list (cons (intern key) value)))))
(defun git-blame-current-commit ()
(let ((info (get-char-property (point) 'git-blame)))
(if info
(car info)
(error "No commit info"))))
(defun git-describe-commit (hash)
(with-temp-buffer
(call-process "git" nil t nil
"log" "-1" "--pretty=oneline"
hash)
(buffer-substring (point-min) (1- (point-max)))))
(defvar git-blame-last-identification nil)
(make-variable-buffer-local 'git-blame-last-identification)
(defun git-blame-identify (&optional hash)
(interactive)
(let ((info (gethash (or hash (git-blame-current-commit)) git-blame-cache)))
(when (and info (not (eq info git-blame-last-identification)))
(message "%s" (nth 4 info))
(setq git-blame-last-identification info))))
;; (defun git-blame-after-save ()
;; (when git-blame-mode
;; (git-blame-cleanup)
;; (git-blame-run)))
;; (add-hook 'after-save-hook 'git-blame-after-save)
(defun git-blame-after-change (start end length)
(when git-blame-mode
(git-blame-enq-update start end)))
(defvar git-blame-last-update nil)
(make-variable-buffer-local 'git-blame-last-update)
(defun git-blame-enq-update (start end)
"Mark the region between START and END as needing blame update"
;; Try to be smart and avoid multiple callouts for sequential
;; editing
(cond ((and git-blame-last-update
(= start (cdr git-blame-last-update)))
(setcdr git-blame-last-update end))
((and git-blame-last-update
(= end (car git-blame-last-update)))
(setcar git-blame-last-update start))
(t
(setq git-blame-last-update (cons start end))
(setq git-blame-update-queue (nconc git-blame-update-queue
(list git-blame-last-update)))))
(unless (or git-blame-proc git-blame-idle-timer)
(setq git-blame-idle-timer
(run-with-idle-timer 0.5 nil 'git-blame-delayed-update))))
(defun git-blame-delayed-update ()
(setq git-blame-idle-timer nil)
(if git-blame-update-queue
(let ((first (pop git-blame-update-queue))
(inhibit-point-motion-hooks t))
(git-blame-update-region (car first) (cdr first)))))
(provide 'git-blame)
;;; git-blame.el ends here

View File

@@ -0,0 +1,103 @@
#!/usr/bin/perl
## tar archive frontend for git-fast-import
##
## For example:
##
## mkdir project; cd project; git init
## perl import-tars.perl *.tar.bz2
## git whatchanged import-tars
##
use strict;
die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV;
my $branch_name = 'import-tars';
my $branch_ref = "refs/heads/$branch_name";
my $committer_name = 'T Ar Creator';
my $committer_email = 'tar@example.com';
open(FI, '|-', 'git', 'fast-import', '--quiet')
or die "Unable to start git fast-import: $!\n";
foreach my $tar_file (@ARGV)
{
$tar_file =~ m,([^/]+)$,;
my $tar_name = $1;
if ($tar_name =~ s/\.(tar\.gz|tgz)$//) {
open(I, '-|', 'gzcat', $tar_file) or die "Unable to gzcat $tar_file: $!\n";
} elsif ($tar_name =~ s/\.(tar\.bz2|tbz2)$//) {
open(I, '-|', 'bzcat', $tar_file) or die "Unable to bzcat $tar_file: $!\n";
} elsif ($tar_name =~ s/\.tar\.Z$//) {
open(I, '-|', 'zcat', $tar_file) or die "Unable to zcat $tar_file: $!\n";
} elsif ($tar_name =~ s/\.tar$//) {
open(I, $tar_file) or die "Unable to open $tar_file: $!\n";
} else {
die "Unrecognized compression format: $tar_file\n";
}
my $commit_time = 0;
my $next_mark = 1;
my $have_top_dir = 1;
my ($top_dir, %files);
while (read(I, $_, 512) == 512) {
my ($name, $mode, $uid, $gid, $size, $mtime,
$chksum, $typeflag, $linkname, $magic,
$version, $uname, $gname, $devmajor, $devminor,
$prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12
Z8 Z1 Z100 Z6
Z2 Z32 Z32 Z8 Z8 Z*', $_;
last unless $name;
$mode = oct $mode;
$size = oct $size;
$mtime = oct $mtime;
next if $mode & 0040000;
print FI "blob\n", "mark :$next_mark\n", "data $size\n";
while ($size > 0 && read(I, $_, 512) == 512) {
print FI substr($_, 0, $size);
$size -= 512;
}
print FI "\n";
my $path = "$prefix$name";
$files{$path} = [$next_mark++, $mode];
$commit_time = $mtime if $mtime > $commit_time;
$path =~ m,^([^/]+)/,;
$top_dir = $1 unless $top_dir;
$have_top_dir = 0 if $top_dir ne $1;
}
print FI <<EOF;
commit $branch_ref
committer $committer_name <$committer_email> $commit_time +0000
data <<END_OF_COMMIT_MESSAGE
Imported from $tar_file.
END_OF_COMMIT_MESSAGE
deleteall
EOF
foreach my $path (keys %files)
{
my ($mark, $mode) = @{$files{$path}};
$path =~ s,^([^/]+)/,, if $have_top_dir;
printf FI "M %o :%i %s\n", $mode & 0111 ? 0755 : 0644, $mark, $path;
}
print FI "\n";
print FI <<EOF;
tag $tar_name
from $branch_ref
tagger $committer_name <$committer_email> $commit_time +0000
data <<END_OF_TAG_MESSAGE
Package $tar_name
END_OF_TAG_MESSAGE
EOF
close I;
}
close FI;

233
contrib/hg-to-git/hg-to-git.py Executable file
View File

@@ -0,0 +1,233 @@
#! /usr/bin/python
""" hg-to-svn.py - A Mercurial to GIT converter
Copyright (C)2007 Stelian Pop <stelian@popies.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""
import os, os.path, sys
import tempfile, popen2, pickle, getopt
import re
# Maps hg version -> git version
hgvers = {}
# List of children for each hg revision
hgchildren = {}
# Current branch for each hg revision
hgbranch = {}
#------------------------------------------------------------------------------
def usage():
print """\
%s: [OPTIONS] <hgprj>
options:
-s, --gitstate=FILE: name of the state to be saved/read
for incrementals
required:
hgprj: name of the HG project to import (directory)
""" % sys.argv[0]
#------------------------------------------------------------------------------
def getgitenv(user, date):
env = ''
elems = re.compile('(.*?)\s+<(.*)>').match(user)
if elems:
env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
env += 'export GIT_COMMITER_NAME="%s" ;' % elems.group(1)
env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
env += 'export GIT_COMMITER_EMAIL="%s" ;' % elems.group(2)
else:
env += 'export GIT_AUTHOR_NAME="%s" ;' % user
env += 'export GIT_COMMITER_NAME="%s" ;' % user
env += 'export GIT_AUTHOR_EMAIL= ;'
env += 'export GIT_COMMITER_EMAIL= ;'
env += 'export GIT_AUTHOR_DATE="%s" ;' % date
env += 'export GIT_COMMITTER_DATE="%s" ;' % date
return env
#------------------------------------------------------------------------------
state = ''
try:
opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir='])
for o, a in opts:
if o in ('-s', '--gitstate'):
state = a
state = os.path.abspath(state)
if len(args) != 1:
raise('params')
except:
usage()
sys.exit(1)
hgprj = args[0]
os.chdir(hgprj)
if state:
if os.path.exists(state):
print 'State does exist, reading'
f = open(state, 'r')
hgvers = pickle.load(f)
else:
print 'State does not exist, first run'
tip = os.popen('hg tip | head -1 | cut -f 2 -d :').read().strip()
print 'tip is', tip
# Calculate the branches
print 'analysing the branches...'
hgchildren["0"] = ()
hgbranch["0"] = "master"
for cset in range(1, int(tip) + 1):
hgchildren[str(cset)] = ()
prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()
if len(prnts) > 0:
parent = prnts[0].strip()
else:
parent = str(cset - 1)
hgchildren[parent] += ( str(cset), )
if len(prnts) > 1:
mparent = prnts[1].strip()
hgchildren[mparent] += ( str(cset), )
else:
mparent = None
if mparent:
# For merge changesets, take either one, preferably the 'master' branch
if hgbranch[mparent] == 'master':
hgbranch[str(cset)] = 'master'
else:
hgbranch[str(cset)] = hgbranch[parent]
else:
# Normal changesets
# For first children, take the parent branch, for the others create a new branch
if hgchildren[parent][0] == str(cset):
hgbranch[str(cset)] = hgbranch[parent]
else:
hgbranch[str(cset)] = "branch-" + str(cset)
if not hgvers.has_key("0"):
print 'creating repository'
os.system('git-init-db')
# loop through every hg changeset
for cset in range(int(tip) + 1):
# incremental, already seen
if hgvers.has_key(str(cset)):
continue
# get info
prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()
if len(prnts) > 0:
parent = prnts[0].strip()
else:
parent = str(cset - 1)
if len(prnts) > 1:
mparent = prnts[1].strip()
else:
mparent = None
(fdcomment, filecomment) = tempfile.mkstemp()
csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v ^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v ^description: | grep -v ^tag:' % cset).read().strip()
os.write(fdcomment, csetcomment)
os.close(fdcomment)
date = os.popen('hg log -r %d | grep ^date: | cut -f 2- -d :' % cset).read().strip()
tag = os.popen('hg log -r %d | grep ^tag: | cut -f 2- -d :' % cset).read().strip()
user = os.popen('hg log -r %d | grep ^user: | cut -f 2- -d :' % cset).read().strip()
print '-----------------------------------------'
print 'cset:', cset
print 'branch:', hgbranch[str(cset)]
print 'user:', user
print 'date:', date
print 'comment:', csetcomment
print 'parent:', parent
if mparent:
print 'mparent:', mparent
if tag:
print 'tag:', tag
print '-----------------------------------------'
# checkout the parent if necessary
if cset != 0:
if hgbranch[str(cset)] == "branch-" + str(cset):
print 'creating new branch', hgbranch[str(cset)]
os.system('git-checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
else:
print 'checking out branch', hgbranch[str(cset)]
os.system('git-checkout %s' % hgbranch[str(cset)])
# merge
if mparent:
if hgbranch[parent] == hgbranch[str(cset)]:
otherbranch = hgbranch[mparent]
else:
otherbranch = hgbranch[parent]
print 'merging', otherbranch, 'into', hgbranch[str(cset)]
os.system(getgitenv(user, date) + 'git-merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
# remove everything except .git and .hg directories
os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
# repopulate with checkouted files
os.system('hg update -C %d' % cset)
# add new files
os.system('git-ls-files -x .hg --others | git-update-index --add --stdin')
# delete removed files
os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin')
# commit
os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment)
os.unlink(filecomment)
# tag
if tag and tag != 'tip':
os.system(getgitenv(user, date) + 'git-tag %s' % tag)
# delete branch if not used anymore...
if mparent and len(hgchildren[str(cset)]):
print "Deleting unused branch:", otherbranch
os.system('git-branch -d %s' % otherbranch)
# retrieve and record the version
vvv = os.popen('git-show | head -1').read()
vvv = vvv[vvv.index(' ') + 1 : ].strip()
print 'record', cset, '->', vvv
hgvers[str(cset)] = vvv
os.system('git-repack -a -d')
# write the state for incrementals
if state:
print 'Writing state'
f = open(state, 'w')
pickle.dump(hgvers, f)
# vim: et ts=8 sw=4 sts=4

View File

@@ -0,0 +1,21 @@
hg-to-git.py is able to convert a Mercurial repository into a git one,
and preserves the branches in the process (unlike tailor)
hg-to-git.py can probably be greatly improved (it's a rather crude
combination of shell and python) but it does already work quite well for
me. Features:
- supports incremental conversion
(for keeping a git repo in sync with a hg one)
- supports hg branches
- converts hg tags
Note that the git repository will be created 'in place' (at the same
location as the source hg repo). You will have to manually remove the
'.hg' directory after the conversion.
Also note that the incremental conversion uses 'simple' hg changesets
identifiers (ordinals, as opposed to SHA-1 ids), and since these ids
are not stable across different repositories the hg-to-git.py state file
is forever tied to one hg repository.
Stelian Pop <stelian@popies.net>

View File

@@ -1,42 +0,0 @@
#
# Even with git, we don't always have name translations.
# So have an email->real name table to translate the
# (hopefully few) missing names
#
# repo-abbrev: /pub/scm/linux/kernel/git/
#
Adrian Bunk <bunk@stusta.de>
Andreas Herrmann <aherrman@de.ibm.com>
Andrew Morton <akpm@osdl.org>
Andrew Vasquez <andrew.vasquez@qlogic.com>
Christoph Hellwig <hch@lst.de>
Corey Minyard <minyard@acm.org>
David Woodhouse <dwmw2@shinybook.infradead.org>
Domen Puncer <domen@coderock.org>
Douglas Gilbert <dougg@torque.net>
Ed L Cashin <ecashin@coraid.com>
Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Felix Moeller <felix@derklecks.de>
Frank Zago <fzago@systemfabricworks.com>
Greg Kroah-Hartman <gregkh@suse.de>
James Bottomley <jejb@mulgrave.(none)>
James Bottomley <jejb@titanic.il.steeleye.com>
Jeff Garzik <jgarzik@pretzel.yyz.us>
Jens Axboe <axboe@suse.de>
Kay Sievers <kay.sievers@vrfy.org>
Mitesh shah <mshah@teja.com>
Morten Welinder <terra@gnome.org>
Morten Welinder <welinder@anemone.rentec.com>
Morten Welinder <welinder@darter.rentec.com>
Morten Welinder <welinder@troll.com>
Nguyen Anh Quynh <aquynh@gmail.com>
Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Peter A Jonsson <pj@ludd.ltu.se>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Rudolf Marek <R.Marek@sh.cvut.cz>
Rui Saraiva <rmps@joel.ist.utl.pt>
Sachin P Sant <ssant@in.ibm.com>
Santtu Hyrkk,Av(B <santtu.hyrkko@gmail.com>
Simon Kelley <simon@thekelleys.org.uk>
Tejun Heo <htejun@gmail.com>
Tony Luck <tony.luck@intel.com>

View File

@@ -408,7 +408,7 @@ static void make_service_overridable(const char *name, int ena) {
/*
* Separate the "extra args" information as supplied by the client connection.
* Any resulting data is squirrelled away in the given interpolation table.
* Any resulting data is squirreled away in the given interpolation table.
*/
static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
{

View File

@@ -7,6 +7,7 @@
#include "diff.h"
#include "diffcore.h"
#include "revision.h"
#include "cache-tree.h"
/*
* diff-files
@@ -271,7 +272,7 @@ static int diff_cache(struct rev_info *revs,
break;
}
/* Show difference between old and new */
show_modified(revs,ac[1], ce, 1,
show_modified(revs, ac[1], ce, 1,
cached, match_missing);
break;
case 1:
@@ -372,3 +373,44 @@ int run_diff_index(struct rev_info *revs, int cached)
diff_flush(&revs->diffopt);
return ret;
}
int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
{
struct tree *tree;
struct rev_info revs;
int i;
struct cache_entry **dst;
struct cache_entry *last = NULL;
/*
* This is used by git-blame to run diff-cache internally;
* it potentially needs to repeatedly run this, so we will
* start by removing the higher order entries the last round
* left behind.
*/
dst = active_cache;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce)) {
if (last && !strcmp(ce->name, last->name))
continue;
cache_tree_invalidate_path(active_cache_tree,
ce->name);
last = ce;
ce->ce_mode = 0;
ce->ce_flags &= ~htons(CE_STAGEMASK);
}
*dst++ = ce;
}
active_nr = dst - active_cache;
init_revisions(&revs, NULL);
revs.prune_data = opt->paths;
tree = parse_tree_indirect(tree_sha1);
if (!tree)
die("bad tree object %s", sha1_to_hex(tree_sha1));
if (read_tree(tree, 1, opt->paths))
return error("unable to read tree %s", sha1_to_hex(tree_sha1));
return diff_cache(&revs, active_cache, active_nr, revs.prune_data,
1, 0);
}

91
diff.c
View File

@@ -560,6 +560,24 @@ static char *pprint_rename(const char *a, const char *b)
int pfx_length, sfx_length;
int len_a = strlen(a);
int len_b = strlen(b);
int qlen_a = quote_c_style(a, NULL, NULL, 0);
int qlen_b = quote_c_style(b, NULL, NULL, 0);
if (qlen_a || qlen_b) {
if (qlen_a) len_a = qlen_a;
if (qlen_b) len_b = qlen_b;
name = xmalloc( len_a + len_b + 5 );
if (qlen_a)
quote_c_style(a, name, NULL, 0);
else
memcpy(name, a, len_a);
memcpy(name + len_a, " => ", 4);
if (qlen_b)
quote_c_style(b, name + len_a + 4, NULL, 0);
else
memcpy(name + len_a + 4, b, len_b + 1);
return name;
}
/* Find common prefix */
pfx_length = 0;
@@ -716,12 +734,14 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
struct diffstat_file *file = data->files[i];
int change = file->added + file->deleted;
len = quote_c_style(file->name, NULL, NULL, 0);
if (len) {
char *qname = xmalloc(len + 1);
quote_c_style(file->name, qname, NULL, 0);
free(file->name);
file->name = qname;
if (!file->is_renamed) { /* renames are already quoted by pprint_rename */
len = quote_c_style(file->name, NULL, NULL, 0);
if (len) {
char *qname = xmalloc(len + 1);
quote_c_style(file->name, qname, NULL, 0);
free(file->name);
file->name = qname;
}
}
len = strlen(file->name);
@@ -853,7 +873,7 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
printf("-\t-\t");
else
printf("%d\t%d\t", file->added, file->deleted);
if (options->line_termination &&
if (options->line_termination && !file->is_renamed &&
quote_c_style(file->name, NULL, NULL, 0))
quote_c_style(file->name, NULL, stdout, 0);
else
@@ -1359,6 +1379,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
s->should_munmap = 1;
/* FIXME! CRLF -> LF conversion goes here, based on "s->path" */
}
else {
char type[20];
@@ -2200,13 +2221,13 @@ static void diff_flush_raw(struct diff_filepair *p,
free((void*)path_two);
}
static void diff_flush_name(struct diff_filepair *p, int line_termination)
static void diff_flush_name(struct diff_filepair *p, struct diff_options *opt)
{
char *path = p->two->path;
if (line_termination)
if (opt->line_termination)
path = quote_one(p->two->path);
printf("%s%c", path, line_termination);
printf("%s%c", path, opt->line_termination);
if (p->two->path != path)
free(path);
}
@@ -2413,24 +2434,29 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
diff_flush_raw(p, opt);
else if (fmt & DIFF_FORMAT_NAME)
diff_flush_name(p, opt->line_termination);
diff_flush_name(p, opt);
}
static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
{
char *name = quote_one(fs->path);
if (fs->mode)
printf(" %s mode %06o %s\n", newdelete, fs->mode, fs->path);
printf(" %s mode %06o %s\n", newdelete, fs->mode, name);
else
printf(" %s %s\n", newdelete, fs->path);
printf(" %s %s\n", newdelete, name);
free(name);
}
static void show_mode_change(struct diff_filepair *p, int show_name)
{
if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
if (show_name)
if (show_name) {
char *name = quote_one(p->two->path);
printf(" mode change %06o => %06o %s\n",
p->one->mode, p->two->mode, p->two->path);
p->one->mode, p->two->mode, name);
free(name);
}
else
printf(" mode change %06o => %06o\n",
p->one->mode, p->two->mode);
@@ -2439,34 +2465,11 @@ static void show_mode_change(struct diff_filepair *p, int show_name)
static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
{
const char *old, *new;
char *names = pprint_rename(p->one->path, p->two->path);
/* Find common prefix */
old = p->one->path;
new = p->two->path;
while (1) {
const char *slash_old, *slash_new;
slash_old = strchr(old, '/');
slash_new = strchr(new, '/');
if (!slash_old ||
!slash_new ||
slash_old - old != slash_new - new ||
memcmp(old, new, slash_new - new))
break;
old = slash_old + 1;
new = slash_new + 1;
}
/* p->one->path thru old is the common prefix, and old and new
* through the end of names are renames
*/
if (old != p->one->path)
printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
(int)(old - p->one->path), p->one->path,
old, new, (int)(0.5 + p->score * 100.0/MAX_SCORE));
else
printf(" %s %s => %s (%d%%)\n", renamecopy,
p->one->path, p->two->path,
(int)(0.5 + p->score * 100.0/MAX_SCORE));
printf(" %s %s (%d%%)\n", renamecopy, names,
(int)(0.5 + p->score * 100.0/MAX_SCORE));
free(names);
show_mode_change(p, 0);
}
@@ -2487,8 +2490,10 @@ static void diff_summary(struct diff_filepair *p)
break;
default:
if (p->score) {
printf(" rewrite %s (%d%%)\n", p->two->path,
char *name = quote_one(p->two->path);
printf(" rewrite %s (%d%%)\n", name,
(int)(0.5 + p->score * 100.0/MAX_SCORE));
free(name);
show_mode_change(p, 0);
} else show_mode_change(p, 1);
break;

1
diff.h
View File

@@ -222,6 +222,7 @@ extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
extern int run_diff_index(struct rev_info *revs, int cached);
extern int do_diff_cache(const unsigned char *, struct diff_options *);
extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
#endif /* DIFF_H */

View File

@@ -89,6 +89,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
return error("git-checkout-index: unable to create file %s (%s)",
path, strerror(errno));
}
/* FIXME: LF -> CRLF conversion goes here, based on "ce->name" */
wrote = write_in_full(fd, new, size);
close(fd);
free(new);

2081
fast-import.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
#!/usr/bin/perl -w
use strict;
sub run_cmd_pipe {
@@ -282,7 +281,7 @@ sub update_cmd {
HEADER => $status_head, },
@mods);
if (@update) {
system(qw(git update-index --add --),
system(qw(git update-index --add --remove --),
map { $_->{VALUE} } @update);
say_n_paths('updated', @update);
}

View File

@@ -3,7 +3,7 @@
# Copyright (c) 2005, 2006 Junio C Hamano
USAGE='[--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way]
[--interactive] [--whitespace=<option>] <mbox>...
[--interactive] [--whitespace=<option>] [-C<n>] [-p<n>] <mbox>...
or, when resuming [--skip | --resolved]'
. git-sh-setup
set_reflog_action am
@@ -106,7 +106,8 @@ It does not apply to blobs recorded in its index."
}
prec=4
dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= ws= resolvemsg=
dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg=
git_apply_opt=
while case "$#" in 0) break;; esac
do
@@ -141,8 +142,8 @@ do
--sk|--ski|--skip)
skip=t; shift ;;
--whitespace=*)
ws=$1; shift ;;
--whitespace=*|-C*|-p*)
git_apply_opt="$git_apply_opt $1"; shift ;;
--resolvemsg=*)
resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;;
@@ -394,7 +395,7 @@ do
case "$resolved" in
'')
git-apply $binary --index $ws "$dotest/patch"
git-apply $git_apply_opt $binary --index "$dotest/patch"
apply_status=$?
;;
t)

View File

@@ -95,6 +95,15 @@ $ENV{'TMPDIR'} = $opt_t if $opt_t; # $ENV{TMPDIR} will affect tempdir() calls:
my $tmp = tempdir('git-archimport-XXXXXX', TMPDIR => 1, CLEANUP => 1);
$opt_v && print "+ Using $tmp as temporary directory\n";
unless (-d $git_dir) { # initial import needs empty directory
opendir DIR, '.' or die "Unable to open current directory: $!\n";
while (my $entry = readdir DIR) {
$entry =~ /^\.\.?$/ or
die "Initial import needs an empty current working directory.\n"
}
closedir DIR
}
my %reachable = (); # Arch repositories we can access
my %unreachable = (); # Arch repositories we can't access :<
my @psets = (); # the collection

View File

@@ -1,6 +1,6 @@
#!/bin/sh
USAGE='[start|bad|good|next|reset|visualize]'
USAGE='[start|bad|good|next|reset|visualize|replay|log]'
LONG_USAGE='git bisect start [<pathspec>] reset bisect state and start bisection.
git bisect bad [<rev>] mark <rev> a known-bad revision.
git bisect good [<rev>...] mark <rev>... known-good revisions.
@@ -11,6 +11,7 @@ git bisect replay <logfile> replay bisection log
git bisect log show bisect log.'
. git-sh-setup
require_work_tree
sq() {
@@PERL@@ -e '
@@ -152,7 +153,7 @@ bisect_next() {
nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
echo "Bisecting: $nr revisions left to test after this"
echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
git checkout new-bisect || exit
git checkout -q new-bisect || exit
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
git-show-branch "$rev"

View File

@@ -1,6 +1,6 @@
#!/bin/sh
USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
USAGE='[-q] [-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
SUBDIRECTORY_OK=Sometimes
. git-sh-setup
require_work_tree
@@ -15,6 +15,7 @@ branch=
newbranch=
newbranch_log=
merge=
quiet=
LF='
'
while [ "$#" != "0" ]; do
@@ -40,6 +41,9 @@ while [ "$#" != "0" ]; do
-m)
merge=1
;;
"-q")
quiet=1
;;
--)
break
;;
@@ -153,35 +157,25 @@ detach_warn=
if test -z "$branch$newbranch" && test "$new" != "$old"
then
detached="$new"
if test -n "$oldbranch"
if test -n "$oldbranch" && test -z "$quiet"
then
detach_warn="Note: you are not on any branch and are at commit \"$new_name\"
detach_warn="Note: moving to \"$new_name\" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
git checkout -b <new_branch_name>"
fi
elif test -z "$oldbranch" && test -n "$branch"
elif test -z "$oldbranch" && test -z "$quiet"
then
# Coming back...
if test -z "$force"
then
git show-ref -d -s | grep "$old" >/dev/null || {
echo >&2 \
"You are not on any branch and switching to branch '$new_name'
may lose your changes. At this point, you can do one of two things:
(1) Decide it is Ok and say 'git checkout -f $new_name';
(2) Start a new branch from the current commit, by saying
'git checkout -b <branch-name>'.
Leaving your HEAD detached; not switching to branch '$new_name'."
exit 1;
}
fi
echo >&2 "Previous HEAD position was $old"
fi
if [ "X$old" = X ]
then
echo >&2 "warning: You appear to be on a branch yet to be born."
echo >&2 "warning: Forcing checkout of $new_name."
if test -z "$quiet"
then
echo >&2 "warning: You appear to be on a branch yet to be born."
echo >&2 "warning: Forcing checkout of $new_name."
fi
force=1
fi
@@ -233,7 +227,7 @@ else
exit 0
)
saved_err=$?
if test "$saved_err" = 0
if test "$saved_err" = 0 && test -z "$quiet"
then
git diff-index --name-status "$new"
fi
@@ -258,11 +252,9 @@ if [ "$?" -eq 0 ]; then
if test -n "$branch"
then
GIT_DIR="$GIT_DIR" git-symbolic-ref -m "checkout: moving to $branch" HEAD "refs/heads/$branch"
if test -n "$newbranch"
if test -z "$quiet"
then
echo >&2 "Switched to a new branch \"$branch\""
else
echo >&2 "Switched to branch \"$branch\""
echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
fi
elif test -n "$detached"
then

View File

@@ -178,19 +178,32 @@ esac && export GIT_DIR && git-init ${template+"$template"} || usage
if test -n "$reference"
then
ref_git=
if test -d "$reference"
then
if test -d "$reference/.git/objects"
then
reference="$reference/.git"
ref_git="$reference/.git"
elif test -d "$reference/objects"
then
ref_git="$reference"
fi
reference=$(cd "$reference" && pwd)
echo "$reference/objects" >"$GIT_DIR/objects/info/alternates"
(cd "$reference" && tar cf - refs) |
(cd "$GIT_DIR/refs" &&
mkdir reference-tmp &&
cd reference-tmp &&
tar xf -)
fi
if test -n "$ref_git"
then
ref_git=$(cd "$ref_git" && pwd)
echo "$ref_git/objects" >"$GIT_DIR/objects/info/alternates"
(
GIT_DIR="$ref_git" git for-each-ref \
--format='%(objectname) %(*objectname)'
) |
while read a b
do
test -z "$a" ||
git update-ref "refs/reference-tmp/$a" "$a"
test -z "$b" ||
git update-ref "refs/reference-tmp/$b" "$b"
done
else
die "reference repository '$reference' is not a local directory."
fi

View File

@@ -528,6 +528,7 @@ else
rloga='commit (initial)'
current=''
fi
set_reflog_action "$rloga"
if test -z "$no_edit"
then
@@ -602,7 +603,7 @@ then
fi &&
commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
git-update-ref -m "$rloga: $rlogm" HEAD $commit "$current" &&
git-update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
if test -f "$NEXT_INDEX"
then

View File

@@ -15,9 +15,9 @@ unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
die "GIT_DIR is not defined or is unreadable";
}
our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m );
our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m );
getopts('hpvcfam:');
getopts('hPpvcfam:');
$opt_h && usage();
@@ -89,7 +89,7 @@ if ($parent) {
last;
}; # found it
}
die "Did not find $parent in the parents for this commit!" if !$found;
die "Did not find $parent in the parents for this commit!" if !$found and !$opt_P;
} else { # we don't have a parent from the cmdline...
if (@parents == 1) { # it's safe to get it from the commit
$parent = $parents[0];

View File

@@ -85,7 +85,35 @@ sub write_author_info($) {
close ($f);
}
getopts("haivmkuo:d:p:C:z:s:M:P:A:S:L:") or usage();
# convert getopts specs for use by git-repo-config
sub read_repo_config {
# Split the string between characters, unless there is a ':'
# So "abc:de" becomes ["a", "b", "c:", "d", "e"]
my @opts = split(/ *(?!:)/, shift);
foreach my $o (@opts) {
my $key = $o;
$key =~ s/://g;
my $arg = 'git-repo-config';
$arg .= ' --bool' if ($o !~ /:$/);
chomp(my $tmp = `$arg --get cvsimport.$key`);
if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
no strict 'refs';
my $opt_name = "opt_" . $key;
if (!$$opt_name) {
$$opt_name = $tmp;
}
}
}
if (@ARGV == 0) {
chomp(my $module = `git-repo-config --get cvsimport.module`);
push(@ARGV, $module);
}
}
my $opts = "haivmkuo:d:p:C:z:s:M:P:A:S:L:";
read_repo_config($opts);
getopts($opts) or usage();
usage if $opt_h;
@ARGV <= 1 or usage();

View File

@@ -253,23 +253,10 @@ if test "$tags"
then
taglist=`IFS=' ' &&
echo "$ls_remote_result" |
git-show-ref --exclude-existing=refs/tags/ |
while read sha1 name
do
case "$sha1" in
fail)
exit 1
esac
case "$name" in
*^*) continue ;;
refs/tags/*) ;;
*) continue ;;
esac
if git-check-ref-format "$name"
then
echo ".${name}:${name}"
else
echo >&2 "warning: tag ${name} ignored"
fi
echo ".${name}:${name}"
done` || exit
if test "$#" -gt 1
then

View File

@@ -22,6 +22,14 @@ do
shift
done
case "$(git config --get gc.packrefs)" in
notbare|"")
test $(is_bare_repository) = true || pack_refs=true;;
*)
pack_refs=$(git config --bool --get gc.packrefs)
esac
test "true" != "$pack_refs" ||
git-pack-refs --prune &&
git-reflog expire --all &&
git-repack -a -d -l &&

3
git-gui/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
GIT-VERSION-FILE
git-citool
git-gui

77
git-gui/GIT-VERSION-GEN Executable file
View File

@@ -0,0 +1,77 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
DEF_VER=0.6.GITGUI
LF='
'
tree_search ()
{
head=$1
tree=$2
for p in $(git rev-list --parents --max-count=1 $head 2>/dev/null)
do
test $tree = $(git rev-parse $p^{tree} 2>/dev/null) &&
vn=$(git describe --abbrev=4 $p 2>/dev/null) &&
case "$vn" in
gitgui-[0-9]*) echo $vn; break;;
esac
done
}
# We may be a subproject, so try looking for the merge
# commit that supplied this directory content if we are
# not at the toplevel. We probably will always be the
# second parent in the commit, but we shouldn't rely on
# that fact.
#
# If we are at the toplevel or the merge assumption fails
# try looking for a gitgui-* tag, or fallback onto the
# distributed version file.
if prefix="$(git rev-parse --show-prefix 2>/dev/null)"
test -n "$prefix" &&
head=$(git rev-list --max-count=1 HEAD -- . 2>/dev/null) &&
tree=$(git rev-parse --verify "HEAD:$prefix" 2>/dev/null) &&
VN=$(tree_search $head $tree)
case "$VN" in
gitgui-[0-9]*) : happy ;;
*) (exit 1) ;;
esac
then
VN=$(echo "$VN" | sed -e 's/^gitgui-//;s/-/./g');
elif VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
case "$VN" in
gitgui-[0-9]*) : happy ;;
*) (exit 1) ;;
esac
then
VN=$(echo "$VN" | sed -e 's/^gitgui-//;s/-/./g');
elif test -f version
then
VN=$(cat version) || VN="$DEF_VER"
else
VN="$DEF_VER"
fi
dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty=
case "$dirty" in
'')
;;
*)
VN="$VN-dirty" ;;
esac
if test -r $GVF
then
VC=$(sed -e 's/^GITGUI_VERSION = //' <$GVF)
else
VC=unset
fi
test "$VN" = "$VC" || {
echo >&2 "GITGUI_VERSION = $VN"
echo "GITGUI_VERSION = $VN" >$GVF
}

56
git-gui/Makefile Normal file
View File

@@ -0,0 +1,56 @@
all::
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
@$(SHELL_PATH) ./GIT-VERSION-GEN
-include GIT-VERSION-FILE
SCRIPT_SH = git-gui.sh
GITGUI_BUILT_INS = git-citool
ALL_PROGRAMS = $(GITGUI_BUILT_INS) $(patsubst %.sh,%,$(SCRIPT_SH))
ifndef SHELL_PATH
SHELL_PATH = /bin/sh
endif
ifndef gitexecdir
gitexecdir := $(shell git --exec-path)
endif
ifndef INSTALL
INSTALL = install
endif
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
rm -f $@ $@+
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \
$@.sh >$@+
chmod +x $@+
mv $@+ $@
$(GITGUI_BUILT_INS): git-gui
rm -f $@ && ln git-gui $@
# These can record GITGUI_VERSION
$(patsubst %.sh,%,$(SCRIPT_SH)): GIT-VERSION-FILE
all:: $(ALL_PROGRAMS)
install: all
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
$(INSTALL) git-gui '$(DESTDIR_SQ)$(gitexecdir_SQ)'
$(foreach p,$(GITGUI_BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
dist-version:
@mkdir -p $(TARDIR)
@echo $(GITGUI_VERSION) > $(TARDIR)/version
clean::
rm -f $(ALL_PROGRAMS) GIT-VERSION-FILE
.PHONY: all install dist-version clean
.PHONY: .FORCE-GIT-VERSION-FILE

44
git-gui/TODO Normal file
View File

@@ -0,0 +1,44 @@
Items outstanding:
* Add file to .gitignore or info/excludes.
* Populate the pull menu with local branches.
* Make use of the new default merge data stored in repo-config.
* Checkout a different local branch.
* Push any local branch to a remote branch.
* Merge any local branches through a real merge UI.
* Allow user to define keyboard shortcuts for frequently used fetch
or merge operations. Or maybe just define a keyboard shortcut
for default fetch/default merge of current branch is enough;
but I do know a few users who merge a couple of common branches
also into the same branch so one default isn't quite enough.
* Better organize fetch/push/pull console windows.
* Clone UI (to download a new repository).
* Remotes editor (for .git/config format only).
* Show a shortlog of the last couple of commits in the main window,
to give the user warm fuzzy feelings that we have their data
saved. Actually this may be the set of commits not yet in
the upstream (aka default merge branch remote repository).
* GUI configuration editor for options listed in
git.git/Documentation/config.txt. Ideally this would
parse that file and generate the options dialog from
the documentation itself, and include the help text
from the documentation as part of the UI somehow.
Known bugs:
* git-gui sometimes just closes on Windows with no error message.
I'm not sure what the problem is here. I suspect the wish
process is just terminating due to a segfault or something,
as the do_quit proc in git-gui doesn't run. It often seems to
occur while writing a commit message in the buffer. Odd.

5924
git-gui/git-gui.sh Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2005 Junio C Hamano
#
# Resolve two trees, using enhancd multi-base read-tree.
# Resolve two trees, using enhanced multi-base read-tree.
# The first parameters up to -- are merge bases; the rest are heads.
bases= head= remotes= sep_seen=

View File

@@ -174,12 +174,8 @@ canon_refs_list_for_fetch () {
else
for merge_branch in $merge_branches
do
if test "$remote" = "$merge_branch" ||
test "$local" = "$merge_branch"
then
dot_prefix=
break
fi
[ "$remote" = "$merge_branch" ] &&
dot_prefix= && break
done
fi
case "$remote" in

View File

@@ -59,7 +59,7 @@ if ! [ -d "$QUILT_PATCHES" ] ; then
exit 1
fi
# Temporay directories
# Temporary directories
tmp_dir=.dotest
tmp_msg="$tmp_dir/msg"
tmp_patch="$tmp_dir/patch"

View File

@@ -45,6 +45,7 @@ do_merge=
dotest=$GIT_DIR/.dotest-merge
prec=4
verbose=
git_am_opt=
continue_merge () {
test -n "$prev_head" || die "prev_head must be defined"
@@ -213,6 +214,10 @@ do
-v|--verbose)
verbose=t
;;
-C*)
git_am_opt=$1
shift
;;
-*)
usage
;;
@@ -322,7 +327,7 @@ fi
if test -z "$do_merge"
then
git-format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG"
exit $?
fi

View File

@@ -130,7 +130,7 @@ sub update_ls_remote {
$info->{'LS_REMOTE'} = \@ref;
}
sub show_wildcard_mapping {
sub list_wildcard_mapping {
my ($forced, $ours, $ls) = @_;
my %refs;
for (@$ls) {
@@ -156,25 +156,14 @@ sub show_wildcard_mapping {
push @tracked, $_;
}
}
if (@new) {
print " New remote branches (next fetch will store in remotes/$ours)\n";
print " @new\n";
}
if (@stale) {
print " Stale tracking branches in remotes/$ours (you'd better remove them)\n";
print " @stale\n";
}
if (@tracked) {
print " Tracked remote branches\n";
print " @tracked\n";
}
return \@new, \@stale, \@tracked;
}
sub show_mapping {
sub list_mapping {
my ($name, $info) = @_;
my $fetch = $info->{'FETCH'};
my $ls = $info->{'LS_REMOTE'};
my (@stale, @tracked);
my (@new, @stale, @tracked);
for (@$fetch) {
next unless (/(\+)?([^:]+):(.*)/);
@@ -182,7 +171,11 @@ sub show_mapping {
if ($theirs eq 'refs/heads/*' &&
$ours =~ /^refs\/remotes\/(.*)\/\*$/) {
# wildcard mapping
show_wildcard_mapping($forced, $1, $ls);
my ($w_new, $w_stale, $w_tracked)
= list_wildcard_mapping($forced, $1, $ls);
push @new, @$w_new;
push @stale, @$w_stale;
push @tracked, @$w_tracked;
}
elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
@@ -196,13 +189,40 @@ sub show_mapping {
}
}
}
if (@stale) {
print " Stale tracking branches in remotes/$name (you'd better remove them)\n";
print " @stale\n";
return \@new, \@stale, \@tracked;
}
sub show_mapping {
my ($name, $info) = @_;
my ($new, $stale, $tracked) = list_mapping($name, $info);
if (@$new) {
print " New remote branches (next fetch will store in remotes/$name)\n";
print " @$new\n";
}
if (@tracked) {
if (@$stale) {
print " Stale tracking branches in remotes/$name (use 'git remote prune')\n";
print " @$stale\n";
}
if (@$tracked) {
print " Tracked remote branches\n";
print " @tracked\n";
print " @$tracked\n";
}
}
sub prune_remote {
my ($name, $ls_remote) = @_;
if (!exists $remote->{$name}) {
print STDERR "No such remote $name\n";
return;
}
my $info = $remote->{$name};
update_ls_remote($ls_remote, $info);
my ($new, $stale, $tracked) = list_mapping($name, $info);
my $prefix = "refs/remotes/$name";
foreach my $to_prune (@$stale) {
my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune");
$git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]);
}
}
@@ -233,14 +253,30 @@ sub show_remote {
}
sub add_remote {
my ($name, $url) = @_;
my ($name, $url, $opts) = @_;
if (exists $remote->{$name}) {
print STDERR "remote $name already exists.\n";
exit(1);
}
$git->command('config', "remote.$name.url", $url);
$git->command('config', "remote.$name.fetch",
"+refs/heads/*:refs/remotes/$name/*");
my $track = $opts->{'track'} || ["*"];
for (@$track) {
$git->command('config', '--add', "remote.$name.fetch",
"+refs/heads/$_:refs/remotes/$name/$_");
}
if ($opts->{'fetch'}) {
$git->command('fetch', $name);
}
if (exists $opts->{'master'}) {
$git->command('symbolic-ref', "refs/remotes/$name/HEAD",
"refs/remotes/$name/$opts->{'master'}");
}
}
sub add_usage {
print STDERR "Usage: git remote add [-f] [-t track]* [-m master] <name> <url>\n";
exit(1);
}
if (!@ARGV) {
@@ -267,16 +303,62 @@ elsif ($ARGV[0] eq 'show') {
show_remote($ARGV[$i], $ls_remote);
}
}
elsif ($ARGV[0] eq 'add') {
if (@ARGV != 3) {
print STDERR "Usage: git remote add <name> <url>\n";
elsif ($ARGV[0] eq 'prune') {
my $ls_remote = 1;
my $i;
for ($i = 1; $i < @ARGV; $i++) {
if ($ARGV[$i] eq '-n') {
$ls_remote = 0;
}
else {
last;
}
}
if ($i >= @ARGV) {
print STDERR "Usage: git remote prune <remote>\n";
exit(1);
}
add_remote($ARGV[1], $ARGV[2]);
for (; $i < @ARGV; $i++) {
prune_remote($ARGV[$i], $ls_remote);
}
}
elsif ($ARGV[0] eq 'add') {
my %opts = ();
while (1 < @ARGV && $ARGV[1] =~ /^-/) {
my $opt = $ARGV[1];
shift @ARGV;
if ($opt eq '-f' || $opt eq '--fetch') {
$opts{'fetch'} = 1;
next;
}
if ($opt eq '-t' || $opt eq '--track') {
if (@ARGV < 1) {
add_usage();
}
$opts{'track'} ||= [];
push @{$opts{'track'}}, $ARGV[1];
shift @ARGV;
next;
}
if ($opt eq '-m' || $opt eq '--master') {
if ((@ARGV < 1) || exists $opts{'master'}) {
add_usage();
}
$opts{'master'} = $ARGV[1];
shift @ARGV;
next;
}
add_usage();
}
if (@ARGV != 3) {
add_usage();
}
add_remote($ARGV[1], $ARGV[2], \%opts);
}
else {
print STDERR "Usage: git remote\n";
print STDERR " git remote add <name> <url>\n";
print STDERR " git remote show <name>\n";
print STDERR " git remote prune <name>\n";
exit(1);
}

View File

@@ -54,6 +54,8 @@ do
shift
done
set_reflog_action "$me"
test "$me,$replay" = "revert,t" && usage
case "$no_commit" in

View File

@@ -48,7 +48,8 @@ cd_to_toplevel () {
}
require_work_tree () {
test $(is_bare_repository) = false ||
test $(is_bare_repository) = false &&
test $(git-rev-parse --is-inside-git-dir) = false ||
die "fatal: $0 cannot be used without a working tree."
}

View File

@@ -681,7 +681,7 @@ sub show_log {
process_commit($_, $r_min, $r_max) foreach reverse @k;
}
out:
eval { command_close_pipe($log) };
close $log;
print '-' x72,"\n" unless $_incremental || $_oneline;
}
@@ -1475,7 +1475,7 @@ sub map_tree_joins {
$seen{$commit} = 1;
}
}
eval { command_close_pipe($pipe) };
close $pipe;
}
}
@@ -1610,7 +1610,7 @@ sub read_repo_config {
@$v = @tmp if @tmp;
} else {
chomp(my $tmp = `$arg --get svn.$key`);
if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) {
if ($tmp && !($arg =~ / --bool/ && $tmp eq 'false')) {
$$v = $tmp;
}
}
@@ -1669,7 +1669,7 @@ sub write_grafts {
last unless /^\S/;
}
}
eval { command_close_pipe($ch) }; # breaking the pipe
close $ch; # breaking the pipe
# if real parents are the only ones in the grafts, drop it
next if join(' ',sort keys %$p) eq join(' ',sort keys %x);
@@ -1766,7 +1766,7 @@ sub get_commit_time {
} elsif ($tz =~ s/^\-//) {
$s -= tz_to_s_offset($tz);
}
eval { command_close_pipe($fh) };
close $fh;
return $s;
}
die "Can't get commit time for commit: $cmt\n";
@@ -2846,7 +2846,7 @@ sub rmdirs {
delete $rm->{join '/', @dn};
}
unless (%$rm) {
eval { command_close_pipe($fh) };
close $fh;
return;
}
}

24
git.c
View File

@@ -163,6 +163,16 @@ static int handle_alias(int *argcp, const char ***argv)
alias_command = (*argv)[0];
git_config(git_alias_config);
if (alias_string) {
if (alias_string[0] == '!') {
trace_printf("trace: alias to shell cmd: %s => %s\n",
alias_command, alias_string + 1);
ret = system(alias_string + 1);
if (ret >= 0 && WIFEXITED(ret) &&
WEXITSTATUS(ret) != 127)
exit(WEXITSTATUS(ret));
die("Failed to run '%s' when expanding alias '%s'\n",
alias_string + 1, alias_command);
}
count = split_cmdline(alias_string, &new_argv);
option_count = handle_options(&new_argv, &count);
memmove(new_argv - option_count, new_argv,
@@ -313,8 +323,9 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
prefix = setup_git_directory();
if (p->option & USE_PAGER)
setup_pager();
if ((p->option & NOT_BARE) && is_bare_repository())
die("%s cannot be used in a bare git directory", cmd);
if ((p->option & NOT_BARE) &&
(is_bare_repository() || is_inside_git_dir()))
die("%s must be run in a work tree", cmd);
trace_argv_printf(argv, argc, "trace: built-in: git");
exit(p->fn(argc, argv, prefix));
@@ -410,8 +421,15 @@ int main(int argc, const char **argv, char **envp)
done_alias = 1;
}
if (errno == ENOENT)
if (errno == ENOENT) {
if (done_alias) {
fprintf(stderr, "Expansion of alias '%s' failed; "
"'%s' is not a git-command\n",
cmd, argv[0]);
exit(1);
}
help_unknown_cmd(cmd);
}
fprintf(stderr, "Failed to run command '%s': %s\n",
cmd, strerror(errno));

View File

@@ -9,15 +9,12 @@ URL: http://kernel.org/pub/software/scm/git/
Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, perl-Git
Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, git-gui, perl-Git
%description
This is a stupid (but extremely fast) directory content manager. It
doesn't do a whole lot, but what it _does_ do is track directory
contents efficiently. It is intended to be the base of an efficient,
distributed source code management system. This package includes
rudimentary tools that can be used as a SCM, but you should look
elsewhere for tools for ordinary humans layered on top of this.
Git is a fast, scalable, distributed revision control system with an
unusually rich command set that provides both high-level operations
and full access to internals.
This is a dummy package which brings in all subpackages.
@@ -26,12 +23,9 @@ Summary: Core git tools
Group: Development/Tools
Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat
%description core
This is a stupid (but extremely fast) directory content manager. It
doesn't do a whole lot, but what it _does_ do is track directory
contents efficiently. It is intended to be the base of an efficient,
distributed source code management system. This package includes
rudimentary tools that can be used as a SCM, but you should look
elsewhere for tools for ordinary humans layered on top of this.
Git is a fast, scalable, distributed revision control system with an
unusually rich command set that provides both high-level operations
and full access to internals.
These are the core tools with minimal dependencies.
@@ -63,6 +57,13 @@ Requires: git-core = %{version}-%{release}
%description email
Git tools for sending email.
%package gui
Summary: Git GUI tool
Group: Development/Tools
Requires: git-core = %{version}-%{release}, tk >= 8.4
%description gui
Git GUI tool
%package -n gitk
Summary: Git revision tree visualiser ('gitk')
Group: Development/Tools
@@ -89,17 +90,18 @@ make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_OWN_SUBPROCESS_PY=YesPlease \
%install
rm -rf $RPM_BUILD_ROOT
make %{_smp_mflags} DESTDIR=$RPM_BUILD_ROOT WITH_OWN_SUBPROCESS_PY=YesPlease \
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
WITH_OWN_SUBPROCESS_PY=YesPlease \
prefix=%{_prefix} mandir=%{_mandir} INSTALLDIRS=vendor \
install %{!?_without_docs: install-doc}
find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
%if %{!?_without_docs:1}0
(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
%else
rm -rf $RPM_BUILD_ROOT%{_mandir}
%endif
@@ -138,6 +140,16 @@ rm -rf $RPM_BUILD_ROOT
%{!?_without_docs: %{_mandir}/man1/*email*.1*}
%{!?_without_docs: %doc Documentation/*email*.html }
%files gui
%defattr(-,root,root)
%{_bindir}/git-gui
%{_bindir}/git-citool
# Not Yet...
# %{!?_without_docs: %{_mandir}/man1/git-gui.1}
# %{!?_without_docs: %doc Documentation/git-gui.html}
# %{!?_without_docs: %{_mandir}/man1/git-citool.1}
# %{!?_without_docs: %doc Documentation/git-citool.html}
%files -n gitk
%defattr(-,root,root)
%doc Documentation/*gitk*.txt
@@ -155,6 +167,12 @@ rm -rf $RPM_BUILD_ROOT
%{!?_without_docs: %doc Documentation/*.html }
%changelog
* Mon Feb 13 2007 Nicolas Pitre <nico@cam.org>
- Update core package description (Git isn't as stupid as it used to be)
* Mon Feb 12 2007 Junio C Hamano <junkio@cox.net>
- Add git-gui and git-citool.
* Mon Nov 14 2005 H. Peter Anvin <hpa@zytor.com> 0.99.9j-1
- Change subpackage names to git-<name> instead of git-core-<name>
- Create empty root package which brings in all subpackages

Some files were not shown because too many files have changed in this diff Show More