slow, such as Microsoft Windows. See gitlink:git-update-index[1].
False by default.
-core.onlyUseSymrefs::
- Always use the "symref" format instead of symbolic links for HEAD
- and other symbolic reference files. True by default.
+core.preferSymlinkRefs::
+ Instead of the default "symref" format for HEAD
+ and other symbolic reference files, use symbolic links.
+ This is sometimes needed to work with old scripts that
+ expect HEAD to be a symbolic link.
core.repositoryFormatVersion::
Internal variable identifying the repository format and layout
The first two lines indicate that it is showing the two branches
and the first line of the commit log message from their
top-of-the-tree commits, you are currently on `master` branch
-(notice the asterisk `*` character), and the first column for
+(notice the asterisk `\*` character), and the first column for
the later output lines is used to show commits contained in the
`master` branch, and the second column for the `mybranch`
branch. Three commits are shown along with their log messages.
-v::
Be verbose.
---::
+\--::
This option can be used to separate command-line options from
the list of files, (useful when filenames might be mistaken
for command-line options).
Only meaningful with `--stdin`; paths are separated with
NUL character instead of LF.
---::
+\--::
Do not interpret any more arguments as options.
The order of the flags used to matter, but not anymore.
DESCRIPTION
-----------
-Each commit between the fork-point and <head> is examined, and compared against
-the change each commit between the fork-point and <upstream> introduces.
-Commits already included in upstream are prefixed with '-' (meaning "drop from
-my local pull"), while commits missing from upstream are prefixed with '+'
-(meaning "add to the updated upstream").
+The changeset (or "diff") of each commit between the fork-point and <head>
+is compared against each commit between the fork-point and <upstream>.
+
+Every commit with a changeset that doesn't exist in the other branch
+has its id (sha1) reported, prefixed by a symbol. Those existing only
+in the <upstream> branch are prefixed with a minus (-) sign, and those
+that only exist in the <head> branch are prefixed with a plus (+) symbol.
+
+Because git-cherry compares the changeset rather than the commit id
+(sha1), you can use git-cherry to find out if a commit you made locally
+has been applied <upstream> under a different commit id. For example,
+this will happen if you're feeding patches <upstream> via email rather
+than pushing or pulling commits directly.
+
OPTIONS
-------
is not allowed.
Examples
-~~~~~~~~
+--------
Clone from upstream::
+
index and the latest commit does not match on the
specified paths to avoid confusion.
---::
+\--::
Do not interpret any more arguments as options.
<file>...::
SYNOPSIS
--------
-'git-count-objects'
+'git-count-objects' [-v]
DESCRIPTION
-----------
This counts the number of unpacked object files and disk space consumed by
them, to help you decide when it is a good time to repack.
+
+OPTIONS
+-------
+-v::
+ In addition to the number of loose objects and disk
+ space consumed, it reports the number of in-pack
+ objects, and number of objects that can be removed by
+ running `git-prune-packed`.
+
+
Author
------
Written by Junio C Hamano <junkio@cox.net>
Furthermore, it lists only files which were modified
from all parents.
--cc::
+--cc::
This flag changes the way a merge commit patch is displayed,
in a similar way to the '-c' option. It implies the '-c'
and '-p' options and further compresses the patch output
git-imap-send requires the following values in the repository
configuration file (shown with examples):
+..........................
[imap]
Folder = "INBOX.Drafts"
[imap]
Host = imap.server.com
User = bob
- Password = pwd
+ Pass = pwd
Port = 143
+..........................
BUGS
Show all commits since version 'v2.6.12' that changed any file
in the include/scsi or drivers/scsi subdirectories
-git log --since="2 weeks ago" -- gitk::
+git log --since="2 weeks ago" \-- gitk::
Show the changes during the last two weeks to the file 'gitk'.
The "--" is necessary to avoid confusion with the *branch* named
lines, show only handful hexdigits prefix.
Non default number of digits can be specified with --abbrev=<n>.
---::
+\--::
Do not interpret any more arguments as options.
<file>::
SYNOPSIS
--------
-'git-merge-index' [-o] [-q] <merge-program> (-a | -- | <file>\*)
+'git-merge-index' [-o] [-q] <merge-program> (-a | \-- | <file>\*)
DESCRIPTION
-----------
OPTIONS
-------
---::
+\--::
Do not interpret any more arguments as options.
-a::
------------
% git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a
+33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99^0~940
------------
Now you are wiser, because you know that it happened 940 revisions before v0.99.
Do not remove anything; just report what it would
remove.
---::
+\--::
Do not interpret any more arguments as options.
<head>...::
NAME
----
-git-rebase - Rebase local commits to new upstream head
+git-rebase - Rebase local commits to a new head
SYNOPSIS
--------
'git-rebase' [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' --continue
+
+'git-rebase' --abort
+
DESCRIPTION
-----------
-git-rebase applies to <upstream> (or optionally to <newbase>) commits
-from <branch> that do not appear in <upstream>. When <branch> is not
-specified it defaults to the current branch (HEAD).
+git-rebase replaces <branch> with a new branch of the same name. When
+the --onto option is provided the new branch starts out with a HEAD equal
+to <newbase>, otherwise it is equal to <upstream>. It then attempts to
+create a new commit for each commit from the original <branch> that does
+not exist in the <upstream> branch.
-When git-rebase is complete, <branch> will be updated to point to the
-newly created line of commit objects, so the previous line will not be
-accessible unless there are other references to it already.
+It is possible that a merge failure will prevent this process from being
+completely automatic. You will have to resolve any such merge failure
+and run `git rebase --continue`. If you can not resolve the merge
+failure, running `git rebase --abort` will restore the original <branch>
+and remove the working files found in the .dotest directory.
+
+Note that if <branch> is not specified on the command line, the currently
+checked out branch is used.
Assume the following history exists and the current branch is "topic":
+------------
A---B---C topic
/
D---E---F---G master
+------------
From this point, the result of either of the following commands:
+
git-rebase master
git-rebase master topic
would be:
+------------
A'--B'--C' topic
/
D---E---F---G master
+------------
While, starting from the same point, the result of either of the following
commands:
would be:
+------------
A'--B'--C' topic
/
D---E---F---G master
+------------
In case of conflict, git-rebase will stop at the first problematic commit
-and leave conflict markers in the tree. After resolving the conflict manually
-and updating the index with the desired resolution, you can continue the
-rebasing process with
+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
+file you edit, you need to tell git that the conflict has been resolved,
+typically this would be done with
+
+
+ git update-index <filename>
+
+
+After resolving the conflict manually and updating the index with the
+desired resolution, you can continue the rebasing process with
+
+
+ git rebase --continue
- git am --resolved --3way
Alternatively, you can undo the git-rebase with
- git reset --hard ORIG_HEAD
- rm -r .dotest
+
+ git rebase --abort
OPTIONS
-------
<branch>::
Working branch; defaults to HEAD.
+--continue::
+ Restart the rebasing process after having resolved a merge conflict.
+
+--abort::
+ Restore the original branch and abort the rebase operation.
+
+NOTES
+-----
+When you rebase a branch, you are changing its history in a way that
+will cause problems for anyone who already has a copy of the branch
+in their repository and tries to pull updates from you. You should
+understand the implications of using 'git rebase' on a repository that
+you share.
+
+When the git rebase command is run, it will first execute a "pre-rebase"
+hook if one exists. You can use this hook to do sanity checks and
+reject the rebase if it isn't appropriate. Please see the template
+pre-rebase hook script for an example.
+
+You must be in the top directory of your project to start (or continue)
+a rebase. Upon completion, <branch> will be the current branch.
+
Author
------
Written by Junio C Hamano <junkio@cox.net>
-d::
After packing, if the newly created packs make some
existing packs redundant, remove the redundant packs.
+ Also runs gitlink:git-prune-packed[1].
-l::
Pass the `--local` option to `git pack-objects`, see
actually the section and the key separated by a dot, and the value will be
escaped.
-If you want to set/unset an option which can occur on multiple lines, you
-should provide a POSIX regex for the value. If you want to handle the lines
-*not* matching the regex, just prepend a single exclamation mark in front
-(see EXAMPLES).
+If you want to set/unset an option which can occur on multiple
+lines, a POSIX regexp `value_regex` needs to be given. Only the
+existing values that match the regexp are updated or unset. If
+you want to handle the lines that do *not* match the regex, just
+prepend a single exclamation mark in front (see EXAMPLES).
The type specifier can be either '--int' or '--bool', which will make
'git-repo-config' ensure that the variable(s) are of the given type and
a "true" or "false" string for bool). If no type specifier is passed,
no checks or transformations are performed on the value.
-This command will fail if
+This command will fail if:
-. .git/config is invalid,
-. .git/config can not be written to,
+. The .git/config file is invalid,
+. Can not write to .git/config,
. no section was provided,
. the section or key is invalid,
. you try to unset an option which does not exist, or
--replace-all::
Default behaviour is to replace at most one line. This replaces
- all lines matching the key (and optionally the value_regex)
+ all lines matching the key (and optionally the value_regex).
--get::
Get the value for a given key (optionally filtered by a regex
Like get, but does not fail if the number of values for the key
is not exactly one.
+--get-regexp::
+ Like --get-all, but interprets the name as a regular expression.
+
--unset::
Remove the line matching the key from .git/config.
Commit to make the current HEAD.
Examples
-~~~~~~~~
+--------
Undo a commit and redo::
+
--bisect::
Limit output to the one commit object which is roughly halfway
between the included and excluded commits. Thus, if 'git-rev-list
- --bisect foo ^bar ^baz' outputs 'midpoint', the output
- of 'git-rev-list foo ^midpoint' and 'git-rev-list midpoint
- ^bar ^baz' would be of roughly the same length. Finding the change
+ --bisect foo {caret}bar {caret}baz' outputs 'midpoint', the output
+ of 'git-rev-list foo {caret}midpoint' and 'git-rev-list midpoint
+ {caret}bar {caret}baz' would be of roughly the same length.
+ Finding the change
which introduces a regression is thus reduced to a binary search:
repeatedly generate and test new 'midpoint's until the commit chain
is of length one.
-v::
Be verbose.
---::
+\--::
This option can be used to separate command-line options from
the list of files, (useful when filenames might be mistaken
for command-line options).
DESCRIPTION
-----------
-Reads a packed archive (.pack) from the standard input, and
-expands the objects contained in the pack into "one-file
-one-object" format in $GIT_OBJECT_DIRECTORY.
+Read a packed archive (.pack) from the standard input, expanding
+the objects contained within and writing them into the repository in
+"loose" (one object per file) format.
+
+Objects that already exist in the repository will *not* be unpacked
+from the pack-file. Therefore, nothing will be unpacked if you use
+this command on a pack-file that exists within the target repository.
+
+Please see the `git-repack` documentation for options to generate
+new packs and replace existing ones.
OPTIONS
-------
--------
[verse]
'git-update-index'
- [--add] [--remove | --force-remove] [--replace]
- [--refresh [-q] [--unmerged] [--ignore-missing]]
+ [--add] [--remove | --force-remove] [--replace]
+ [--refresh] [-q] [--unmerged] [--ignore-missing]
[--cacheinfo <mode> <object> <file>]\*
[--chmod=(+|-)x]
[--assume-unchanged | --no-assume-unchanged]
- [--really-refresh]
+ [--really-refresh] [--unresolve] [--again]
[--info-only] [--index-info]
[-z] [--stdin]
[--verbose]
filesystem that has very slow lstat(2) system call
(e.g. cifs).
+--again::
+ Runs `git-update-index` itself on the paths whose index
+ entries are different from those from the `HEAD` commit.
+
+--unresolve::
+ Restores the 'unmerged' or 'needs updating' state of a
+ file during a merge if it was cleared by accident.
+
--info-only::
Do not create objects in the object database for all
<file> arguments that follow this flag; just insert
Only meaningful with `--stdin`; paths are separated with
NUL character instead of LF.
---::
+\--::
Do not interpret any more arguments as options.
<file>::
-v::
After verifying the pack, show list of objects contained
in the pack.
---::
+\--::
Do not interpret any more arguments as options.
OUTPUT FORMAT
Show as patches the commits since version 'v2.6.12' that changed
any file in the include/scsi or drivers/scsi subdirectories
-git-whatchanged --since="2 weeks ago" -- gitk::
+git-whatchanged --since="2 weeks ago" \-- gitk::
Show the changes during the last two weeks to the file 'gitk'.
The "--" is necessary to avoid confusion with the *branch* named
Show as the changes since version 'v2.6.12' that changed any
file in the include/scsi or drivers/scsi subdirectories
-gitk --since="2 weeks ago" -- gitk::
+gitk --since="2 weeks ago" \-- gitk::
Show the changes during the last two weeks to the file 'gitk'.
The "--" is necessary to avoid confusion with the *branch* named
-object::
- The unit of storage in git. It is uniquely identified by
- the SHA1 of its contents. Consequently, an object can not
- be changed.
-
-object name::
- The unique identifier of an object. The hash of the object's contents
- using the Secure Hash Algorithm 1 and usually represented by the 40
- character hexadecimal encoding of the hash of the object (possibly
- followed by a white space).
-
-SHA1::
- Synonym for object name.
-
-object identifier::
- Synonym for object name.
-
-hash::
- In git's context, synonym to object name.
+alternate object database::
+ Via the alternates mechanism, a repository can inherit part of its
+ object database from another object database, which is called
+ "alternate".
-object database::
- Stores a set of "objects", and an individual object is identified
- by its object name. The objects usually live in `$GIT_DIR/objects/`.
+bare repository::
+ A bare repository is normally an appropriately named
+ directory with a `.git` suffix that does not have a
+ locally checked-out copy of any of the files under revision
+ control. That is, all of the `git` administrative and
+ control files that would normally be present in the
+ hidden `.git` sub-directory are directly present in
+ the `repository.git` directory instead, and no other files
+ are present and checked out. Usually publishers of public
+ repositories make bare repositories available.
blob object::
Untyped object, e.g. the contents of a file.
-tree object::
- An object containing a list of file names and modes along with refs
- to the associated blob and/or tree objects. A tree is equivalent
- to a directory.
-
-tree::
- Either a working tree, or a tree object together with the
- dependent blob and tree objects (i.e. a stored representation
- of a working tree).
-
-DAG::
- Directed acyclic graph. The commit objects form a directed acyclic
- graph, because they have parents (directed), and the graph of commit
- objects is acyclic (there is no chain which begins and ends with the
- same object).
-
-index::
- A collection of files with stat information, whose contents are
- stored as objects. The index is a stored version of your working
- tree. Truth be told, it can also contain a second, and even a third
- version of a working tree, which are used when merging.
-
-index entry::
- The information regarding a particular file, stored in the index.
- An index entry can be unmerged, if a merge was started, but not
- yet finished (i.e. if the index contains multiple versions of
- that file).
-
-unmerged index:
- An index which contains unmerged index entries.
+branch::
+ A non-cyclical graph of revisions, i.e. the complete history of
+ a particular revision, which is called the branch head. The
+ branch heads are stored in `$GIT_DIR/refs/heads/`.
cache::
Obsolete for: index.
-working tree::
- The set of files and directories currently being worked on,
- i.e. you can work in your working tree without using git at all.
-
-directory::
- The list you get with "ls" :-)
+chain::
+ A list of objects, where each object in the list contains a
+ reference to its successor (for example, the successor of a commit
+ could be one of its parents).
-revision::
- A particular state of files and directories which was stored in
- the object database. It is referenced by a commit object.
+changeset::
+ BitKeeper/cvsps speak for "commit". Since git does not store
+ changes, but states, it really does not make sense to use
+ the term "changesets" with git.
checkout::
The action of updating the working tree to a revision which was
stored in the object database.
+cherry-picking::
+ In SCM jargon, "cherry pick" means to choose a subset of
+ changes out of a series of changes (typically commits)
+ and record them as a new series of changes on top of
+ different codebase. In GIT, this is performed by
+ "git cherry-pick" command to extract the change
+ introduced by an existing commit and to record it based
+ on the tip of the current branch as a new commit.
+
+clean::
+ A working tree is clean, if it corresponds to the revision
+ referenced by the current head. Also see "dirty".
+
commit::
As a verb: The action of storing the current state of the index in the
object database. The result is a revision.
tree object which corresponds to the top directory of the
stored revision.
-parent::
- A commit object contains a (possibly empty) list of the logical
- predecessor(s) in the line of development, i.e. its parents.
+core git::
+ Fundamental data structures and utilities of git. Exposes only
+ limited source code management tools.
-changeset::
- BitKeeper/cvsps speak for "commit". Since git does not store
- changes, but states, it really does not make sense to use
- the term "changesets" with git.
+DAG::
+ Directed acyclic graph. The commit objects form a directed acyclic
+ graph, because they have parents (directed), and the graph of commit
+ objects is acyclic (there is no chain which begins and ends with the
+ same object).
-clean::
- A working tree is clean, if it corresponds to the revision
- referenced by the current head.
+dircache::
+ You are *waaaaay* behind.
dirty::
A working tree is said to be dirty if it contains modifications
which have not been committed to the current branch.
-head::
- The top of a branch. It contains a ref to the corresponding
- commit object.
+directory::
+ The list you get with "ls" :-)
-branch::
- A non-cyclical graph of revisions, i.e. the complete history of
- a particular revision, which is called the branch head. The
- branch heads are stored in `$GIT_DIR/refs/heads/`.
+ent::
+ Favorite synonym to "tree-ish" by some total geeks. See
+ `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
+ explanation.
-master::
- The default branch. Whenever you create a git repository, a branch
- named "master" is created, and becomes the active branch. In most
- cases, this contains the local development.
+fast forward::
+ A fast-forward is a special type of merge where you have
+ a revision and you are "merging" another branch's changes
+ that happen to be a descendant of what you have.
+ In such these cases, you do not make a new merge commit but
+ instead just update to his revision. This will happen
+ frequently on a tracking branch of a remote repository.
-origin::
- The default upstream branch. Most projects have one upstream
- project which they track, and by default 'origin' is used for
- that purpose. New updates from upstream will be fetched into
- this branch; you should never commit to it yourself.
+fetch::
+ Fetching a branch means to get the branch's head ref from a
+ remote repository, to find out which objects are missing from
+ the local object database, and to get them, too.
-ref::
- A 40-byte hex representation of a SHA1 pointing to a particular
- object. These may be stored in `$GIT_DIR/refs/`.
+file system::
+ Linus Torvalds originally designed git to be a user space file
+ system, i.e. the infrastructure to hold files and directories.
+ That ensured the efficiency and speed of git.
+
+git archive::
+ Synonym for repository (for arch people).
+
+hash::
+ In git's context, synonym to object name.
+
+head::
+ The top of a branch. It contains a ref to the corresponding
+ commit object.
head ref::
A ref pointing to a head. Often, this is abbreviated to "head".
Head refs are stored in `$GIT_DIR/refs/heads/`.
-tree-ish::
- A ref pointing to either a commit object, a tree object, or a
- tag object pointing to a tag or commit or tree object.
+hook::
+ During the normal execution of several git commands,
+ call-outs are made to optional scripts that allow
+ a developer to add functionality or checking.
+ Typically, the hooks allow for a command to be pre-verified
+ and potentially aborted, and allow for a post-notification
+ after the operation is done.
+ The hook scripts are found in the `$GIT_DIR/hooks/` directory,
+ and are enabled by simply making them executable.
-ent::
- Favorite synonym to "tree-ish" by some total geeks. See
- `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
- explanation.
+index::
+ A collection of files with stat information, whose contents are
+ stored as objects. The index is a stored version of your working
+ tree. Truth be told, it can also contain a second, and even a third
+ version of a working tree, which are used when merging.
-tag object::
- An object containing a ref pointing to another object, which can
- contain a message just like a commit object. It can also
- contain a (PGP) signature, in which case it is called a "signed
- tag object".
+index entry::
+ The information regarding a particular file, stored in the index.
+ An index entry can be unmerged, if a merge was started, but not
+ yet finished (i.e. if the index contains multiple versions of
+ that file).
-tag::
- A ref pointing to a tag or commit object. In contrast to a head,
- a tag is not changed by a commit. Tags (not tag objects) are
- stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with
- a Lisp tag (which is called object type in git's context).
- A tag is most typically used to mark a particular point in the
- commit ancestry chain.
+master::
+ The default development branch. Whenever you create a git
+ repository, a branch named "master" is created, and becomes
+ the active branch. In most cases, this contains the local
+ development, though that is purely conventional and not required.
merge::
To merge branches means to try to accumulate the changes since a
merge uses heuristics to accomplish that. Evidently, an automatic
merge can fail.
-octopus::
- To merge more than two branches. Also denotes an intelligent
- predator.
+object::
+ The unit of storage in git. It is uniquely identified by
+ the SHA1 of its contents. Consequently, an object can not
+ be changed.
-resolve::
- The action of fixing up manually what a failed automatic merge
- left behind.
+object database::
+ Stores a set of "objects", and an individual object is identified
+ by its object name. The objects usually live in `$GIT_DIR/objects/`.
-rewind::
- To throw away part of the development, i.e. to assign the head to
- an earlier revision.
+object identifier::
+ Synonym for object name.
-rebase::
- To clean a branch by starting from the head of the main line of
- development ("master"), and reapply the (possibly cherry-picked)
- changes from that branch.
+object name::
+ The unique identifier of an object. The hash of the object's contents
+ using the Secure Hash Algorithm 1 and usually represented by the 40
+ character hexadecimal encoding of the hash of the object (possibly
+ followed by a white space).
-repository::
- A collection of refs together with an object database containing
- all objects, which are reachable from the refs, possibly accompanied
- by meta data from one or more porcelains. A repository can
- share an object database with other repositories.
+object type:
+ One of the identifiers "commit","tree","tag" and "blob" describing
+ the type of an object.
-git archive::
- Synonym for repository (for arch people).
+octopus::
+ To merge more than two branches. Also denotes an intelligent
+ predator.
-file system::
- Linus Torvalds originally designed git to be a user space file
- system, i.e. the infrastructure to hold files and directories.
- That ensured the efficiency and speed of git.
+origin::
+ The default upstream tracking branch. Most projects have at
+ least one upstream project which they track. By default
+ 'origin' is used for that purpose. New upstream updates
+ will be fetched into this branch; you should never commit
+ to it yourself.
-alternate object database::
- Via the alternates mechanism, a repository can inherit part of its
- object database from another object database, which is called
- "alternate".
+pack::
+ A set of objects which have been compressed into one file (to save
+ space or to transmit them efficiently).
-reachable::
- An object is reachable from a ref/commit/tree/tag, if there is a
- chain leading from the latter to the former.
+pack index::
+ The list of identifiers, and other information, of the objects in a
+ pack, to assist in efficiently accessing the contents of a pack.
-chain::
- A list of objects, where each object in the list contains a
- reference to its successor (for example, the successor of a commit
- could be one of its parents).
+parent::
+ A commit object contains a (possibly empty) list of the logical
+ predecessor(s) in the line of development, i.e. its parents.
-fetch::
- Fetching a branch means to get the branch's head ref from a
- remote repository, to find out which objects are missing from
- the local object database, and to get them, too.
+pickaxe::
+ The term pickaxe refers to an option to the diffcore routines
+ that help select changes that add or delete a given text string.
+ With the --pickaxe-all option, it can be used to view the
+ full changeset that introduced or removed, say, a particular
+ line of text. See gitlink:git-diff[1].
+
+plumbing::
+ Cute name for core git.
+
+porcelain::
+ Cute name for programs and program suites depending on core git,
+ presenting a high level access to core git. Porcelains expose
+ more of a SCM interface than the plumbing.
pull::
Pulling a branch means to fetch it and merge it.
the remote head ref. If the remote head is not an ancestor to the
local head, the push fails.
-pack::
- A set of objects which have been compressed into one file (to save
- space or to transmit them efficiently).
+reachable::
+ An object is reachable from a ref/commit/tree/tag, if there is a
+ chain leading from the latter to the former.
-pack index::
- The list of identifiers, and other information, of the objects in a
- pack, to assist in efficiently accessing the contents of a pack.
+rebase::
+ To clean a branch by starting from the head of the main line of
+ development ("master"), and reapply the (possibly cherry-picked)
+ changes from that branch.
-core git::
- Fundamental data structures and utilities of git. Exposes only
- limited source code management tools.
+ref::
+ A 40-byte hex representation of a SHA1 or a name that denotes
+ a particular object. These may be stored in `$GIT_DIR/refs/`.
+
+refspec::
+ A refspec is used by fetch and push to describe the mapping
+ between remote ref and local ref. They are combined with
+ a colon in the format <src>:<dst>, preceded by an optional
+ plus sign, +. For example:
+ `git fetch $URL refs/heads/master:refs/heads/origin`
+ means "grab the master branch head from the $URL and store
+ it as my origin branch head".
+ And `git push $URL refs/heads/master:refs/heads/to-upstream`
+ means "publish my master branch head as to-upstream master head
+ at $URL". See also gitlink:git-push[1]
-plumbing::
- Cute name for core git.
+repository::
+ A collection of refs together with an object database containing
+ all objects, which are reachable from the refs, possibly accompanied
+ by meta data from one or more porcelains. A repository can
+ share an object database with other repositories.
-porcelain::
- Cute name for programs and program suites depending on core git,
- presenting a high level access to core git. Porcelains expose
- more of a SCM interface than the plumbing.
+resolve::
+ The action of fixing up manually what a failed automatic merge
+ left behind.
-object type:
- One of the identifiers "commit","tree","tag" and "blob" describing
- the type of an object.
+revision::
+ A particular state of files and directories which was stored in
+ the object database. It is referenced by a commit object.
+
+rewind::
+ To throw away part of the development, i.e. to assign the head to
+ an earlier revision.
SCM::
Source code management (tool).
-dircache::
- You are *waaaaay* behind.
+SHA1::
+ Synonym for object name.
+
+topic branch::
+ A regular git branch that is used by a developer to
+ identify a conceptual line of development. Since branches
+ are very easy and inexpensive, it is often desirable to
+ have several small branches that each contain very well
+ defined concepts or small incremental yet related changes.
+
+tracking branch::
+ A regular git branch that is used to follow changes from
+ another repository. A tracking branch should not contain
+ direct modifications or have local commits made to it.
+ A tracking branch can usually be identified as the
+ right-hand-side ref in a Pull: refspec.
+
+tree object::
+ An object containing a list of file names and modes along with refs
+ to the associated blob and/or tree objects. A tree is equivalent
+ to a directory.
+
+tree::
+ Either a working tree, or a tree object together with the
+ dependent blob and tree objects (i.e. a stored representation
+ of a working tree).
+
+tree-ish::
+ A ref pointing to either a commit object, a tree object, or a
+ tag object pointing to a tag or commit or tree object.
+
+tag object::
+ An object containing a ref pointing to another object, which can
+ contain a message just like a commit object. It can also
+ contain a (PGP) signature, in which case it is called a "signed
+ tag object".
+
+tag::
+ A ref pointing to a tag or commit object. In contrast to a head,
+ a tag is not changed by a commit. Tags (not tag objects) are
+ stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with
+ a Lisp tag (which is called object type in git's context).
+ A tag is most typically used to mark a particular point in the
+ commit ancestry chain.
+
+unmerged index:
+ An index which contains unmerged index entries.
+
+working tree::
+ The set of files and directories currently being worked on,
+ i.e. you can work in your working tree without using git at all.
';
@keys=sort {uc($a) cmp uc($b)} keys %terms;
-$pattern='(\b'.join('\b|\b',reverse @keys).'\b)';
+$pattern='(\b(?<!link:git-)'.join('\b|\b(?<!link:git-)',reverse @keys).'\b)';
foreach $key (@keys) {
$terms{$key}=~s/$pattern/sprintf "<<ref_".no_spaces($1).",$1>>";/eg;
print '[[ref_'.no_spaces($key).']]'.$key."::\n"
#
# Define NO_SETENV if you don't have setenv in the C library.
#
-# Define USE_SYMLINK_HEAD if you want .git/HEAD to be a symbolic link.
-# Don't enable it on Windows.
+# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
+# Enable it on Windows. By default, symrefs are still used.
#
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
SCRIPT_SH = \
git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
- git-count-objects.sh git-diff.sh git-fetch.sh \
+ git-fetch.sh \
git-format-patch.sh git-ls-remote.sh \
git-merge-one-file.sh git-parse-remote.sh \
- git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
+ git-prune.sh git-pull.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
- git-tag.sh git-verify-tag.sh git-whatchanged.sh \
+ git-tag.sh git-verify-tag.sh \
git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh git-grep.sh \
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
- git-cherry-pick git-show git-status
+ git-cherry-pick git-status
# The ones that do not have to link with lcrypto, lz nor xdiff.
SIMPLE_PROGRAMS = \
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
-BUILT_INS = git-log$X
+BUILT_INS = git-log$X git-whatchanged$X git-show$X \
+ git-count-objects$X git-diff$X git-push$X
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
diffcore-delta.o log-tree.o
LIB_OBJS = \
- blob.o commit.o connect.o csum-file.o \
+ blob.o commit.o connect.o csum-file.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
$(DIFF_OBJS)
BUILTIN_OBJS = \
- builtin-log.o builtin-help.o
+ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
NO_D_TYPE_IN_DIRENT = YesPlease
NO_D_INO_IN_DIRENT = YesPlease
NO_STRCASESTR = YesPlease
+ NO_SYMLINK_HEAD = YesPlease
NEEDS_LIBICONV = YesPlease
# There are conflicting reports about this.
# On some boxes NO_MMAP is needed, and not so elsewhere.
ifdef NO_D_INO_IN_DIRENT
ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
endif
+ifdef NO_SYMLINK_HEAD
+ ALL_CFLAGS += -DNO_SYMLINK_HEAD
+endif
ifdef NO_STRCASESTR
COMPAT_CFLAGS += -DNO_STRCASESTR
COMPAT_OBJS += compat/strcasestr.o
git-cherry-pick: git-revert
cp $< $@
-git-show: git-whatchanged
- cp $< $@
-
git-status: git-commit
cp $< $@
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
-git-rev-list$X: rev-list.o $(LIB_FILE)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
- $(LIBS) $(OPENSSL_LIBSSL)
-
init-db.o: init-db.c
$(CC) -c $(ALL_CFLAGS) \
-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
test-delta$X: test-delta.c diff-delta.o patch-delta.o
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
check:
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
#include "cache.h"
#include "quote.h"
#include "blob.h"
+#include "delta.h"
// --check turns on checking that the working tree matches the
// files that are being modified, but doesn't apply the patch
//
static const char *prefix;
static int prefix_length = -1;
+static int newfd = -1;
static int p_value = 1;
static int allow_binary_replacement = 0;
char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode;
int is_rename, is_copy, is_new, is_delete, is_binary;
+#define BINARY_DELTA_DEFLATED 1
+#define BINARY_LITERAL_DEFLATED 2
+ unsigned long deflate_origlen;
int lines_added, lines_deleted;
int score;
struct fragment *fragments;
patch->old_mode != patch->new_mode);
}
+static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+{
+ /* We have read "GIT binary patch\n"; what follows is a line
+ * that says the patch method (currently, either "deflated
+ * literal" or "deflated delta") and the length of data before
+ * deflating; a sequence of 'length-byte' followed by base-85
+ * encoded data follows.
+ *
+ * Each 5-byte sequence of base-85 encodes up to 4 bytes,
+ * and we would limit the patch line to 66 characters,
+ * so one line can fit up to 13 groups that would decode
+ * to 52 bytes max. The length byte 'A'-'Z' corresponds
+ * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
+ * The end of binary is signalled with an empty line.
+ */
+ int llen, used;
+ struct fragment *fragment;
+ char *data = NULL;
+
+ patch->fragments = fragment = xcalloc(1, sizeof(*fragment));
+
+ /* Grab the type of patch */
+ llen = linelen(buffer, size);
+ used = llen;
+ linenr++;
+
+ if (!strncmp(buffer, "delta ", 6)) {
+ patch->is_binary = BINARY_DELTA_DEFLATED;
+ patch->deflate_origlen = strtoul(buffer + 6, NULL, 10);
+ }
+ else if (!strncmp(buffer, "literal ", 8)) {
+ patch->is_binary = BINARY_LITERAL_DEFLATED;
+ patch->deflate_origlen = strtoul(buffer + 8, NULL, 10);
+ }
+ else
+ return error("unrecognized binary patch at line %d: %.*s",
+ linenr-1, llen-1, buffer);
+ buffer += llen;
+ while (1) {
+ int byte_length, max_byte_length, newsize;
+ llen = linelen(buffer, size);
+ used += llen;
+ linenr++;
+ if (llen == 1)
+ break;
+ /* Minimum line is "A00000\n" which is 7-byte long,
+ * and the line length must be multiple of 5 plus 2.
+ */
+ if ((llen < 7) || (llen-2) % 5)
+ goto corrupt;
+ max_byte_length = (llen - 2) / 5 * 4;
+ byte_length = *buffer;
+ if ('A' <= byte_length && byte_length <= 'Z')
+ byte_length = byte_length - 'A' + 1;
+ else if ('a' <= byte_length && byte_length <= 'z')
+ byte_length = byte_length - 'a' + 27;
+ else
+ goto corrupt;
+ /* if the input length was not multiple of 4, we would
+ * have filler at the end but the filler should never
+ * exceed 3 bytes
+ */
+ if (max_byte_length < byte_length ||
+ byte_length <= max_byte_length - 4)
+ goto corrupt;
+ newsize = fragment->size + byte_length;
+ data = xrealloc(data, newsize);
+ if (decode_85(data + fragment->size,
+ buffer + 1,
+ byte_length))
+ goto corrupt;
+ fragment->size = newsize;
+ buffer += llen;
+ size -= llen;
+ }
+ fragment->patch = data;
+ return used;
+ corrupt:
+ return error("corrupt binary patch at line %d: %.*s",
+ linenr-1, llen-1, buffer);
+}
+
static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
{
int hdrsize, patchsize;
"Files ",
NULL,
};
+ static const char git_binary[] = "GIT binary patch\n";
int i;
int hd = hdrsize + offset;
unsigned long llen = linelen(buffer + hd, size - hd);
- if (!memcmp(" differ\n", buffer + hd + llen - 8, 8))
+ if (llen == sizeof(git_binary) - 1 &&
+ !memcmp(git_binary, buffer + hd, llen)) {
+ int used;
+ linenr++;
+ used = parse_binary(buffer + hd + llen,
+ size - hd - llen, patch);
+ if (used)
+ patchsize = used + llen;
+ else
+ patchsize = 0;
+ }
+ else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
for (i = 0; binhdr[i]; i++) {
int len = strlen(binhdr[i]);
if (len < size - hd &&
!memcmp(binhdr[i], buffer + hd, len)) {
+ linenr++;
patch->is_binary = 1;
+ patchsize = llen;
break;
}
}
+ }
/* Empty patch cannot be applied if:
* - it is a binary patch and we do not do binary_replace, or
return offset;
}
-static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+static char *inflate_it(const void *data, unsigned long size,
+ unsigned long inflated_size)
+{
+ z_stream stream;
+ void *out;
+ int st;
+
+ memset(&stream, 0, sizeof(stream));
+
+ stream.next_in = (unsigned char *)data;
+ stream.avail_in = size;
+ stream.next_out = out = xmalloc(inflated_size);
+ stream.avail_out = inflated_size;
+ inflateInit(&stream);
+ st = inflate(&stream, Z_FINISH);
+ if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
+ free(out);
+ return NULL;
+ }
+ return out;
+}
+
+static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
+{
+ unsigned long dst_size;
+ struct fragment *fragment = patch->fragments;
+ void *data;
+ void *result;
+
+ data = inflate_it(fragment->patch, fragment->size,
+ patch->deflate_origlen);
+ if (!data)
+ return error("corrupt patch data");
+ switch (patch->is_binary) {
+ case BINARY_DELTA_DEFLATED:
+ result = patch_delta(desc->buffer, desc->size,
+ data,
+ patch->deflate_origlen,
+ &dst_size);
+ free(desc->buffer);
+ desc->buffer = result;
+ free(data);
+ break;
+ case BINARY_LITERAL_DEFLATED:
+ free(desc->buffer);
+ desc->buffer = data;
+ dst_size = patch->deflate_origlen;
+ break;
+ }
+ if (!desc->buffer)
+ return -1;
+ desc->size = desc->alloc = dst_size;
+ return 0;
+}
+
+static int apply_binary(struct buffer_desc *desc, struct patch *patch)
{
- struct fragment *frag = patch->fragments;
const char *name = patch->old_name ? patch->old_name : patch->new_name;
+ unsigned char sha1[20];
+ unsigned char hdr[50];
+ int hdrlen;
- if (patch->is_binary) {
- unsigned char sha1[20];
+ if (!allow_binary_replacement)
+ return error("cannot apply binary patch to '%s' "
+ "without --allow-binary-replacement",
+ name);
- if (!allow_binary_replacement)
- return error("cannot apply binary patch to '%s' "
- "without --allow-binary-replacement",
- name);
+ /* For safety, we require patch index line to contain
+ * full 40-byte textual SHA1 for old and new, at least for now.
+ */
+ if (strlen(patch->old_sha1_prefix) != 40 ||
+ strlen(patch->new_sha1_prefix) != 40 ||
+ get_sha1_hex(patch->old_sha1_prefix, sha1) ||
+ get_sha1_hex(patch->new_sha1_prefix, sha1))
+ return error("cannot apply binary patch to '%s' "
+ "without full index line", name);
- /* For safety, we require patch index line to contain
- * full 40-byte textual SHA1 for old and new, at least for now.
+ if (patch->old_name) {
+ /* See if the old one matches what the patch
+ * applies to.
*/
- if (strlen(patch->old_sha1_prefix) != 40 ||
- strlen(patch->new_sha1_prefix) != 40 ||
- get_sha1_hex(patch->old_sha1_prefix, sha1) ||
- get_sha1_hex(patch->new_sha1_prefix, sha1))
- return error("cannot apply binary patch to '%s' "
- "without full index line", name);
-
- if (patch->old_name) {
- unsigned char hdr[50];
- int hdrlen;
-
- /* See if the old one matches what the patch
- * applies to.
- */
- write_sha1_file_prepare(desc->buffer, desc->size,
- blob_type, sha1, hdr, &hdrlen);
- if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
- return error("the patch applies to '%s' (%s), "
- "which does not match the "
- "current contents.",
- name, sha1_to_hex(sha1));
- }
- else {
- /* Otherwise, the old one must be empty. */
- if (desc->size)
- return error("the patch applies to an empty "
- "'%s' but it is not empty", name);
- }
+ write_sha1_file_prepare(desc->buffer, desc->size,
+ blob_type, sha1, hdr, &hdrlen);
+ if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
+ return error("the patch applies to '%s' (%s), "
+ "which does not match the "
+ "current contents.",
+ name, sha1_to_hex(sha1));
+ }
+ else {
+ /* Otherwise, the old one must be empty. */
+ if (desc->size)
+ return error("the patch applies to an empty "
+ "'%s' but it is not empty", name);
+ }
- /* For now, we do not record post-image data in the patch,
- * and require the object already present in the recipient's
- * object database.
+ get_sha1_hex(patch->new_sha1_prefix, sha1);
+ if (!memcmp(sha1, null_sha1, 20)) {
+ free(desc->buffer);
+ desc->alloc = desc->size = 0;
+ desc->buffer = NULL;
+ return 0; /* deletion patch */
+ }
+
+ if (has_sha1_file(sha1)) {
+ /* We already have the postimage */
+ char type[10];
+ unsigned long size;
+
+ free(desc->buffer);
+ desc->buffer = read_sha1_file(sha1, type, &size);
+ if (!desc->buffer)
+ return error("the necessary postimage %s for "
+ "'%s' cannot be read",
+ patch->new_sha1_prefix, name);
+ desc->alloc = desc->size = size;
+ }
+ else {
+ /* We have verified desc matches the preimage;
+ * apply the patch data to it, which is stored
+ * in the patch->fragments->{patch,size}.
*/
- if (desc->buffer) {
- free(desc->buffer);
- desc->alloc = desc->size = 0;
- }
- get_sha1_hex(patch->new_sha1_prefix, sha1);
-
- if (memcmp(sha1, null_sha1, 20)) {
- char type[10];
- unsigned long size;
-
- desc->buffer = read_sha1_file(sha1, type, &size);
- if (!desc->buffer)
- return error("the necessary postimage %s for "
- "'%s' does not exist",
- patch->new_sha1_prefix, name);
- desc->alloc = desc->size = size;
- }
+ if (apply_binary_fragment(desc, patch))
+ return error("binary patch does not apply to '%s'",
+ name);
- return 0;
+ /* verify that the result matches */
+ write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
+ sha1, hdr, &hdrlen);
+ if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
+ return error("binary patch to '%s' creates incorrect result", name);
}
+ return 0;
+}
+
+static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+{
+ struct fragment *frag = patch->fragments;
+ const char *name = patch->old_name ? patch->old_name : patch->new_name;
+
+ if (patch->is_binary)
+ return apply_binary(desc, patch);
+
while (frag) {
if (apply_one_fragment(desc, frag) < 0)
return error("patch failed: %s:%ld",
static int apply_patch(int fd, const char *filename)
{
- int newfd;
unsigned long offset, size;
char *buffer = read_patch_file(fd, &size);
struct patch *list = NULL, **listp = &list;
size -= nr;
}
- newfd = -1;
if (whitespace_error && (new_whitespace == error_on_whitespace))
apply = 0;
write_index = check_index && apply;
- if (write_index)
+ if (write_index && newfd < 0)
newfd = hold_index_file_for_update(&cache_file, get_index_file());
if (check_index) {
if (read_cache() < 0)
if (apply)
write_out_results(list, skipped_patch);
- if (write_index) {
- if (write_cache(newfd, active_cache, active_nr) ||
- commit_index_file(&cache_file))
- die("Unable to write new cachefile");
- }
-
if (show_index_info)
show_index_list(list);
diffstat = 1;
continue;
}
- if (!strcmp(arg, "--allow-binary-replacement")) {
+ if (!strcmp(arg, "--allow-binary-replacement") ||
+ !strcmp(arg, "--binary")) {
allow_binary_replacement = 1;
continue;
}
whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : "");
}
+
+ if (write_index) {
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_index_file(&cache_file))
+ die("Unable to write new cachefile");
+ }
+
return 0;
}
--- /dev/null
+#include "cache.h"
+
+#undef DEBUG_85
+
+#ifdef DEBUG_85
+#define say(a) fprintf(stderr, a)
+#define say1(a,b) fprintf(stderr, a, b)
+#define say2(a,b,c) fprintf(stderr, a, b, c)
+#else
+#define say(a) do {} while(0)
+#define say1(a,b) do {} while(0)
+#define say2(a,b,c) do {} while(0)
+#endif
+
+static const char en85[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z',
+ '!', '#', '$', '%', '&', '(', ')', '*', '+', '-',
+ ';', '<', '=', '>', '?', '@', '^', '_', '`', '{',
+ '|', '}', '~'
+};
+
+static char de85[256];
+static void prep_base85(void)
+{
+ int i;
+ if (de85['Z'])
+ return;
+ for (i = 0; i < ARRAY_SIZE(en85); i++) {
+ int ch = en85[i];
+ de85[ch] = i + 1;
+ }
+}
+
+int decode_85(char *dst, char *buffer, int len)
+{
+ prep_base85();
+
+ say2("decode 85 <%.*s>", len/4*5, buffer);
+ while (len) {
+ unsigned acc = 0;
+ int de, cnt = 4;
+ unsigned char ch;
+ do {
+ ch = *buffer++;
+ de = de85[ch];
+ if (--de < 0)
+ return error("invalid base85 alphabet %c", ch);
+ acc = acc * 85 + de;
+ } while (--cnt);
+ ch = *buffer++;
+ de = de85[ch];
+ if (--de < 0)
+ return error("invalid base85 alphabet %c", ch);
+ /*
+ * Detect overflow. The largest
+ * 5-letter possible is "|NsC0" to
+ * encode 0xffffffff, and "|NsC" gives
+ * 0x03030303 at this point (i.e.
+ * 0xffffffff = 0x03030303 * 85).
+ */
+ if (0x03030303 < acc ||
+ 0xffffffff - de < (acc *= 85))
+ error("invalid base85 sequence %.5s", buffer-5);
+ acc += de;
+ say1(" %08x", acc);
+
+ cnt = (len < 4) ? len : 4;
+ len -= cnt;
+ do {
+ acc = (acc << 8) | (acc >> 24);
+ *dst++ = acc;
+ } while (--cnt);
+ }
+ say("\n");
+
+ return 0;
+}
+
+void encode_85(char *buf, unsigned char *data, int bytes)
+{
+ prep_base85();
+
+ say("encode 85");
+ while (bytes) {
+ unsigned acc = 0;
+ int cnt;
+ for (cnt = 24; cnt >= 0; cnt -= 8) {
+ int ch = *data++;
+ acc |= ch << cnt;
+ if (--bytes == 0)
+ break;
+ }
+ say1(" %08x", acc);
+ for (cnt = 4; cnt >= 0; cnt--) {
+ int val = acc % 85;
+ acc /= 85;
+ buf[cnt] = en85[val];
+ }
+ buf += 5;
+ }
+ say("\n");
+
+ *buf = 0;
+}
+
+#ifdef DEBUG_85
+int main(int ac, char **av)
+{
+ char buf[1024];
+
+ if (!strcmp(av[1], "-e")) {
+ int len = strlen(av[2]);
+ encode_85(buf, av[2], len);
+ if (len <= 26) len = len + 'A' - 1;
+ else len = len + 'a' - 26 + 1;
+ printf("encoded: %c%s\n", len, buf);
+ return 0;
+ }
+ if (!strcmp(av[1], "-d")) {
+ int len = *av[2];
+ if ('A' <= len && len <= 'Z') len = len - 'A' + 1;
+ else len = len - 'a' + 26 + 1;
+ decode_85(buf, av[2]+1, len);
+ printf("decoded: %.*s\n", len, buf);
+ return 0;
+ }
+ if (!strcmp(av[1], "-t")) {
+ char t[4] = { -1,-1,-1,-1 };
+ encode_85(buf, t, 4);
+ printf("encoded: D%s\n", buf);
+ return 0;
+ }
+}
+#endif
paths[1] = NULL;
diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
- &revs->diffopt);
+ &revs->pruning);
ret = rev_compare_tree(revs, c1->tree, c2->tree);
- diff_tree_release_paths(&revs->diffopt);
+ diff_tree_release_paths(&revs->pruning);
return ret;
}
paths[1] = NULL;
diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
- &revs->diffopt);
+ &revs->pruning);
ret = rev_same_tree_as_empty(revs, t1);
- diff_tree_release_paths(&revs->diffopt);
+ diff_tree_release_paths(&revs->pruning);
return ret;
}
args[0] = filename;
args[1] = NULL;
- diff_tree_setup_paths(args, &rev.diffopt);
+ diff_tree_setup_paths(args, &rev.pruning);
prepare_revision_walk(&rev);
process_commits(&rev, filename, &initial);
--- /dev/null
+/*
+ * Builtin "git count-objects".
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "builtin.h"
+
+static const char count_objects_usage[] = "git-count-objects [-v]";
+
+static void count_objects(DIR *d, char *path, int len, int verbose,
+ unsigned long *loose,
+ unsigned long *loose_size,
+ unsigned long *packed_loose,
+ unsigned long *garbage)
+{
+ struct dirent *ent;
+ while ((ent = readdir(d)) != NULL) {
+ char hex[41];
+ unsigned char sha1[20];
+ const char *cp;
+ int bad = 0;
+
+ if ((ent->d_name[0] == '.') &&
+ (ent->d_name[1] == 0 ||
+ ((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
+ continue;
+ for (cp = ent->d_name; *cp; cp++) {
+ int ch = *cp;
+ if (('0' <= ch && ch <= '9') ||
+ ('a' <= ch && ch <= 'f'))
+ continue;
+ bad = 1;
+ break;
+ }
+ if (cp - ent->d_name != 38)
+ bad = 1;
+ else {
+ struct stat st;
+ memcpy(path + len + 3, ent->d_name, 38);
+ path[len + 2] = '/';
+ path[len + 41] = 0;
+ if (lstat(path, &st) || !S_ISREG(st.st_mode))
+ bad = 1;
+ else
+ (*loose_size) += st.st_blocks;
+ }
+ if (bad) {
+ if (verbose) {
+ error("garbage found: %.*s/%s",
+ len + 2, path, ent->d_name);
+ (*garbage)++;
+ }
+ continue;
+ }
+ (*loose)++;
+ if (!verbose)
+ continue;
+ memcpy(hex, path+len, 2);
+ memcpy(hex+2, ent->d_name, 38);
+ hex[40] = 0;
+ if (get_sha1_hex(hex, sha1))
+ die("internal error");
+ if (has_sha1_pack(sha1))
+ (*packed_loose)++;
+ }
+}
+
+int cmd_count_objects(int ac, const char **av, char **ep)
+{
+ int i;
+ int verbose = 0;
+ const char *objdir = get_object_directory();
+ int len = strlen(objdir);
+ char *path = xmalloc(len + 50);
+ unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
+ unsigned long loose_size = 0;
+
+ for (i = 1; i < ac; i++) {
+ const char *arg = av[i];
+ if (*arg != '-')
+ break;
+ else if (!strcmp(arg, "-v"))
+ verbose = 1;
+ else
+ usage(count_objects_usage);
+ }
+
+ /* we do not take arguments other than flags for now */
+ if (i < ac)
+ usage(count_objects_usage);
+ memcpy(path, objdir, len);
+ if (len && objdir[len-1] != '/')
+ path[len++] = '/';
+ for (i = 0; i < 256; i++) {
+ DIR *d;
+ sprintf(path + len, "%02x", i);
+ d = opendir(path);
+ if (!d)
+ continue;
+ count_objects(d, path, len, verbose,
+ &loose, &loose_size, &packed_loose, &garbage);
+ closedir(d);
+ }
+ if (verbose) {
+ struct packed_git *p;
+ if (!packed_git)
+ prepare_packed_git();
+ for (p = packed_git; p; p = p->next) {
+ if (!p->pack_local)
+ continue;
+ packed += num_packed_objects(p);
+ }
+ printf("count: %lu\n", loose);
+ printf("size: %lu\n", loose_size / 2);
+ printf("in-pack: %lu\n", packed);
+ printf("prune-packable: %lu\n", packed_loose);
+ printf("garbage: %lu\n", garbage);
+ }
+ else
+ printf("%lu objects, %lu kilobytes\n",
+ loose, loose_size / 2);
+ return 0;
+}
--- /dev/null
+/*
+ * Builtin "git diff"
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tag.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "builtin.h"
+
+/* NEEDSWORK: struct object has place for name but we _do_
+ * know mode when we extracted the blob out of a tree, which
+ * we currently lose.
+ */
+struct blobinfo {
+ unsigned char sha1[20];
+ const char *name;
+};
+
+static const char builtin_diff_usage[] =
+"diff <options> <rev>{0,2} -- <path>*";
+
+static int builtin_diff_files(struct rev_info *revs,
+ int argc, const char **argv)
+{
+ int silent = 0;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--base"))
+ revs->max_count = 1;
+ else if (!strcmp(arg, "--ours"))
+ revs->max_count = 2;
+ else if (!strcmp(arg, "--theirs"))
+ revs->max_count = 3;
+ else if (!strcmp(arg, "-q"))
+ silent = 1;
+ else if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ /*
+ * Make sure there are NO revision (i.e. pending object) parameter,
+ * specified rev.max_count is reasonable (0 <= n <= 3), and
+ * there is no other revision filtering parameter.
+ */
+ if (revs->pending_objects ||
+ revs->min_age != -1 ||
+ revs->max_age != -1 ||
+ 3 < revs->max_count)
+ usage(builtin_diff_usage);
+ if (revs->max_count < 0 &&
+ (revs->diffopt.output_format == DIFF_FORMAT_PATCH))
+ revs->combine_merges = revs->dense_combined_merges = 1;
+ /*
+ * Backward compatibility wart - "diff-files -s" used to
+ * defeat the common diff option "-s" which asked for
+ * DIFF_FORMAT_NO_OUTPUT.
+ */
+ if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ return run_diff_files(revs, silent);
+}
+
+static void stuff_change(struct diff_options *opt,
+ unsigned old_mode, unsigned new_mode,
+ const unsigned char *old_sha1,
+ const unsigned char *new_sha1,
+ const char *old_name,
+ const char *new_name)
+{
+ struct diff_filespec *one, *two;
+
+ if (memcmp(null_sha1, old_sha1, 20) &&
+ memcmp(null_sha1, new_sha1, 20) &&
+ !memcmp(old_sha1, new_sha1, 20))
+ return;
+
+ if (opt->reverse_diff) {
+ unsigned tmp;
+ const unsigned char *tmp_u;
+ const char *tmp_c;
+ tmp = old_mode; old_mode = new_mode; new_mode = tmp;
+ tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
+ tmp_c = old_name; old_name = new_name; new_name = tmp_c;
+ }
+ one = alloc_filespec(old_name);
+ two = alloc_filespec(new_name);
+ fill_filespec(one, old_sha1, old_mode);
+ fill_filespec(two, new_sha1, new_mode);
+
+ /* NEEDSWORK: shouldn't this part of diffopt??? */
+ diff_queue(&diff_queued_diff, one, two);
+}
+
+static int builtin_diff_b_f(struct rev_info *revs,
+ int argc, const char **argv,
+ struct blobinfo *blob,
+ const char *path)
+{
+ /* Blob vs file in the working tree*/
+ struct stat st;
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ if (lstat(path, &st))
+ die("'%s': %s", path, strerror(errno));
+ if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
+ die("'%s': not a regular file or symlink", path);
+ stuff_change(&revs->diffopt,
+ canon_mode(st.st_mode), canon_mode(st.st_mode),
+ blob[0].sha1, null_sha1,
+ blob[0].name, path);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return 0;
+}
+
+static int builtin_diff_blobs(struct rev_info *revs,
+ int argc, const char **argv,
+ struct blobinfo *blob)
+{
+ /* Blobs */
+ unsigned mode = canon_mode(S_IFREG | 0644);
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ stuff_change(&revs->diffopt,
+ mode, mode,
+ blob[0].sha1, blob[1].sha1,
+ blob[1].name, blob[1].name);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return 0;
+}
+
+static int builtin_diff_index(struct rev_info *revs,
+ int argc, const char **argv)
+{
+ int cached = 0;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--cached"))
+ cached = 1;
+ else if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ /*
+ * Make sure there is one revision (i.e. pending object),
+ * and there is no revision filtering parameters.
+ */
+ if (!revs->pending_objects || revs->pending_objects->next ||
+ revs->max_count != -1 || revs->min_age != -1 ||
+ revs->max_age != -1)
+ usage(builtin_diff_usage);
+ return run_diff_index(revs, cached);
+}
+
+static int builtin_diff_tree(struct rev_info *revs,
+ int argc, const char **argv,
+ struct object_list *ent)
+{
+ const unsigned char *(sha1[2]);
+ int swap = 1;
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+
+ /* We saw two trees, ent[0] and ent[1].
+ * unless ent[0] is unintesting, they are swapped
+ */
+ if (ent[0].item->flags & UNINTERESTING)
+ swap = 0;
+ sha1[swap] = ent[0].item->sha1;
+ sha1[1-swap] = ent[1].item->sha1;
+ diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
+ log_tree_diff_flush(revs);
+ return 0;
+}
+
+static int builtin_diff_combined(struct rev_info *revs,
+ int argc, const char **argv,
+ struct object_list *ent,
+ int ents)
+{
+ const unsigned char (*parent)[20];
+ int i;
+
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--raw"))
+ revs->diffopt.output_format = DIFF_FORMAT_RAW;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
+ if (!revs->dense_combined_merges && !revs->combine_merges)
+ revs->dense_combined_merges = revs->combine_merges = 1;
+ parent = xmalloc(ents * sizeof(*parent));
+ /* Again, the revs are all reverse */
+ for (i = 0; i < ents; i++)
+ memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20);
+ diff_tree_combined(parent[0], parent + 1, ents - 1,
+ revs->dense_combined_merges, revs);
+ return 0;
+}
+
+static void add_head(struct rev_info *revs)
+{
+ unsigned char sha1[20];
+ struct object *obj;
+ if (get_sha1("HEAD", sha1))
+ return;
+ obj = parse_object(sha1);
+ if (!obj)
+ return;
+ add_object(obj, &revs->pending_objects, NULL, "HEAD");
+}
+
+int cmd_diff(int argc, const char **argv, char **envp)
+{
+ struct rev_info rev;
+ struct object_list *list, ent[100];
+ int ents = 0, blobs = 0, paths = 0;
+ const char *path = NULL;
+ struct blobinfo blob[2];
+
+ /*
+ * We could get N tree-ish in the rev.pending_objects list.
+ * Also there could be M blobs there, and P pathspecs.
+ *
+ * N=0, M=0:
+ * cache vs files (diff-files)
+ * N=0, M=2:
+ * compare two random blobs. P must be zero.
+ * N=0, M=1, P=1:
+ * compare a blob with a working tree file.
+ *
+ * N=1, M=0:
+ * tree vs cache (diff-index --cached)
+ *
+ * N=2, M=0:
+ * tree vs tree (diff-tree)
+ *
+ * Other cases are errors.
+ */
+
+ git_config(git_diff_config);
+ init_revisions(&rev);
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
+ /* Do we have --cached and not have a pending object, then
+ * default to HEAD by hand. Eek.
+ */
+ if (!rev.pending_objects) {
+ int i;
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--"))
+ break;
+ else if (!strcmp(arg, "--cached")) {
+ add_head(&rev);
+ break;
+ }
+ }
+ }
+
+ for (list = rev.pending_objects; list; list = list->next) {
+ struct object *obj = list->item;
+ const char *name = list->name;
+ int flags = (obj->flags & UNINTERESTING);
+ if (!obj->parsed)
+ obj = parse_object(obj->sha1);
+ obj = deref_tag(obj, NULL, 0);
+ if (!obj)
+ die("invalid object '%s' given.", name);
+ if (!strcmp(obj->type, commit_type))
+ obj = &((struct commit *)obj)->tree->object;
+ if (!strcmp(obj->type, tree_type)) {
+ if (ARRAY_SIZE(ent) <= ents)
+ die("more than %d trees given: '%s'",
+ (int) ARRAY_SIZE(ent), name);
+ obj->flags |= flags;
+ ent[ents].item = obj;
+ ent[ents].name = name;
+ ents++;
+ continue;
+ }
+ if (!strcmp(obj->type, blob_type)) {
+ if (2 <= blobs)
+ die("more than two blobs given: '%s'", name);
+ memcpy(blob[blobs].sha1, obj->sha1, 20);
+ blob[blobs].name = name;
+ blobs++;
+ continue;
+
+ }
+ die("unhandled object '%s' given.", name);
+ }
+ if (rev.prune_data) {
+ const char **pathspec = rev.prune_data;
+ while (*pathspec) {
+ if (!path)
+ path = *pathspec;
+ paths++;
+ pathspec++;
+ }
+ }
+
+ /*
+ * Now, do the arguments look reasonable?
+ */
+ if (!ents) {
+ switch (blobs) {
+ case 0:
+ return builtin_diff_files(&rev, argc, argv);
+ break;
+ case 1:
+ if (paths != 1)
+ usage(builtin_diff_usage);
+ return builtin_diff_b_f(&rev, argc, argv, blob, path);
+ break;
+ case 2:
+ if (paths)
+ usage(builtin_diff_usage);
+ return builtin_diff_blobs(&rev, argc, argv, blob);
+ break;
+ default:
+ usage(builtin_diff_usage);
+ }
+ }
+ else if (blobs)
+ usage(builtin_diff_usage);
+ else if (ents == 1)
+ return builtin_diff_index(&rev, argc, argv);
+ else if (ents == 2)
+ return builtin_diff_tree(&rev, argc, argv, ent);
+ else
+ return builtin_diff_combined(&rev, argc, argv, ent, ents);
+ usage(builtin_diff_usage);
+}
--- /dev/null
+/*
+ * "git push"
+ */
+#include "cache.h"
+#include "refs.h"
+#include "run-command.h"
+#include "builtin.h"
+
+#define MAX_URI (16)
+
+static const char push_usage[] = "git push [--all] [--tags] [--force] <repository> [<refspec>...]";
+
+static int all = 0, tags = 0, force = 0, thin = 1;
+static const char *execute = NULL;
+
+#define BUF_SIZE (2084)
+static char buffer[BUF_SIZE];
+
+static const char **refspec = NULL;
+static int refspec_nr = 0;
+
+static void add_refspec(const char *ref)
+{
+ int nr = refspec_nr + 1;
+ refspec = xrealloc(refspec, nr * sizeof(char *));
+ refspec[nr-1] = ref;
+ refspec_nr = nr;
+}
+
+static int expand_one_ref(const char *ref, const unsigned char *sha1)
+{
+ /* Ignore the "refs/" at the beginning of the refname */
+ ref += 5;
+
+ if (strncmp(ref, "tags/", 5))
+ return 0;
+
+ add_refspec(strdup(ref));
+ return 0;
+}
+
+static void expand_refspecs(void)
+{
+ if (all) {
+ if (refspec_nr)
+ die("cannot mix '--all' and a refspec");
+
+ /*
+ * No need to expand "--all" - we'll just use
+ * the "--all" flag to send-pack
+ */
+ return;
+ }
+ if (!tags)
+ return;
+ for_each_ref(expand_one_ref);
+}
+
+static void set_refspecs(const char **refs, int nr)
+{
+ if (nr) {
+ size_t bytes = nr * sizeof(char *);
+
+ refspec = xrealloc(refspec, bytes);
+ memcpy(refspec, refs, bytes);
+ refspec_nr = nr;
+ }
+ expand_refspecs();
+}
+
+static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
+{
+ int n = 0;
+ FILE *f = fopen(git_path("remotes/%s", repo), "r");
+ int has_explicit_refspec = refspec_nr || all || tags;
+
+ if (!f)
+ return -1;
+ while (fgets(buffer, BUF_SIZE, f)) {
+ int is_refspec;
+ char *s, *p;
+
+ if (!strncmp("URL: ", buffer, 5)) {
+ is_refspec = 0;
+ s = buffer + 5;
+ } else if (!strncmp("Push: ", buffer, 6)) {
+ is_refspec = 1;
+ s = buffer + 6;
+ } else
+ continue;
+
+ /* Remove whitespace at the head.. */
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ continue;
+
+ /* ..and at the end */
+ p = s + strlen(s);
+ while (isspace(p[-1]))
+ *--p = 0;
+
+ if (!is_refspec) {
+ if (n < MAX_URI)
+ uri[n++] = strdup(s);
+ else
+ error("more than %d URL's specified, ignoreing the rest", MAX_URI);
+ }
+ else if (is_refspec && !has_explicit_refspec)
+ add_refspec(strdup(s));
+ }
+ fclose(f);
+ if (!n)
+ die("remote '%s' has no URL", repo);
+ return n;
+}
+
+static const char **config_uri;
+static const char *config_repo;
+static int config_repo_len;
+static int config_current_uri;
+static int config_get_refspecs;
+
+static int get_remote_config(const char* key, const char* value)
+{
+ if (!strncmp(key, "remote.", 7) &&
+ !strncmp(key + 7, config_repo, config_repo_len)) {
+ if (!strcmp(key + 7 + config_repo_len, ".url")) {
+ if (config_current_uri < MAX_URI)
+ config_uri[config_current_uri++] = strdup(value);
+ else
+ 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(strdup(value));
+ }
+ return 0;
+}
+
+static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
+{
+ config_repo_len = strlen(repo);
+ config_repo = repo;
+ config_current_uri = 0;
+ config_uri = uri;
+ config_get_refspecs = !(refspec_nr || all || tags);
+
+ git_config(get_remote_config);
+ return config_current_uri;
+}
+
+static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
+{
+ const char *slash = strchr(repo, '/');
+ int n = slash ? slash - repo : 1000;
+ FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
+ char *s, *p;
+ int len;
+
+ if (!f)
+ return 0;
+ s = fgets(buffer, BUF_SIZE, f);
+ fclose(f);
+ if (!s)
+ return 0;
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ return 0;
+ p = s + strlen(s);
+ while (isspace(p[-1]))
+ *--p = 0;
+ len = p - s;
+ if (slash)
+ len += strlen(slash);
+ p = xmalloc(len + 1);
+ strcpy(p, s);
+ if (slash)
+ strcat(p, slash);
+ uri[0] = p;
+ return 1;
+}
+
+/*
+ * Read remotes and branches file, fill the push target URI
+ * list. If there is no command line refspecs, read Push: lines
+ * to set up the *refspec list as well.
+ * return the number of push target URIs
+ */
+static int read_config(const char *repo, const char *uri[MAX_URI])
+{
+ int n;
+
+ if (*repo != '/') {
+ n = get_remotes_uri(repo, uri);
+ if (n > 0)
+ return n;
+
+ n = get_config_remotes_uri(repo, uri);
+ if (n > 0)
+ return n;
+
+ n = get_branches_uri(repo, uri);
+ if (n > 0)
+ return n;
+ }
+
+ uri[0] = repo;
+ return 1;
+}
+
+static int do_push(const char *repo)
+{
+ const char *uri[MAX_URI];
+ int i, n;
+ int remote;
+ const char **argv;
+ int argc;
+
+ n = read_config(repo, uri);
+ if (n <= 0)
+ die("bad repository '%s'", repo);
+
+ argv = xmalloc((refspec_nr + 10) * sizeof(char *));
+ argv[0] = "dummy-send-pack";
+ argc = 1;
+ if (all)
+ argv[argc++] = "--all";
+ if (force)
+ argv[argc++] = "--force";
+ if (execute)
+ argv[argc++] = execute;
+ if (thin)
+ argv[argc++] = "--thin";
+ remote = argc;
+ argv[argc++] = "dummy-remote";
+ while (refspec_nr--)
+ argv[argc++] = *refspec++;
+ argv[argc] = NULL;
+
+ for (i = 0; i < n; i++) {
+ int error;
+ const char *dest = uri[i];
+ const char *sender = "git-send-pack";
+ if (!strncmp(dest, "http://", 7) ||
+ !strncmp(dest, "https://", 8))
+ sender = "git-http-push";
+ argv[0] = sender;
+ argv[remote] = dest;
+ error = run_command_v(argc, argv);
+ if (!error)
+ continue;
+ switch (error) {
+ case -ERR_RUN_COMMAND_FORK:
+ die("unable to fork for %s", sender);
+ case -ERR_RUN_COMMAND_EXEC:
+ die("unable to exec %s", sender);
+ case -ERR_RUN_COMMAND_WAITPID:
+ case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
+ case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
+ case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
+ die("%s died with strange error", sender);
+ default:
+ return -error;
+ }
+ }
+ return 0;
+}
+
+int cmd_push(int argc, const char **argv, char **envp)
+{
+ int i;
+ const char *repo = "origin"; // default repository
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-') {
+ repo = arg;
+ i++;
+ break;
+ }
+ if (!strcmp(arg, "--all")) {
+ all = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--tags")) {
+ tags = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--force")) {
+ force = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--thin")) {
+ thin = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--no-thin")) {
+ thin = 0;
+ continue;
+ }
+ if (!strncmp(arg, "--exec=", 7)) {
+ execute = arg;
+ continue;
+ }
+ usage(push_usage);
+ }
+ set_refspecs(argv + i, argc - i);
+ return do_push(repo);
+}
extern int cmd_whatchanged(int argc, const char **argv, char **envp);
extern int cmd_show(int argc, const char **argv, char **envp);
extern int cmd_log(int argc, const char **argv, char **envp);
+extern int cmd_diff(int argc, const char **argv, char **envp);
+extern int cmd_count_objects(int argc, const char **argv, char **envp);
+
+extern int cmd_push(int argc, const char **argv, char **envp);
#endif
extern const char *prefix_path(const char *prefix, int len, const char *path);
extern const char *prefix_filename(const char *prefix, int len, const char *path);
extern void verify_filename(const char *prefix, const char *name);
+extern void verify_non_filename(const char *prefix, const char *name);
#define alloc_nr(x) (((x)+16)*3/2)
/* Environment bits from configuration mechanism */
extern int trust_executable_bit;
extern int assume_unchanged;
-extern int only_use_symrefs;
+extern int prefer_symlink_refs;
extern int warn_ambiguous_refs;
extern int diff_rename_limit_default;
extern int shared_repository;
/* pager.c */
extern void setup_pager(void);
+/* base85 */
+int decode_85(char *dst, char *line, int linelen);
+void encode_85(char *buf, unsigned char *data, int bytes);
+
#endif /* CACHE_H */
setup_git_directory();
git_config(git_default_config);
- if (argc != 3 || get_sha1(argv[2], sha1))
+ if (argc != 3)
usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
+ if (get_sha1(argv[2], sha1))
+ die("Not a valid object name %s", argv[2]);
opt = 0;
if ( argv[1][0] == '-' ) {
return !has_sha1_file(sha1);
case 'p':
- if (get_sha1(argv[2], sha1) ||
- sha1_object_info(sha1, type, NULL))
+ if (sha1_object_info(sha1, type, NULL))
die("Not a valid object name %s", argv[2]);
/* custom pretty-print here */
/* Check out named files first */
for ( ; i < argc; i++) {
const char *arg = argv[i];
+ const char *p;
if (all)
die("git-checkout-index: don't mix '--all' and explicit filenames");
if (read_from_stdin)
die("git-checkout-index: don't mix '--stdin' and explicit filenames");
- checkout_file(prefix_path(prefix, prefix_length, arg));
+ p = prefix_path(prefix, prefix_length, arg);
+ checkout_file(p);
+ if (p < arg || p > arg + strlen(arg))
+ free((char*)p);
}
if (read_from_stdin) {
strbuf_init(&buf);
while (1) {
char *path_name;
+ const char *p;
+
read_line(&buf, stdin, line_termination);
if (buf.eof)
break;
path_name = unquote_c_style(buf.buf, NULL);
else
path_name = buf.buf;
- checkout_file(prefix_path(prefix, prefix_length, path_name));
+ p = prefix_path(prefix, prefix_length, path_name);
+ checkout_file(p);
+ if (p < path_name || p > path_name + strlen(path_name))
+ free((char *)p);
if (path_name != buf.buf)
free(path_name);
}
}
}
-void diff_tree_combined_merge(const unsigned char *sha1,
- int dense, struct rev_info *rev)
+void diff_tree_combined(const unsigned char *sha1,
+ const unsigned char parent[][20],
+ int num_parent,
+ int dense,
+ struct rev_info *rev)
{
struct diff_options *opt = &rev->diffopt;
- struct commit *commit = lookup_commit(sha1);
struct diff_options diffopts;
- struct commit_list *parents;
struct combine_diff_path *p, *paths = NULL;
- int num_parent, i, num_paths;
+ int i, num_paths;
int do_diffstat;
do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||
diffopts.with_stat = 0;
diffopts.recursive = 1;
- /* count parents */
- for (parents = commit->parents, num_parent = 0;
- parents;
- parents = parents->next, num_parent++)
- ; /* nothing */
-
/* find set of paths that everybody touches */
- for (parents = commit->parents, i = 0;
- parents;
- parents = parents->next, i++) {
- struct commit *parent = parents->item;
+ for (i = 0; i < num_parent; i++) {
/* show stat against the first parent even
* when doing combined diff.
*/
diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
else
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
- diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
- &diffopts);
+ diff_tree_sha1(parent[i], sha1, "", &diffopts);
diffcore_std(&diffopts);
paths = intersect_paths(paths, i, num_parent);
free(tmp);
}
}
+
+void diff_tree_combined_merge(const unsigned char *sha1,
+ int dense, struct rev_info *rev)
+{
+ int num_parent;
+ const unsigned char (*parent)[20];
+ struct commit *commit = lookup_commit(sha1);
+ struct commit_list *parents;
+
+ /* count parents */
+ for (parents = commit->parents, num_parent = 0;
+ parents;
+ parents = parents->next, num_parent++)
+ ; /* nothing */
+
+ parent = xmalloc(num_parent * sizeof(*parent));
+ for (parents = commit->parents, num_parent = 0;
+ parents;
+ parents = parents->next, num_parent++)
+ memcpy(parent + num_parent, parents->item->object.sha1, 20);
+ diff_tree_combined(sha1, parent, num_parent, dense, rev);
+}
git_config(git_default_config);
- if (argc < 2 || get_sha1(argv[1], tree_sha1) < 0)
+ if (argc < 2)
usage(commit_tree_usage);
+ if (get_sha1(argv[1], tree_sha1))
+ die("Not a valid object name %s", argv[1]);
check_valid(tree_sha1, tree_type);
for (i = 2; i < argc; i += 2) {
char *a, *b;
a = argv[i]; b = argv[i+1];
- if (!b || strcmp(a, "-p") || get_sha1(b, parent_sha1[parents]))
+ if (!b || strcmp(a, "-p"))
usage(commit_tree_usage);
+ if (get_sha1(b, parent_sha1[parents]))
+ die("Not a valid object name %s", b);
check_valid(parent_sha1[parents], commit_type);
if (new_parent(parents))
parents++;
space = 1;
continue;
}
+ if (!quote) {
+ if (c == ';' || c == '#') {
+ comment = 1;
+ continue;
+ }
+ }
if (space) {
if (len)
value[len++] = ' ';
quote = 1-quote;
continue;
}
- if (!quote) {
- if (c == ';' || c == '#') {
- comment = 1;
- continue;
- }
- }
value[len++] = c;
}
}
return 0;
}
- if (!strcmp(var, "core.symrefsonly")) {
- only_use_symrefs = git_config_bool(var, value);
+ if (!strcmp(var, "core.prefersymlinkrefs")) {
+ prefer_symlink_refs = git_config_bool(var, value);
return 0;
}
store.offset[store.seen] = ftell(config_file);
store.state = KEY_SEEN;
store.seen++;
- } else if(!strncmp(key, store.key, store.baselen))
- store.state = SECTION_SEEN;
+ } else if (strrchr(key, '.') - key == store.baselen &&
+ !strncmp(key, store.key, store.baselen)) {
+ store.state = SECTION_SEEN;
+ store.offset[store.seen] = ftell(config_file);
+ }
}
return 0;
}
const char* value_regex, int multi_replace)
{
int i;
- int fd, in_fd;
+ int fd = -1, in_fd;
int ret;
char* config_filename = strdup(git_path("config"));
char* lock_file = strdup(git_path("config.lock"));
if ( ENOENT != errno ) {
error("opening %s: %s", config_filename,
strerror(errno));
- close(fd);
- unlink(lock_file);
ret = 3; /* same as "invalid config file" */
goto out_free;
}
/* if nothing to unset, error out */
if (value == NULL) {
- close(fd);
- unlink(lock_file);
ret = 5;
goto out_free;
}
/* if nothing to unset, or too many matches, error out */
if ((store.seen == 0 && value == NULL) ||
(store.seen > 1 && multi_replace == 0)) {
- close(fd);
- unlink(lock_file);
ret = 5;
goto out_free;
}
unlink(config_filename);
}
- close(fd);
-
if (rename(lock_file, config_filename) < 0) {
fprintf(stderr, "Could not rename the lock file?\n");
ret = 4;
ret = 0;
out_free:
+ if (0 <= fd)
+ close(fd);
if (config_filename)
free(config_filename);
- if (lock_file)
+ if (lock_file) {
+ unlink(lock_file);
free(lock_file);
+ }
return ret;
}
$GIT_SVN_INDEX $GIT_SVN
$GIT_DIR $REV_DIR/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '0.11.0';
+$VERSION = '1.0.0';
use Cwd qw/abs_path/;
$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
my %cmd = (
fetch => [ \&fetch, "Download new revisions from SVN",
{ 'revision|r=s' => \$_revision, %fc_opts } ],
- init => [ \&init, "Initialize and fetch (import)", { } ],
+ init => [ \&init, "Initialize a repo for tracking" .
+ " (requires URL argument)", { } ],
commit => [ \&commit, "Commit git revisions to SVN",
{ 'stdin|' => \$_stdin,
'edit|e' => \$_edit,
}
sub init {
- $SVN_URL = shift or croak "SVN repository location required\n";
+ $SVN_URL = shift or die "SVN repository location required " .
+ "as a command-line argument\n";
unless (-d $GIT_DIR) {
sys('git-init-db');
}
--------
init::
Creates an empty git repository with additional metadata
- directories for git-svn. The SVN_URL must be specified
- at this point.
+ directories for git-svn. The Subversion URL must be specified
+ as a command-line argument.
fetch::
- Fetch unfetched revisions from the SVN_URL we are tracking.
- refs/heads/remotes/git-svn will be updated to the latest revision.
+ Fetch unfetched revisions from the Subversion URL we are
+ tracking. refs/remotes/git-svn will be updated to the
+ latest revision.
- Note: You should never attempt to modify the remotes/git-svn branch
- outside of git-svn. Instead, create a branch from remotes/git-svn
- and work on that branch. Use the 'commit' command (see below)
- to write git commits back to remotes/git-svn.
+ Note: You should never attempt to modify the remotes/git-svn
+ branch outside of git-svn. Instead, create a branch from
+ remotes/git-svn and work on that branch. Use the 'commit'
+ command (see below) to write git commits back to
+ remotes/git-svn.
+
+ See 'Additional Fetch Arguments' if you are interested in
+ manually joining branches on commit.
commit::
Commit specified commit or tree objects to SVN. This relies on
tracked with git-svn. Unfortunately, git-clone does not clone
git-svn metadata and the svn working tree that git-svn uses for
its operations. This rebuilds the metadata so git-svn can
- resume fetch operations. SVN_URL may be optionally specified if
- the directory/repository you're tracking has moved or changed
- protocols.
+ resume fetch operations. A Subversion URL may be optionally
+ specified at the command-line if the directory/repository you're
+ tracking has moved or changed protocols.
show-ignore::
Recursively finds and lists the svn:ignore property on
repo-config key: svn.l
repo-config key: svn.findcopiesharder
+-A<filename>::
+--authors-file=<filename>::
+
+ Syntax is compatible with the files used by git-svnimport and
+ git-cvsimport:
+
+------------------------------------------------------------------------
+loginname = Joe User <user@example.com>
+------------------------------------------------------------------------
+
+ If this option is specified and git-svn encounters an SVN
+ committer name that does not exist in the authors-file, git-svn
+ will abort operation. The user will then have to add the
+ appropriate entry. Re-running the previous git-svn command
+ after the authors-file is modified should continue operation.
+
+ repo-config key: svn.authors-file
+
ADVANCED OPTIONS
----------------
-b<refname>::
setup_git_directory();
- if (argc != 2 || get_sha1(argv[1], sha1))
+ if (argc != 2)
usage("git-convert-objects <sha1>");
+ if (get_sha1(argv[1], sha1))
+ die("Not a valid object name %s", argv[1]);
entry = convert_entry(sha1);
printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1));
#ifndef DELTA_H
#define DELTA_H
-/* handling of delta buffers */
-extern void *diff_delta(void *from_buf, unsigned long from_size,
- void *to_buf, unsigned long to_size,
- unsigned long *delta_size, unsigned long max_size);
-extern void *patch_delta(void *src_buf, unsigned long src_size,
- void *delta_buf, unsigned long delta_size,
+/* opaque object for delta index */
+struct delta_index;
+
+/*
+ * create_delta_index: compute index data from given buffer
+ *
+ * This returns a pointer to a struct delta_index that should be passed to
+ * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
+ * is returned on failure. The given buffer must not be freed nor altered
+ * before free_delta_index() is called. The returned pointer must be freed
+ * using free_delta_index().
+ */
+extern struct delta_index *
+create_delta_index(const void *buf, unsigned long bufsize);
+
+/*
+ * free_delta_index: free the index created by create_delta_index()
+ */
+extern void free_delta_index(struct delta_index *index);
+
+/*
+ * create_delta: create a delta from given index for the given buffer
+ *
+ * This function may be called multiple times with different buffers using
+ * the same delta_index pointer. If max_delta_size is non-zero and the
+ * resulting delta is to be larger than max_delta_size then NULL is returned.
+ * On success, a non-NULL pointer to the buffer with the delta data is
+ * returned and *delta_size is updated with its size. The returned buffer
+ * must be freed by the caller.
+ */
+extern void *
+create_delta(const struct delta_index *index,
+ const void *buf, unsigned long bufsize,
+ unsigned long *delta_size, unsigned long max_delta_size);
+
+/*
+ * diff_delta: create a delta from source buffer to target buffer
+ *
+ * If max_delta_size is non-zero and the resulting delta is to be larger
+ * than max_delta_size then NULL is returned. On success, a non-NULL
+ * pointer to the buffer with the delta data is returned and *delta_size is
+ * updated with its size. The returned buffer must be freed by the caller.
+ */
+static inline void *
+diff_delta(const void *src_buf, unsigned long src_bufsize,
+ const void *trg_buf, unsigned long trg_bufsize,
+ unsigned long *delta_size, unsigned long max_delta_size)
+{
+ struct delta_index *index = create_delta_index(src_buf, src_bufsize);
+ if (index) {
+ void *delta = create_delta(index, trg_buf, trg_bufsize,
+ delta_size, max_delta_size);
+ free_delta_index(index);
+ return delta;
+ }
+ return NULL;
+}
+
+/*
+ * patch_delta: recreate target buffer given source buffer and delta data
+ *
+ * On success, a non-NULL pointer to the target buffer is returned and
+ * *trg_bufsize is updated with its size. On failure a NULL pointer is
+ * returned. The returned buffer must be freed by the caller.
+ */
+extern void *patch_delta(const void *src_buf, unsigned long src_size,
+ const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size);
/* the smallest possible delta size is 4 bytes */
/*
* This must be called twice on the delta data buffer, first to get the
- * expected reference buffer size, and again to get the result buffer size.
+ * expected source buffer size, and again to get the target buffer size.
*/
static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
const unsigned char *top)
static int initialized = 0;
struct commit_name *n;
- if (get_sha1(arg, sha1) < 0)
- usage(describe_usage);
+ if (get_sha1(arg, sha1))
+ die("Not a valid object name %s", arg);
cmit = lookup_commit_reference(sha1);
if (!cmit)
- usage(describe_usage);
+ die("%s is not a valid '%s' object", arg, commit_type);
if (!initialized) {
initialized = 1;
#include <stdlib.h>
#include <string.h>
-#include <zlib.h>
#include "delta.h"
-/* block size: min = 16, max = 64k, power of 2 */
-#define BLK_SIZE 16
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
+/* maximum hash entry list for the same hash bucket */
+#define HASH_LIMIT 64
+
+#define RABIN_SHIFT 23
+#define RABIN_WINDOW 16
+
+static const unsigned int T[256] = {
+ 0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344,
+ 0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259,
+ 0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85,
+ 0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2,
+ 0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a,
+ 0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db,
+ 0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753,
+ 0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964,
+ 0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8,
+ 0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5,
+ 0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81,
+ 0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6,
+ 0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e,
+ 0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77,
+ 0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff,
+ 0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8,
+ 0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc,
+ 0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1,
+ 0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d,
+ 0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a,
+ 0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2,
+ 0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02,
+ 0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a,
+ 0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd,
+ 0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61,
+ 0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c,
+ 0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08,
+ 0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f,
+ 0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7,
+ 0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe,
+ 0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76,
+ 0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141,
+ 0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65,
+ 0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78,
+ 0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4,
+ 0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93,
+ 0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b,
+ 0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa,
+ 0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872,
+ 0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645,
+ 0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99,
+ 0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84,
+ 0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811
+};
-#define GR_PRIME 0x9e370001
-#define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift))
+static const unsigned int U[256] = {
+ 0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a,
+ 0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48,
+ 0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511,
+ 0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d,
+ 0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8,
+ 0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe,
+ 0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb,
+ 0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937,
+ 0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e,
+ 0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c,
+ 0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d,
+ 0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1,
+ 0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4,
+ 0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa,
+ 0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef,
+ 0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263,
+ 0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302,
+ 0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000,
+ 0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59,
+ 0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5,
+ 0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90,
+ 0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7,
+ 0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2,
+ 0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e,
+ 0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467,
+ 0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765,
+ 0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604,
+ 0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88,
+ 0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd,
+ 0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3,
+ 0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996,
+ 0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a,
+ 0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b,
+ 0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609,
+ 0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50,
+ 0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc,
+ 0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99,
+ 0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf,
+ 0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa,
+ 0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176,
+ 0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f,
+ 0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d,
+ 0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a
+};
-struct index {
+struct index_entry {
const unsigned char *ptr;
unsigned int val;
- struct index *next;
+ struct index_entry *next;
+};
+
+struct delta_index {
+ const void *src_buf;
+ unsigned long src_size;
+ unsigned int hash_mask;
+ struct index_entry *hash[0];
};
-static struct index ** delta_index(const unsigned char *buf,
- unsigned long bufsize,
- unsigned long trg_bufsize,
- unsigned int *hash_shift)
+struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
{
- unsigned int i, hsize, hshift, hlimit, entries, *hash_count;
- const unsigned char *data;
- struct index *entry, **hash;
+ unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
+ const unsigned char *data, *buffer = buf;
+ struct delta_index *index;
+ struct index_entry *entry, **hash;
void *mem;
+ unsigned long memsize;
+
+ if (!buf || !bufsize)
+ return NULL;
- /* determine index hash size */
- entries = bufsize / BLK_SIZE;
+ /* Determine index hash size. Note that indexing skips the
+ first byte to allow for optimizing the rabin polynomial
+ initialization in create_delta(). */
+ entries = (bufsize - 1) / RABIN_WINDOW;
hsize = entries / 4;
for (i = 4; (1 << i) < hsize && i < 31; i++);
hsize = 1 << i;
- hshift = 32 - i;
- *hash_shift = hshift;
+ hmask = hsize - 1;
/* allocate lookup index */
- mem = malloc(hsize * sizeof(*hash) + entries * sizeof(*entry));
+ memsize = sizeof(*index) +
+ sizeof(*hash) * hsize +
+ sizeof(*entry) * entries;
+ mem = malloc(memsize);
if (!mem)
return NULL;
+ index = mem;
+ mem = index + 1;
hash = mem;
- entry = mem + hsize * sizeof(*hash);
+ mem = hash + hsize;
+ entry = mem;
+
+ index->src_buf = buf;
+ index->src_size = bufsize;
+ index->hash_mask = hmask;
memset(hash, 0, hsize * sizeof(*hash));
/* allocate an array to count hash entries */
hash_count = calloc(hsize, sizeof(*hash_count));
if (!hash_count) {
- free(hash);
+ free(index);
return NULL;
}
/* then populate the index */
- data = buf + entries * BLK_SIZE - BLK_SIZE;
- while (data >= buf) {
- unsigned int val = adler32(0, data, BLK_SIZE);
- i = HASH(val, hshift);
- entry->ptr = data;
- entry->val = val;
- entry->next = hash[i];
- hash[i] = entry++;
- hash_count[i]++;
- data -= BLK_SIZE;
- }
+ prev_val = ~0;
+ for (data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW;
+ data >= buffer;
+ data -= RABIN_WINDOW) {
+ unsigned int val = 0;
+ for (i = 1; i <= RABIN_WINDOW; i++)
+ val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
+ if (val == prev_val) {
+ /* keep the lowest of consecutive identical blocks */
+ entry[-1].ptr = data + RABIN_WINDOW;
+ } else {
+ prev_val = val;
+ i = val & hmask;
+ entry->ptr = data + RABIN_WINDOW;
+ entry->val = val;
+ entry->next = hash[i];
+ hash[i] = entry++;
+ hash_count[i]++;
+ entries--;
+ }
+ }
/*
* Determine a limit on the number of entries in the same hash
* bucket that would bring us to O(m*n) computing costs (m and n
* corresponding to reference and target buffer sizes).
*
- * The more the target buffer is large, the more it is important to
- * have small entry lists for each hash buckets. With such a limit
- * the cost is bounded to something more like O(m+n).
- */
- hlimit = (1 << 26) / trg_bufsize;
- if (hlimit < 4*BLK_SIZE)
- hlimit = 4*BLK_SIZE;
-
- /*
- * Now make sure none of the hash buckets has more entries than
+ * Make sure none of the hash buckets has more entries than
* we're willing to test. Otherwise we cull the entry list
* uniformly to still preserve a good repartition across
* the reference buffer.
*/
for (i = 0; i < hsize; i++) {
- if (hash_count[i] < hlimit)
+ if (hash_count[i] < HASH_LIMIT)
continue;
entry = hash[i];
do {
- struct index *keep = entry;
- int skip = hash_count[i] / hlimit / 2;
+ struct index_entry *keep = entry;
+ int skip = hash_count[i] / HASH_LIMIT / 2;
do {
entry = entry->next;
} while(--skip && entry);
}
free(hash_count);
- return hash;
+ /* If we didn't use all hash entries, free the unused memory. */
+ if (entries)
+ index = realloc(index, memsize - entries * sizeof(*entry));
+
+ return index;
}
-/* provide the size of the copy opcode given the block offset and size */
-#define COPYOP_SIZE(o, s) \
- (!!(o & 0xff) + !!(o & 0xff00) + !!(o & 0xff0000) + !!(o & 0xff000000) + \
- !!(s & 0xff) + !!(s & 0xff00) + 1)
+void free_delta_index(struct delta_index *index)
+{
+ free(index);
+}
-/* the maximum size for any opcode */
-#define MAX_OP_SIZE COPYOP_SIZE(0xffffffff, 0xffffffff)
+/*
+ * The maximum size for any opcode sequence, including the initial header
+ * plus rabin window plus biggest copy.
+ */
+#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7)
-void *diff_delta(void *from_buf, unsigned long from_size,
- void *to_buf, unsigned long to_size,
- unsigned long *delta_size,
- unsigned long max_size)
+void *
+create_delta(const struct delta_index *index,
+ const void *trg_buf, unsigned long trg_size,
+ unsigned long *delta_size, unsigned long max_size)
{
- unsigned int i, outpos, outsize, hash_shift;
+ unsigned int i, outpos, outsize, val;
int inscnt;
const unsigned char *ref_data, *ref_top, *data, *top;
unsigned char *out;
- struct index *entry, **hash;
- if (!from_size || !to_size)
- return NULL;
- hash = delta_index(from_buf, from_size, to_size, &hash_shift);
- if (!hash)
+ if (!trg_buf || !trg_size)
return NULL;
outpos = 0;
if (max_size && outsize >= max_size)
outsize = max_size + MAX_OP_SIZE + 1;
out = malloc(outsize);
- if (!out) {
- free(hash);
+ if (!out)
return NULL;
- }
-
- ref_data = from_buf;
- ref_top = from_buf + from_size;
- data = to_buf;
- top = to_buf + to_size;
/* store reference buffer size */
- out[outpos++] = from_size;
- from_size >>= 7;
- while (from_size) {
- out[outpos - 1] |= 0x80;
- out[outpos++] = from_size;
- from_size >>= 7;
+ i = index->src_size;
+ while (i >= 0x80) {
+ out[outpos++] = i | 0x80;
+ i >>= 7;
}
+ out[outpos++] = i;
/* store target buffer size */
- out[outpos++] = to_size;
- to_size >>= 7;
- while (to_size) {
- out[outpos - 1] |= 0x80;
- out[outpos++] = to_size;
- to_size >>= 7;
+ i = trg_size;
+ while (i >= 0x80) {
+ out[outpos++] = i | 0x80;
+ i >>= 7;
}
-
- inscnt = 0;
+ out[outpos++] = i;
+
+ ref_data = index->src_buf;
+ ref_top = ref_data + index->src_size;
+ data = trg_buf;
+ top = trg_buf + trg_size;
+
+ outpos++;
+ val = 0;
+ for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
+ out[outpos++] = *data;
+ val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+ }
+ inscnt = i;
while (data < top) {
unsigned int moff = 0, msize = 0;
- if (data + BLK_SIZE <= top) {
- unsigned int val = adler32(0, data, BLK_SIZE);
- i = HASH(val, hash_shift);
- for (entry = hash[i]; entry; entry = entry->next) {
- const unsigned char *ref = entry->ptr;
- const unsigned char *src = data;
- unsigned int ref_size = ref_top - ref;
- if (entry->val != val)
- continue;
- if (ref_size > top - src)
- ref_size = top - src;
- if (ref_size > 0x10000)
- ref_size = 0x10000;
- if (ref_size <= msize)
- break;
- while (ref_size-- && *src++ == *ref)
- ref++;
- if (msize < ref - entry->ptr) {
- /* this is our best match so far */
- msize = ref - entry->ptr;
- moff = entry->ptr - ref_data;
- }
+ struct index_entry *entry;
+ val ^= U[data[-RABIN_WINDOW]];
+ val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+ i = val & index->hash_mask;
+ for (entry = index->hash[i]; entry; entry = entry->next) {
+ const unsigned char *ref = entry->ptr;
+ const unsigned char *src = data;
+ unsigned int ref_size = ref_top - ref;
+ if (entry->val != val)
+ continue;
+ if (ref_size > top - src)
+ ref_size = top - src;
+ if (ref_size > 0x10000)
+ ref_size = 0x10000;
+ if (ref_size <= msize)
+ break;
+ while (ref_size-- && *src++ == *ref)
+ ref++;
+ if (msize < ref - entry->ptr) {
+ /* this is our best match so far */
+ msize = ref - entry->ptr;
+ moff = entry->ptr - ref_data;
}
}
- if (!msize || msize < COPYOP_SIZE(moff, msize)) {
+ if (msize < 4) {
if (!inscnt)
outpos++;
out[outpos++] = *data++;
} else {
unsigned char *op;
+ if (msize >= RABIN_WINDOW) {
+ const unsigned char *sk;
+ sk = data + msize - RABIN_WINDOW;
+ val = 0;
+ for (i = 0; i < RABIN_WINDOW; i++)
+ val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
+ } else {
+ const unsigned char *sk = data + 1;
+ for (i = 1; i < msize; i++) {
+ val ^= U[sk[-RABIN_WINDOW]];
+ val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
+ }
+ }
+
if (inscnt) {
while (moff && ref_data[moff-1] == data[-1]) {
if (msize == 0x10000)
if (max_size && outsize >= max_size)
outsize = max_size + MAX_OP_SIZE + 1;
if (max_size && outpos > max_size)
- out = NULL;
- else
- out = realloc(out, outsize);
+ break;
+ out = realloc(out, outsize);
if (!out) {
free(tmp);
- free(hash);
return NULL;
}
}
if (inscnt)
out[outpos - inscnt - 1] = inscnt;
- free(hash);
+ if (max_size && outpos > max_size) {
+ free(out);
+ return NULL;
+ }
+
*delta_size = outpos;
return out;
}
#include "quote.h"
#include "diff.h"
#include "diffcore.h"
+#include "delta.h"
#include "xdiff-interface.h"
static int use_size_cache;
static void show_stats(struct diffstat_t* data)
{
- char *prefix = "";
int i, len, add, del, total, adds = 0, dels = 0;
int max, max_change = 0, max_len = 0;
int total_files = data->nr;
}
for (i = 0; i < data->nr; i++) {
+ char *prefix = "";
char *name = data->files[i]->name;
int added = data->files[i]->added;
int deleted = data->files[i]->deleted;
total_files, adds, dels);
}
+static unsigned char *deflate_it(char *data,
+ unsigned long size,
+ unsigned long *result_size)
+{
+ int bound;
+ unsigned char *deflated;
+ z_stream stream;
+
+ memset(&stream, 0, sizeof(stream));
+ deflateInit(&stream, Z_BEST_COMPRESSION);
+ bound = deflateBound(&stream, size);
+ deflated = xmalloc(bound);
+ stream.next_out = deflated;
+ stream.avail_out = bound;
+
+ stream.next_in = (unsigned char *)data;
+ stream.avail_in = size;
+ while (deflate(&stream, Z_FINISH) == Z_OK)
+ ; /* nothing */
+ deflateEnd(&stream);
+ *result_size = stream.total_out;
+ return deflated;
+}
+
+static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+{
+ void *cp;
+ void *delta;
+ void *deflated;
+ void *data;
+ unsigned long orig_size;
+ unsigned long delta_size;
+ unsigned long deflate_size;
+ unsigned long data_size;
+
+ printf("GIT binary patch\n");
+ /* We could do deflated delta, or we could do just deflated two,
+ * whichever is smaller.
+ */
+ delta = NULL;
+ deflated = deflate_it(two->ptr, two->size, &deflate_size);
+ if (one->size && two->size) {
+ delta = diff_delta(one->ptr, one->size,
+ two->ptr, two->size,
+ &delta_size, deflate_size);
+ if (delta) {
+ void *to_free = delta;
+ orig_size = delta_size;
+ delta = deflate_it(delta, delta_size, &delta_size);
+ free(to_free);
+ }
+ }
+
+ if (delta && delta_size < deflate_size) {
+ printf("delta %lu\n", orig_size);
+ free(deflated);
+ data = delta;
+ data_size = delta_size;
+ }
+ else {
+ printf("literal %lu\n", two->size);
+ free(delta);
+ data = deflated;
+ data_size = deflate_size;
+ }
+
+ /* emit data encoded in base85 */
+ cp = data;
+ while (data_size) {
+ int bytes = (52 < data_size) ? 52 : data_size;
+ char line[70];
+ data_size -= bytes;
+ if (bytes <= 26)
+ line[0] = bytes + 'A' - 1;
+ else
+ line[0] = bytes - 26 + 'a' - 1;
+ encode_85(line + 1, cp, bytes);
+ cp += bytes;
+ puts(line);
+ }
+ printf("\n");
+ free(data);
+}
+
#define FIRST_FEW_BYTES 8000
static int mmfile_is_binary(mmfile_t *mf)
{
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
+ struct diff_options *o,
int complete_rewrite)
{
mmfile_t mf1, mf2;
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
- printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
+ if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+ /* Quite common confusing case */
+ if (mf1.size == mf2.size &&
+ !memcmp(mf1.ptr, mf2.ptr, mf1.size))
+ goto free_ab_and_return;
+ if (o->binary)
+ emit_binary_diff(&mf1, &mf2);
+ else
+ printf("Binary files %s and %s differ\n",
+ lbl[0], lbl[1]);
+ }
else {
/* Crazy xdl interfaces.. */
const char *diffopts = getenv("GIT_DIFF_OPTS");
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
+ struct diff_options *o,
int complete_rewrite)
{
if (pgm) {
}
if (one && two)
builtin_diff(name, other ? other : name,
- one, two, xfrm_msg, complete_rewrite);
+ one, two, xfrm_msg, o, complete_rewrite);
else
printf("* Unmerged path %s\n", name);
}
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
- run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+ run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
return;
}
}
if (memcmp(one->sha1, two->sha1, 20)) {
- char one_sha1[41];
int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
- memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
len += snprintf(msg + len, sizeof(msg) - len,
"index %.*s..%.*s",
- abbrev, one_sha1, abbrev,
- sha1_to_hex(two->sha1));
+ abbrev, sha1_to_hex(one->sha1),
+ abbrev, sha1_to_hex(two->sha1));
if (one->mode == two->mode)
len += snprintf(msg + len, sizeof(msg) - len,
" %06o", one->mode);
* needs to be split into deletion and creation.
*/
struct diff_filespec *null = alloc_filespec(two->path);
- run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+ run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
free(null);
null = alloc_filespec(one->path);
- run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+ run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
free(null);
}
else
- run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
+ run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
complete_rewrite);
free(name_munged);
options->rename_limit = strtoul(arg+2, NULL, 10);
else if (!strcmp(arg, "--full-index"))
options->full_index = 1;
+ else if (!strcmp(arg, "--binary")) {
+ options->output_format = DIFF_FORMAT_PATCH;
+ options->full_index = options->binary = 1;
+ }
else if (!strcmp(arg, "--name-only"))
options->output_format = DIFF_FORMAT_NAME;
else if (!strcmp(arg, "--name-status"))
with_raw:1,
with_stat:1,
tree_in_recursive:1,
+ binary:1,
full_index:1,
silent_on_remove:1,
find_copies_harder:1;
extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
int dense, struct rev_info *);
+extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev);
+
extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
extern void diff_addremove(struct diff_options *,
char git_default_name[MAX_GITNAME];
int trust_executable_bit = 1;
int assume_unchanged = 0;
-int only_use_symrefs = 0;
+int prefer_symlink_refs = 0;
int warn_ambiguous_refs = 1;
int repository_format_version = 0;
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
exit 1
}
+stop_here_user_resolve () {
+ cmdline=$(basename $0)
+ if test '' != "$interactive"
+ then
+ cmdline="$cmdline -i"
+ fi
+ if test '' != "$threeway"
+ then
+ cmdline="$cmdline -3"
+ fi
+ if test '.dotest' != "$dotest"
+ then
+ cmdline="$cmdline -d=$dotest"
+ fi
+ echo "When you have resolved this problem run \"$cmdline --resolved\"."
+ echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
+
+ stop_here $1
+}
+
go_next () {
rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
"$dotest/patch" "$dotest/info"
if test '' = "$changed"
then
echo "No changes - did you forget update-index?"
- stop_here $this
+ stop_here_user_resolve $this
fi
unmerged=$(git-ls-files -u)
if test -n "$unmerged"
then
echo "You still have unmerged paths in your index"
echo "did you forget update-index?"
- stop_here $this
+ stop_here_user_resolve $this
fi
apply_status=0
;;
if test $apply_status != 0
then
echo Patch failed at $msgnum.
- stop_here $this
+ stop_here_user_resolve $this
fi
if test -x "$GIT_DIR"/hooks/pre-applypatch
test -f "$repo/objects/info/alternates" &&
cat "$repo/objects/info/alternates";
echo "$repo/objects"
- } >"$GIT_DIR/objects/info/alternates"
+ } >>"$GIT_DIR/objects/info/alternates"
;;
esac
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-GIT_DIR=`git-rev-parse --git-dir` || exit $?
-
-dc </dev/null 2>/dev/null || {
- # This is not a real DC at all -- it just knows how
- # this script feeds DC and does the computation itself.
- dc () {
- while read a b
- do
- case $a,$b in
- 0,) acc=0 ;;
- *,+) acc=$(($acc + $a)) ;;
- p,) echo "$acc" ;;
- esac
- done
- }
-}
-
-echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \
-$({
- echo 0
- # "no-such" is to help Darwin folks by not using xargs -r.
- find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null |
- xargs du -k "$GIT_DIR/objects/no-such" 2>/dev/null |
- sed -e 's/[ ].*/ +/'
- echo p
-} | dc) kilobytes
# TODO: log processing is memory bound
# if we can parse into a 2nd file that is in reverse order
# we can probably do something really efficient
- my @git_log_params = ('--parents', '--topo-order');
+ my @git_log_params = ('--pretty', '--parents', '--topo-order');
if (defined $lastcommit) {
push @git_log_params, "$lastcommit..$self->{module}";
} else {
push @git_log_params, $self->{module};
}
- open(GITLOG, '-|', 'git-log', @git_log_params) or die "Cannot call git-log: $!";
+ # git-rev-list is the backend / plumbing version of git-log
+ open(GITLOG, '-|', 'git-rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
my @commits;
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-# Copyright (c) 2005 Junio C Hamano
-
-USAGE='[ --diff-options ] <ent>{0,2} [<path>...]'
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-rev=$(git-rev-parse --revs-only --no-flags --sq "$@") || exit
-flags=$(git-rev-parse --no-revs --flags --sq "$@")
-files=$(git-rev-parse --no-revs --no-flags --sq "$@")
-
-# I often say 'git diff --cached -p' and get scolded by git-diff-files, but
-# obviously I mean 'git diff --cached -p HEAD' in that case.
-case "$rev" in
-'')
- case " $flags " in
- *" '--cached' "*)
- rev='HEAD '
- ;;
- esac
-esac
-
-# If we have -[123] --ours --theirs --base, don't do --cc by default.
-case " $flags " in
-*" '-"[123]"' "* | *" '--ours' "* | *" '--base' "* | *" '--theirs' "*)
- cc_or_p=-p ;;
-*)
- cc_or_p=--cc ;;
-esac
-
-# If we do not have --name-status, --name-only, -r, -c or --stat,
-# default to --cc.
-case " $flags " in
-*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* | \
-*" '--stat' "*)
- ;;
-*)
- flags="$flags'$cc_or_p' " ;;
-esac
-
-# If we do not have -B, -C, -r, nor -p, default to -M.
-case " $flags " in
-*" '-"[BCMrp]* | *" '--find-copies-harder' "*)
- ;; # something like -M50.
-*)
- flags="$flags'-M' " ;;
-esac
-
-case "$rev" in
-?*' '?*' '?*)
- usage
- ;;
-?*' '^?*)
- begin=$(expr "$rev" : '.*^.\([0-9a-f]*\).*') &&
- end=$(expr "$rev" : '.\([0-9a-f]*\). .*') || exit
- cmd="git-diff-tree $flags $begin $end -- $files"
- ;;
-?*' '?*)
- cmd="git-diff-tree $flags $rev -- $files"
- ;;
-?*' ')
- cmd="git-diff-index $flags $rev -- $files"
- ;;
-'')
- cmd="git-diff-files $flags -- $files"
- ;;
-*)
- usage
- ;;
-esac
-
-eval "$cmd"
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k"
fi
- remote_name_quoted=$(perl -e '
+ max_depth=5
+ depth=0
+ head="ref: $remote_name"
+ while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
+ do
+ remote_name_quoted=$(perl -e '
my $u = $ARGV[0];
+ $u =~ s/^ref:\s*//;
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
print "$u";
- ' "$remote_name")
- head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
+ ' "$head")
+ head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted")
+ depth=$( expr \( $depth + 1 \) )
+ done
expr "z$head" : "z$_x40\$" >/dev/null ||
- die "Failed to fetch $remote_name from $remote"
+ die "Failed to fetch $remote_name from $remote"
echo >&2 Fetching "$remote_name from $remote" using http
git-http-fetch -v -a "$head" "$remote/" || exit
;;
#
USAGE='[--onto <newbase>] <upstream> [<branch>]'
-LONG_USAGE='git-rebase applies to <upstream> (or optionally to <newbase>) commits
-from <branch> that do not appear in <upstream>. When <branch> is not
-specified it defaults to the current branch (HEAD).
-
-When git-rebase is complete, <branch> will be updated to point to the
-newly created line of commit objects, so the previous line will not be
-accessible unless there are other references to it already.
-
-Assuming the following history:
-
- A---B---C topic
- /
- D---E---F---G master
-
-The result of the following command:
-
- git-rebase --onto master~1 master topic
-
- would be:
-
- A'\''--B'\''--C'\'' topic
- /
- D---E---F---G master
+LONG_USAGE='git-rebase replaces <branch> with a new branch of the
+same name. When the --onto option is provided the new branch starts
+out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
+It then attempts to create a new commit for each commit from the original
+<branch> that does not exist in the <upstream> branch.
+
+It is possible that a merge failure will prevent this process from being
+completely automatic. You will have to resolve any such merge failure
+and run git-rebase --continue. If you can not resolve the merge failure,
+running git-rebase --abort will restore the original <branch> and remove
+the working files found in the .dotest directory.
+
+Note that if <branch> is not specified on the command line, the
+currently checked out branch is used. You must be in the top
+directory of your project to start (or continue) a rebase.
+
+Example: git-rebase master~1 topic
+
+ A---B---C topic A'\''--B'\''--C'\'' topic
+ / --> /
+ D---E---F---G master D---E---F---G master
'
-
. git-sh-setup
unset newbase
while case "$#" in 0) break ;; esac
do
case "$1" in
+ --continue)
+ diff=$(git-diff-files)
+ case "$diff" in
+ ?*) echo "You must edit all merge conflicts and then"
+ echo "mark them as resolved using git update-index"
+ exit 1
+ ;;
+ esac
+ git am --resolved --3way
+ exit
+ ;;
+ --abort)
+ [ -d .dotest ] || die "No rebase in progress?"
+ git reset --hard ORIG_HEAD
+ rm -r .dotest
+ exit
+ ;;
--onto)
test 2 -le "$#" || usage
newbase="$2"
exit 1
if [ -z "$name" ]; then
echo Nothing new to pack.
- exit 0
-fi
-echo "Pack pack-$name created."
+else
+ echo "Pack pack-$name created."
-mkdir -p "$PACKDIR" || exit
+ mkdir -p "$PACKDIR" || exit
-mv .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
-mv .tmp-pack-$name.idx "$PACKDIR/pack-$name.idx" ||
-exit
+ mv .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
+ mv .tmp-pack-$name.idx "$PACKDIR/pack-$name.idx" ||
+ exit
+fi
if test "$remove_redundant" = t
then
my $to = join (",\n\t", @recipients);
@recipients = unique_email_list(@recipients,@cc);
my $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++));
+ my $gitversion = '@@GIT_VERSION@@';
+ if ($gitversion =~ m/..GIT_VERSION../) {
+ $gitversion = `git --version`;
+ chomp $gitversion;
+ # keep only what's after the last space
+ $gitversion =~ s/^.* //;
+ }
my $header = "From: $from
To: $to
Reply-To: $from
Date: $date
Message-Id: $message_id
-X-Mailer: git-send-email @@GIT_VERSION@@
+X-Mailer: git-send-email $gitversion
";
$header .= "In-Reply-To: $reply_to\n" if $reply_to;
+++ /dev/null
-#!/bin/sh
-
-USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-diff-tree options] [git-rev-list options]'
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit
-case "$0" in
-*whatchanged)
- count=
- test -z "$diff_tree_flags" &&
- diff_tree_flags=$(git-repo-config --get whatchanged.difftree)
- diff_tree_default_flags='-c -M --abbrev' ;;
-*show)
- count=-n1
- test -z "$diff_tree_flags" &&
- diff_tree_flags=$(git-repo-config --get show.difftree)
- diff_tree_default_flags='--cc --always' ;;
-esac
-test -z "$diff_tree_flags" &&
- diff_tree_flags="$diff_tree_default_flags"
-
-rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") &&
-diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") &&
-
-eval "git-rev-list $count $rev_list_args" |
-eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" |
-LESS="$LESS -S" ${PAGER:-less}
{ "log", cmd_log },
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
+ { "push", cmd_push },
+ { "count-objects", cmd_count_objects },
+ { "diff", cmd_diff },
};
int i;
}
}
-proc start_rev_list {rlargs} {
+proc start_rev_list {view} {
global startmsecs nextupdate ncmupdate
global commfd leftover tclencoding datemode
+ global viewargs viewfiles commitidx
set startmsecs [clock clicks -milliseconds]
set nextupdate [expr {$startmsecs + 100}]
set ncmupdate 1
- initlayout
+ set commitidx($view) 0
+ set args $viewargs($view)
+ if {$viewfiles($view) ne {}} {
+ set args [concat $args "--" $viewfiles($view)]
+ }
set order "--topo-order"
if {$datemode} {
set order "--date-order"
}
if {[catch {
- set commfd [open [concat | git-rev-list --header $order \
- --parents --boundary --default HEAD $rlargs] r]
+ set fd [open [concat | git-rev-list --header $order \
+ --parents --boundary --default HEAD $args] r]
} err]} {
puts stderr "Error executing git-rev-list: $err"
exit 1
}
- set leftover {}
- fconfigure $commfd -blocking 0 -translation lf
+ set commfd($view) $fd
+ set leftover($view) {}
+ fconfigure $fd -blocking 0 -translation lf
if {$tclencoding != {}} {
- fconfigure $commfd -encoding $tclencoding
+ fconfigure $fd -encoding $tclencoding
+ }
+ fileevent $fd readable [list getcommitlines $fd $view]
+ nowbusy $view
+}
+
+proc stop_rev_list {} {
+ global commfd curview
+
+ if {![info exists commfd($curview)]} return
+ set fd $commfd($curview)
+ catch {
+ set pid [pid $fd]
+ exec kill $pid
}
- fileevent $commfd readable [list getcommitlines $commfd]
- . config -cursor watch
- settextcursor watch
+ catch {close $fd}
+ unset commfd($curview)
}
-proc getcommits {rargs} {
- global phase canv mainfont
+proc getcommits {} {
+ global phase canv mainfont curview
set phase getcommits
- start_rev_list $rargs
- $canv delete all
- $canv create text 3 3 -anchor nw -text "Reading commits..." \
- -font $mainfont -tags textitems
+ initlayout
+ start_rev_list $curview
+ show_status "Reading commits..."
}
-proc getcommitlines {commfd} {
+proc getcommitlines {fd view} {
global commitlisted nextupdate
- global leftover
+ global leftover commfd
global displayorder commitidx commitrow commitdata
- global parentlist childlist children
+ global parentlist childlist children curview hlview
+ global vparentlist vchildlist vdisporder vcmitlisted
- set stuff [read $commfd]
+ set stuff [read $fd]
if {$stuff == {}} {
- if {![eof $commfd]} return
+ if {![eof $fd]} return
+ global viewname
+ unset commfd($view)
+ notbusy $view
# set it blocking so we wait for the process to terminate
- fconfigure $commfd -blocking 1
- if {![catch {close $commfd} err]} {
- after idle finishcommits
- return
+ fconfigure $fd -blocking 1
+ if {[catch {close $fd} err]} {
+ set fv {}
+ if {$view != $curview} {
+ set fv " for the \"$viewname($view)\" view"
+ }
+ if {[string range $err 0 4] == "usage"} {
+ set err "Gitk: error reading commits$fv:\
+ bad arguments to git-rev-list."
+ if {$viewname($view) eq "Command line"} {
+ append err \
+ " (Note: arguments to gitk are passed to git-rev-list\
+ to allow selection of commits to be displayed.)"
+ }
+ } else {
+ set err "Error reading commits$fv: $err"
+ }
+ error_popup $err
}
- if {[string range $err 0 4] == "usage"} {
- set err \
- "Gitk: error reading commits: bad arguments to git-rev-list.\
- (Note: arguments to gitk are passed to git-rev-list\
- to allow selection of commits to be displayed.)"
- } else {
- set err "Error reading commits: $err"
+ if {$view == $curview} {
+ after idle finishcommits
}
- error_popup $err
- exit 1
+ return
}
set start 0
set gotsome 0
while 1 {
set i [string first "\0" $stuff $start]
if {$i < 0} {
- append leftover [string range $stuff $start end]
+ append leftover($view) [string range $stuff $start end]
break
}
if {$start == 0} {
- set cmit $leftover
+ set cmit $leftover($view)
append cmit [string range $stuff 0 [expr {$i - 1}]]
- set leftover {}
+ set leftover($view) {}
} else {
set cmit [string range $stuff $start [expr {$i - 1}]]
}
set id [lindex $ids 0]
if {$listed} {
set olds [lrange $ids 1 end]
- if {[llength $olds] > 1} {
- set olds [lsort -unique $olds]
- }
+ set i 0
foreach p $olds {
- lappend children($p) $id
+ if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
+ lappend children($view,$p) $id
+ }
+ incr i
}
} else {
set olds {}
}
- lappend parentlist $olds
- if {[info exists children($id)]} {
- lappend childlist $children($id)
- } else {
- lappend childlist {}
+ if {![info exists children($view,$id)]} {
+ set children($view,$id) {}
}
set commitdata($id) [string range $cmit [expr {$j + 1}] end]
- set commitrow($id) $commitidx
- incr commitidx
- lappend displayorder $id
- lappend commitlisted $listed
+ set commitrow($view,$id) $commitidx($view)
+ incr commitidx($view)
+ if {$view == $curview} {
+ lappend parentlist $olds
+ lappend childlist $children($view,$id)
+ lappend displayorder $id
+ lappend commitlisted $listed
+ } else {
+ lappend vparentlist($view) $olds
+ lappend vchildlist($view) $children($view,$id)
+ lappend vdisporder($view) $id
+ lappend vcmitlisted($view) $listed
+ }
set gotsome 1
}
if {$gotsome} {
- layoutmore
+ if {$view == $curview} {
+ layoutmore
+ } elseif {[info exists hlview] && $view == $hlview} {
+ highlightmore
+ }
}
if {[clock clicks -milliseconds] >= $nextupdate} {
- doupdate 1
+ doupdate
}
}
-proc doupdate {reading} {
+proc doupdate {} {
global commfd nextupdate numcommits ncmupdate
- if {$reading} {
- fileevent $commfd readable {}
+ foreach v [array names commfd] {
+ fileevent $commfd($v) readable {}
}
update
set nextupdate [expr {[clock clicks -milliseconds] + 100}]
} else {
set ncmupdate [expr {$numcommits + 100}]
}
- if {$reading} {
- fileevent $commfd readable [list getcommitlines $commfd]
+ foreach v [array names commfd] {
+ set fd $commfd($v)
+ fileevent $fd readable [list getcommitlines $fd $v]
}
}
parsecommit $id $contents 0
}
-proc updatecommits {rargs} {
- stopfindproc
- foreach v {colormap selectedline matchinglines treediffs
- mergefilelist currentid rowtextx commitrow
- rowidlist rowoffsets idrowranges idrangedrawn iddrawn
- linesegends crossings cornercrossings} {
- global $v
- catch {unset $v}
+proc updatecommits {} {
+ global viewdata curview phase displayorder
+ global children commitrow
+
+ if {$phase ne {}} {
+ stop_rev_list
+ set phase {}
}
- allcanvs delete all
+ set n $curview
+ foreach id $displayorder {
+ catch {unset children($n,$id)}
+ catch {unset commitrow($n,$id)}
+ }
+ set curview -1
+ catch {unset viewdata($n)}
readrefs
- getcommits $rargs
+ showview $n
}
proc parsecommit {id contents listed} {
match id path]} {
continue
}
+ if {[regexp {^remotes/.*/HEAD$} $path match]} {
+ continue
+ }
if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
set type others
set name $path
}
+ if {[regexp {^remotes/} $path match]} {
+ set type heads
+ }
if {$type == "tags"} {
set tagids($name) $id
lappend idtags($id) $name
close $refd
}
-proc error_popup msg {
- set w .error
- toplevel $w
- wm transient $w .
+proc show_error {w msg} {
message $w.m -text $msg -justify center -aspect 400
pack $w.m -side top -fill x -padx 20 -pady 20
button $w.ok -text OK -command "destroy $w"
tkwait window $w
}
-proc makewindow {rargs} {
- global canv canv2 canv3 linespc charspc ctext cflist textfont mainfont uifont
+proc error_popup msg {
+ set w .error
+ toplevel $w
+ wm transient $w .
+ show_error $w $msg
+}
+
+proc makewindow {} {
+ global canv canv2 canv3 linespc charspc ctext cflist
+ global textfont mainfont uifont
global findtype findtypemenu findloc findstring fstring geometry
global entries sha1entry sha1string sha1but
global maincursor textcursor curtextcursor
.bar add cascade -label "File" -menu .bar.file
.bar configure -font $uifont
menu .bar.file
- .bar.file add command -label "Update" -command [list updatecommits $rargs]
+ .bar.file add command -label "Update" -command updatecommits
.bar.file add command -label "Reread references" -command rereadrefs
.bar.file add command -label "Quit" -command doquit
.bar.file configure -font $uifont
.bar add cascade -label "Edit" -menu .bar.edit
.bar.edit add command -label "Preferences" -command doprefs
.bar.edit configure -font $uifont
+
+ menu .bar.view -font $uifont
+ menu .bar.view.hl -font $uifont -tearoff 0
+ .bar add cascade -label "View" -menu .bar.view
+ .bar.view add command -label "New view..." -command {newview 0}
+ .bar.view add command -label "Edit view..." -command editview \
+ -state disabled
+ .bar.view add command -label "Delete view" -command delview -state disabled
+ .bar.view add cascade -label "Highlight" -menu .bar.view.hl
+ .bar.view add separator
+ .bar.view add radiobutton -label "All files" -command {showview 0} \
+ -variable selectedview -value 0
+ .bar.view.hl add command -label "New view..." -command {newview 1}
+ .bar.view.hl add command -label "Remove" -command delhighlight \
+ -state disabled
+ .bar.view.hl add separator
+
menu .bar.help
.bar add cascade -label "Help" -menu .bar.help
.bar.help add command -label "About gitk" -command about
set ctext .ctop.cdet.left.ctext
text $ctext -bg white -state disabled -font $textfont \
-width $geometry(ctextw) -height $geometry(ctexth) \
- -yscrollcommand ".ctop.cdet.left.sb set" -wrap none
+ -yscrollcommand {.ctop.cdet.left.sb set} -wrap none
scrollbar .ctop.cdet.left.sb -command "$ctext yview"
pack .ctop.cdet.left.sb -side right -fill y
pack $ctext -side left -fill both -expand 1
$ctext tag conf found -back yellow
frame .ctop.cdet.right
+ frame .ctop.cdet.right.mode
+ radiobutton .ctop.cdet.right.mode.patch -text "Patch" \
+ -command reselectline -variable cmitmode -value "patch"
+ radiobutton .ctop.cdet.right.mode.tree -text "Tree" \
+ -command reselectline -variable cmitmode -value "tree"
+ grid .ctop.cdet.right.mode.patch .ctop.cdet.right.mode.tree -sticky ew
+ pack .ctop.cdet.right.mode -side top -fill x
set cflist .ctop.cdet.right.cfiles
- listbox $cflist -bg white -selectmode extended -width $geometry(cflistw) \
- -yscrollcommand ".ctop.cdet.right.sb set" -font $mainfont
+ set indent [font measure $mainfont "nn"]
+ text $cflist -width $geometry(cflistw) -background white -font $mainfont \
+ -tabs [list $indent [expr {2 * $indent}]] \
+ -yscrollcommand ".ctop.cdet.right.sb set" \
+ -cursor [. cget -cursor] \
+ -spacing1 1 -spacing3 1
scrollbar .ctop.cdet.right.sb -command "$cflist yview"
pack .ctop.cdet.right.sb -side right -fill y
pack $cflist -side left -fill both -expand 1
+ $cflist tag configure highlight \
+ -background [$cflist cget -selectbackground]
.ctop.cdet add .ctop.cdet.right
bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
bind . <Control-KP_Add> {incrfont 1}
bind . <Control-minus> {incrfont -1}
bind . <Control-KP_Subtract> {incrfont -1}
- bind $cflist <<ListboxSelect>> listboxsel
bind . <Destroy> {savestuff %W}
bind . <Button-1> "click %W"
bind $fstring <Key-Return> dofind
bind $sha1entry <Key-Return> gotocommit
bind $sha1entry <<PasteSelection>> clearsha1
+ bind $cflist <1> {sel_flist %W %x %y; break}
+ bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
+ bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
set maincursor [. cget -cursor]
set textcursor [$ctext cget -cursor]
global canv canv2 canv3 ctext cflist mainfont textfont uifont
global stuffsaved findmergefiles maxgraphpct
global maxwidth
+ global viewname viewfiles viewargs viewperm nextviewnum
+ global cmitmode
if {$stuffsaved} return
if {![winfo viewable .]} return
puts $f [list set findmergefiles $findmergefiles]
puts $f [list set maxgraphpct $maxgraphpct]
puts $f [list set maxwidth $maxwidth]
+ puts $f [list set cmitmode $cmitmode]
puts $f "set geometry(width) [winfo width .ctop]"
puts $f "set geometry(height) [winfo height .ctop]"
puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
set wid [expr {([winfo width $cflist] - 11) \
/ [font measure [$cflist cget -font] "0"]}]
puts $f "set geometry(cflistw) $wid"
+ puts -nonewline $f "set permviews {"
+ for {set v 0} {$v < $nextviewnum} {incr v} {
+ if {$viewperm($v)} {
+ puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
+ }
+ }
+ puts $f "}"
close $f
file rename -force "~/.gitk-new" "~/.gitk"
}
pack $w.ok -side bottom
}
+# Procedures for manipulating the file list window at the
+# bottom right of the overall window.
+
+proc treeview {w l openlevs} {
+ global treecontents treediropen treeheight treeparent treeindex
+
+ set ix 0
+ set treeindex() 0
+ set lev 0
+ set prefix {}
+ set prefixend -1
+ set prefendstack {}
+ set htstack {}
+ set ht 0
+ set treecontents() {}
+ $w conf -state normal
+ foreach f $l {
+ while {[string range $f 0 $prefixend] ne $prefix} {
+ if {$lev <= $openlevs} {
+ $w mark set e:$treeindex($prefix) "end -1c"
+ $w mark gravity e:$treeindex($prefix) left
+ }
+ set treeheight($prefix) $ht
+ incr ht [lindex $htstack end]
+ set htstack [lreplace $htstack end end]
+ set prefixend [lindex $prefendstack end]
+ set prefendstack [lreplace $prefendstack end end]
+ set prefix [string range $prefix 0 $prefixend]
+ incr lev -1
+ }
+ set tail [string range $f [expr {$prefixend+1}] end]
+ while {[set slash [string first "/" $tail]] >= 0} {
+ lappend htstack $ht
+ set ht 0
+ lappend prefendstack $prefixend
+ incr prefixend [expr {$slash + 1}]
+ set d [string range $tail 0 $slash]
+ lappend treecontents($prefix) $d
+ set oldprefix $prefix
+ append prefix $d
+ set treecontents($prefix) {}
+ set treeindex($prefix) [incr ix]
+ set treeparent($prefix) $oldprefix
+ set tail [string range $tail [expr {$slash+1}] end]
+ if {$lev <= $openlevs} {
+ set ht 1
+ set treediropen($prefix) [expr {$lev < $openlevs}]
+ set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
+ $w mark set d:$ix "end -1c"
+ $w mark gravity d:$ix left
+ set str "\n"
+ for {set i 0} {$i < $lev} {incr i} {append str "\t"}
+ $w insert end $str
+ $w image create end -align center -image $bm -padx 1 \
+ -name a:$ix
+ $w insert end $d
+ $w mark set s:$ix "end -1c"
+ $w mark gravity s:$ix left
+ }
+ incr lev
+ }
+ if {$tail ne {}} {
+ if {$lev <= $openlevs} {
+ incr ht
+ set str "\n"
+ for {set i 0} {$i < $lev} {incr i} {append str "\t"}
+ $w insert end $str
+ $w insert end $tail
+ }
+ lappend treecontents($prefix) $tail
+ }
+ }
+ while {$htstack ne {}} {
+ set treeheight($prefix) $ht
+ incr ht [lindex $htstack end]
+ set htstack [lreplace $htstack end end]
+ }
+ $w conf -state disabled
+}
+
+proc linetoelt {l} {
+ global treeheight treecontents
+
+ set y 2
+ set prefix {}
+ while {1} {
+ foreach e $treecontents($prefix) {
+ if {$y == $l} {
+ return "$prefix$e"
+ }
+ set n 1
+ if {[string index $e end] eq "/"} {
+ set n $treeheight($prefix$e)
+ if {$y + $n > $l} {
+ append prefix $e
+ incr y
+ break
+ }
+ }
+ incr y $n
+ }
+ }
+}
+
+proc treeclosedir {w dir} {
+ global treediropen treeheight treeparent treeindex
+
+ set ix $treeindex($dir)
+ $w conf -state normal
+ $w delete s:$ix e:$ix
+ set treediropen($dir) 0
+ $w image configure a:$ix -image tri-rt
+ $w conf -state disabled
+ set n [expr {1 - $treeheight($dir)}]
+ while {$dir ne {}} {
+ incr treeheight($dir) $n
+ set dir $treeparent($dir)
+ }
+}
+
+proc treeopendir {w dir} {
+ global treediropen treeheight treeparent treecontents treeindex
+
+ set ix $treeindex($dir)
+ $w conf -state normal
+ $w image configure a:$ix -image tri-dn
+ $w mark set e:$ix s:$ix
+ $w mark gravity e:$ix right
+ set lev 0
+ set str "\n"
+ set n [llength $treecontents($dir)]
+ for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
+ incr lev
+ append str "\t"
+ incr treeheight($x) $n
+ }
+ foreach e $treecontents($dir) {
+ if {[string index $e end] eq "/"} {
+ set de $dir$e
+ set iy $treeindex($de)
+ $w mark set d:$iy e:$ix
+ $w mark gravity d:$iy left
+ $w insert e:$ix $str
+ set treediropen($de) 0
+ $w image create e:$ix -align center -image tri-rt -padx 1 \
+ -name a:$iy
+ $w insert e:$ix $e
+ $w mark set s:$iy e:$ix
+ $w mark gravity s:$iy left
+ set treeheight($de) 1
+ } else {
+ $w insert e:$ix $str
+ $w insert e:$ix $e
+ }
+ }
+ $w mark gravity e:$ix left
+ $w conf -state disabled
+ set treediropen($dir) 1
+ set top [lindex [split [$w index @0,0] .] 0]
+ set ht [$w cget -height]
+ set l [lindex [split [$w index s:$ix] .] 0]
+ if {$l < $top} {
+ $w yview $l.0
+ } elseif {$l + $n + 1 > $top + $ht} {
+ set top [expr {$l + $n + 2 - $ht}]
+ if {$l < $top} {
+ set top $l
+ }
+ $w yview $top.0
+ }
+}
+
+proc treeclick {w x y} {
+ global treediropen cmitmode ctext cflist cflist_top
+
+ if {$cmitmode ne "tree"} return
+ if {![info exists cflist_top]} return
+ set l [lindex [split [$w index "@$x,$y"] "."] 0]
+ $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+ $cflist tag add highlight $l.0 "$l.0 lineend"
+ set cflist_top $l
+ if {$l == 1} {
+ $ctext yview 1.0
+ return
+ }
+ set e [linetoelt $l]
+ if {[string index $e end] ne "/"} {
+ showfile $e
+ } elseif {$treediropen($e)} {
+ treeclosedir $w $e
+ } else {
+ treeopendir $w $e
+ }
+}
+
+proc setfilelist {id} {
+ global treefilelist cflist
+
+ treeview $cflist $treefilelist($id) 0
+}
+
+image create bitmap tri-rt -background black -foreground blue -data {
+ #define tri-rt_width 13
+ #define tri-rt_height 13
+ static unsigned char tri-rt_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
+ 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
+} -maskdata {
+ #define tri-rt-mask_width 13
+ #define tri-rt-mask_height 13
+ static unsigned char tri-rt-mask_bits[] = {
+ 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
+ 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
+ 0x08, 0x00};
+}
+image create bitmap tri-dn -background black -foreground blue -data {
+ #define tri-dn_width 13
+ #define tri-dn_height 13
+ static unsigned char tri-dn_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
+ 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
+} -maskdata {
+ #define tri-dn-mask_width 13
+ #define tri-dn-mask_height 13
+ static unsigned char tri-dn-mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
+ 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
+}
+
+proc init_flist {first} {
+ global cflist cflist_top selectedline difffilestart
+
+ $cflist conf -state normal
+ $cflist delete 0.0 end
+ if {$first ne {}} {
+ $cflist insert end $first
+ set cflist_top 1
+ $cflist tag add highlight 1.0 "1.0 lineend"
+ } else {
+ catch {unset cflist_top}
+ }
+ $cflist conf -state disabled
+ set difffilestart {}
+}
+
+proc add_flist {fl} {
+ global flistmode cflist
+
+ $cflist conf -state normal
+ if {$flistmode eq "flat"} {
+ foreach f $fl {
+ $cflist insert end "\n$f"
+ }
+ }
+ $cflist conf -state disabled
+}
+
+proc sel_flist {w x y} {
+ global flistmode ctext difffilestart cflist cflist_top cmitmode
+
+ if {$cmitmode eq "tree"} return
+ if {![info exists cflist_top]} return
+ set l [lindex [split [$w index "@$x,$y"] "."] 0]
+ $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+ $cflist tag add highlight $l.0 "$l.0 lineend"
+ set cflist_top $l
+ if {$l == 1} {
+ $ctext yview 1.0
+ } else {
+ catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
+ }
+}
+
+# Functions for adding and removing shell-type quoting
+
+proc shellquote {str} {
+ if {![string match "*\['\"\\ \t]*" $str]} {
+ return $str
+ }
+ if {![string match "*\['\"\\]*" $str]} {
+ return "\"$str\""
+ }
+ if {![string match "*'*" $str]} {
+ return "'$str'"
+ }
+ return "\"[string map {\" \\\" \\ \\\\} $str]\""
+}
+
+proc shellarglist {l} {
+ set str {}
+ foreach a $l {
+ if {$str ne {}} {
+ append str " "
+ }
+ append str [shellquote $a]
+ }
+ return $str
+}
+
+proc shelldequote {str} {
+ set ret {}
+ set used -1
+ while {1} {
+ incr used
+ if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
+ append ret [string range $str $used end]
+ set used [string length $str]
+ break
+ }
+ set first [lindex $first 0]
+ set ch [string index $str $first]
+ if {$first > $used} {
+ append ret [string range $str $used [expr {$first - 1}]]
+ set used $first
+ }
+ if {$ch eq " " || $ch eq "\t"} break
+ incr used
+ if {$ch eq "'"} {
+ set first [string first "'" $str $used]
+ if {$first < 0} {
+ error "unmatched single-quote"
+ }
+ append ret [string range $str $used [expr {$first - 1}]]
+ set used $first
+ continue
+ }
+ if {$ch eq "\\"} {
+ if {$used >= [string length $str]} {
+ error "trailing backslash"
+ }
+ append ret [string index $str $used]
+ continue
+ }
+ # here ch == "\""
+ while {1} {
+ if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
+ error "unmatched double-quote"
+ }
+ set first [lindex $first 0]
+ set ch [string index $str $first]
+ if {$first > $used} {
+ append ret [string range $str $used [expr {$first - 1}]]
+ set used $first
+ }
+ if {$ch eq "\""} break
+ incr used
+ append ret [string index $str $used]
+ incr used
+ }
+ }
+ return [list $used $ret]
+}
+
+proc shellsplit {str} {
+ set l {}
+ while {1} {
+ set str [string trimleft $str]
+ if {$str eq {}} break
+ set dq [shelldequote $str]
+ set n [lindex $dq 0]
+ set word [lindex $dq 1]
+ set str [string range $str $n end]
+ lappend l $word
+ }
+ return $l
+}
+
+# Code to implement multiple views
+
+proc newview {ishighlight} {
+ global nextviewnum newviewname newviewperm uifont newishighlight
+ global newviewargs revtreeargs
+
+ set newishighlight $ishighlight
+ set top .gitkview
+ if {[winfo exists $top]} {
+ raise $top
+ return
+ }
+ set newviewname($nextviewnum) "View $nextviewnum"
+ set newviewperm($nextviewnum) 0
+ set newviewargs($nextviewnum) [shellarglist $revtreeargs]
+ vieweditor $top $nextviewnum "Gitk view definition"
+}
+
+proc editview {} {
+ global curview
+ global viewname viewperm newviewname newviewperm
+ global viewargs newviewargs
+
+ set top .gitkvedit-$curview
+ if {[winfo exists $top]} {
+ raise $top
+ return
+ }
+ set newviewname($curview) $viewname($curview)
+ set newviewperm($curview) $viewperm($curview)
+ set newviewargs($curview) [shellarglist $viewargs($curview)]
+ vieweditor $top $curview "Gitk: edit view $viewname($curview)"
+}
+
+proc vieweditor {top n title} {
+ global newviewname newviewperm viewfiles
+ global uifont
+
+ toplevel $top
+ wm title $top $title
+ label $top.nl -text "Name" -font $uifont
+ entry $top.name -width 20 -textvariable newviewname($n)
+ grid $top.nl $top.name -sticky w -pady 5
+ checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
+ grid $top.perm - -pady 5 -sticky w
+ message $top.al -aspect 1000 -font $uifont \
+ -text "Commits to include (arguments to git-rev-list):"
+ grid $top.al - -sticky w -pady 5
+ entry $top.args -width 50 -textvariable newviewargs($n) \
+ -background white
+ grid $top.args - -sticky ew -padx 5
+ message $top.l -aspect 1000 -font $uifont \
+ -text "Enter files and directories to include, one per line:"
+ grid $top.l - -sticky w
+ text $top.t -width 40 -height 10 -background white
+ if {[info exists viewfiles($n)]} {
+ foreach f $viewfiles($n) {
+ $top.t insert end $f
+ $top.t insert end "\n"
+ }
+ $top.t delete {end - 1c} end
+ $top.t mark set insert 0.0
+ }
+ grid $top.t - -sticky ew -padx 5
+ frame $top.buts
+ button $top.buts.ok -text "OK" -command [list newviewok $top $n]
+ button $top.buts.can -text "Cancel" -command [list destroy $top]
+ grid $top.buts.ok $top.buts.can
+ grid columnconfigure $top.buts 0 -weight 1 -uniform a
+ grid columnconfigure $top.buts 1 -weight 1 -uniform a
+ grid $top.buts - -pady 10 -sticky ew
+ focus $top.t
+}
+
+proc doviewmenu {m first cmd op args} {
+ set nmenu [$m index end]
+ for {set i $first} {$i <= $nmenu} {incr i} {
+ if {[$m entrycget $i -command] eq $cmd} {
+ eval $m $op $i $args
+ break
+ }
+ }
+}
+
+proc allviewmenus {n op args} {
+ doviewmenu .bar.view 7 [list showview $n] $op $args
+ doviewmenu .bar.view.hl 3 [list addhighlight $n] $op $args
+}
+
+proc newviewok {top n} {
+ global nextviewnum newviewperm newviewname newishighlight
+ global viewname viewfiles viewperm selectedview curview
+ global viewargs newviewargs
+
+ if {[catch {
+ set newargs [shellsplit $newviewargs($n)]
+ } err]} {
+ error_popup "Error in commit selection arguments: $err"
+ wm raise $top
+ focus $top
+ return
+ }
+ set files {}
+ foreach f [split [$top.t get 0.0 end] "\n"] {
+ set ft [string trim $f]
+ if {$ft ne {}} {
+ lappend files $ft
+ }
+ }
+ if {![info exists viewfiles($n)]} {
+ # creating a new view
+ incr nextviewnum
+ set viewname($n) $newviewname($n)
+ set viewperm($n) $newviewperm($n)
+ set viewfiles($n) $files
+ set viewargs($n) $newargs
+ addviewmenu $n
+ if {!$newishighlight} {
+ after idle showview $n
+ } else {
+ after idle addhighlight $n
+ }
+ } else {
+ # editing an existing view
+ set viewperm($n) $newviewperm($n)
+ if {$newviewname($n) ne $viewname($n)} {
+ set viewname($n) $newviewname($n)
+ allviewmenus $n entryconf -label $viewname($n)
+ }
+ if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
+ set viewfiles($n) $files
+ set viewargs($n) $newargs
+ if {$curview == $n} {
+ after idle updatecommits
+ }
+ }
+ }
+ catch {destroy $top}
+}
+
+proc delview {} {
+ global curview viewdata viewperm
+
+ if {$curview == 0} return
+ allviewmenus $curview delete
+ set viewdata($curview) {}
+ set viewperm($curview) 0
+ showview 0
+}
+
+proc addviewmenu {n} {
+ global viewname
+
+ .bar.view add radiobutton -label $viewname($n) \
+ -command [list showview $n] -variable selectedview -value $n
+ .bar.view.hl add radiobutton -label $viewname($n) \
+ -command [list addhighlight $n] -variable selectedhlview -value $n
+}
+
+proc flatten {var} {
+ global $var
+
+ set ret {}
+ foreach i [array names $var] {
+ lappend ret $i [set $var\($i\)]
+ }
+ return $ret
+}
+
+proc unflatten {var l} {
+ global $var
+
+ catch {unset $var}
+ foreach {i v} $l {
+ set $var\($i\) $v
+ }
+}
+
+proc showview {n} {
+ global curview viewdata viewfiles
+ global displayorder parentlist childlist rowidlist rowoffsets
+ global colormap rowtextx commitrow nextcolor canvxmax
+ global numcommits rowrangelist commitlisted idrowranges
+ global selectedline currentid canv canvy0
+ global matchinglines treediffs
+ global pending_select phase
+ global commitidx rowlaidout rowoptim linesegends
+ global commfd nextupdate
+ global selectedview hlview selectedhlview
+ global vparentlist vchildlist vdisporder vcmitlisted
+
+ if {$n == $curview} return
+ set selid {}
+ if {[info exists selectedline]} {
+ set selid $currentid
+ set y [yc $selectedline]
+ set ymax [lindex [$canv cget -scrollregion] 3]
+ set span [$canv yview]
+ set ytop [expr {[lindex $span 0] * $ymax}]
+ set ybot [expr {[lindex $span 1] * $ymax}]
+ if {$ytop < $y && $y < $ybot} {
+ set yscreen [expr {$y - $ytop}]
+ } else {
+ set yscreen [expr {($ybot - $ytop) / 2}]
+ }
+ }
+ unselectline
+ normalline
+ stopfindproc
+ if {$curview >= 0} {
+ set vparentlist($curview) $parentlist
+ set vchildlist($curview) $childlist
+ set vdisporder($curview) $displayorder
+ set vcmitlisted($curview) $commitlisted
+ if {$phase ne {}} {
+ set viewdata($curview) \
+ [list $phase $rowidlist $rowoffsets $rowrangelist \
+ [flatten idrowranges] [flatten idinlist] \
+ $rowlaidout $rowoptim $numcommits $linesegends]
+ } elseif {![info exists viewdata($curview)]
+ || [lindex $viewdata($curview) 0] ne {}} {
+ set viewdata($curview) \
+ [list {} $rowidlist $rowoffsets $rowrangelist]
+ }
+ }
+ catch {unset matchinglines}
+ catch {unset treediffs}
+ clear_display
+
+ set curview $n
+ set selectedview $n
+ set selectedhlview -1
+ .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
+ .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
+ catch {unset hlview}
+ .bar.view.hl entryconf 1 -state disabled
+
+ if {![info exists viewdata($n)]} {
+ set pending_select $selid
+ getcommits
+ return
+ }
+
+ set v $viewdata($n)
+ set phase [lindex $v 0]
+ set displayorder $vdisporder($n)
+ set parentlist $vparentlist($n)
+ set childlist $vchildlist($n)
+ set commitlisted $vcmitlisted($n)
+ set rowidlist [lindex $v 1]
+ set rowoffsets [lindex $v 2]
+ set rowrangelist [lindex $v 3]
+ if {$phase eq {}} {
+ set numcommits [llength $displayorder]
+ catch {unset idrowranges}
+ } else {
+ unflatten idrowranges [lindex $v 4]
+ unflatten idinlist [lindex $v 5]
+ set rowlaidout [lindex $v 6]
+ set rowoptim [lindex $v 7]
+ set numcommits [lindex $v 8]
+ set linesegends [lindex $v 9]
+ }
+
+ catch {unset colormap}
+ catch {unset rowtextx}
+ set nextcolor 0
+ set canvxmax [$canv cget -width]
+ set curview $n
+ set row 0
+ setcanvscroll
+ set yf 0
+ set row 0
+ if {$selid ne {} && [info exists commitrow($n,$selid)]} {
+ set row $commitrow($n,$selid)
+ # try to get the selected row in the same position on the screen
+ set ymax [lindex [$canv cget -scrollregion] 3]
+ set ytop [expr {[yc $row] - $yscreen}]
+ if {$ytop < 0} {
+ set ytop 0
+ }
+ set yf [expr {$ytop * 1.0 / $ymax}]
+ }
+ allcanvs yview moveto $yf
+ drawvisible
+ selectline $row 0
+ if {$phase ne {}} {
+ if {$phase eq "getcommits"} {
+ show_status "Reading commits..."
+ }
+ if {[info exists commfd($n)]} {
+ layoutmore
+ } else {
+ finishcommits
+ }
+ } elseif {$numcommits == 0} {
+ show_status "No commits selected"
+ }
+}
+
+proc addhighlight {n} {
+ global hlview curview viewdata highlighted highlightedrows
+ global selectedhlview
+
+ if {[info exists hlview]} {
+ delhighlight
+ }
+ set hlview $n
+ set selectedhlview $n
+ .bar.view.hl entryconf 1 -state normal
+ set highlighted($n) 0
+ set highlightedrows {}
+ if {$n != $curview && ![info exists viewdata($n)]} {
+ set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
+ set vparentlist($n) {}
+ set vchildlist($n) {}
+ set vdisporder($n) {}
+ set vcmitlisted($n) {}
+ start_rev_list $n
+ } else {
+ highlightmore
+ }
+}
+
+proc delhighlight {} {
+ global hlview highlightedrows canv linehtag mainfont
+ global selectedhlview selectedline
+
+ if {![info exists hlview]} return
+ unset hlview
+ set selectedhlview {}
+ .bar.view.hl entryconf 1 -state disabled
+ foreach l $highlightedrows {
+ $canv itemconf $linehtag($l) -font $mainfont
+ if {$l == $selectedline} {
+ $canv delete secsel
+ set t [eval $canv create rect [$canv bbox $linehtag($l)] \
+ -outline {{}} -tags secsel \
+ -fill [$canv cget -selectbackground]]
+ $canv lower $t
+ }
+ }
+}
+
+proc highlightmore {} {
+ global hlview highlighted commitidx highlightedrows linehtag mainfont
+ global displayorder vdisporder curview canv commitrow selectedline
+
+ set font [concat $mainfont bold]
+ set max $commitidx($hlview)
+ if {$hlview == $curview} {
+ set disp $displayorder
+ } else {
+ set disp $vdisporder($hlview)
+ }
+ for {set i $highlighted($hlview)} {$i < $max} {incr i} {
+ set id [lindex $disp $i]
+ if {[info exists commitrow($curview,$id)]} {
+ set row $commitrow($curview,$id)
+ if {[info exists linehtag($row)]} {
+ $canv itemconf $linehtag($row) -font $font
+ lappend highlightedrows $row
+ if {$row == $selectedline} {
+ $canv delete secsel
+ set t [eval $canv create rect \
+ [$canv bbox $linehtag($row)] \
+ -outline {{}} -tags secsel \
+ -fill [$canv cget -selectbackground]]
+ $canv lower $t
+ }
+ }
+ }
+ }
+ set highlighted($hlview) $max
+}
+
+# Graph layout functions
+
proc shortids {ids} {
set res {}
foreach id $ids {
}
proc usedinrange {id l1 l2} {
- global children commitrow
+ global children commitrow childlist curview
- if {[info exists commitrow($id)]} {
- set r $commitrow($id)
+ if {[info exists commitrow($curview,$id)]} {
+ set r $commitrow($curview,$id)
if {$l1 <= $r && $r <= $l2} {
return [expr {$r - $l1 + 1}]
}
+ set kids [lindex $childlist $r]
+ } else {
+ set kids $children($curview,$id)
}
- foreach c $children($id) {
- if {[info exists commitrow($c)]} {
- set r $commitrow($c)
- if {$l1 <= $r && $r <= $l2} {
- return [expr {$r - $l1 + 1}]
- }
+ foreach c $kids {
+ set r $commitrow($curview,$c)
+ if {$l1 <= $r && $r <= $l2} {
+ return [expr {$r - $l1 + 1}]
}
}
return 0
proc initlayout {} {
global rowidlist rowoffsets displayorder commitlisted
global rowlaidout rowoptim
- global idinlist rowchk
- global commitidx numcommits canvxmax canv
+ global idinlist rowchk rowrangelist idrowranges
+ global numcommits canvxmax canv
global nextcolor
global parentlist childlist children
+ global colormap rowtextx
+ global linesegends
- set commitidx 0
set numcommits 0
set displayorder {}
set commitlisted {}
set parentlist {}
set childlist {}
- catch {unset children}
+ set rowrangelist {}
set nextcolor 0
set rowidlist {{}}
set rowoffsets {{}}
set rowlaidout 0
set rowoptim 0
set canvxmax [$canv cget -width]
+ catch {unset colormap}
+ catch {unset rowtextx}
+ catch {unset idrowranges}
+ set linesegends {}
}
proc setcanvscroll {} {
proc layoutmore {} {
global rowlaidout rowoptim commitidx numcommits optim_delay
- global uparrowlen
+ global uparrowlen curview
set row $rowlaidout
- set rowlaidout [layoutrows $row $commitidx 0]
+ set rowlaidout [layoutrows $row $commitidx($curview) 0]
set orow [expr {$rowlaidout - $uparrowlen - 1}]
if {$orow > $rowoptim} {
- checkcrossings $rowoptim $orow
optimize_rows $rowoptim 0 $orow
set rowoptim $orow
}
}
proc showstuff {canshow} {
- global numcommits
- global linesegends idrowranges idrangedrawn
+ global numcommits commitrow pending_select selectedline
+ global linesegends idrowranges idrangedrawn curview
if {$numcommits == 0} {
global phase
set rows [visiblerows]
set r0 [lindex $rows 0]
set r1 [lindex $rows 1]
+ set selrow -1
for {set r $row} {$r < $canshow} {incr r} {
- if {[info exists linesegends($r)]} {
- foreach id $linesegends($r) {
- set i -1
- foreach {s e} $idrowranges($id) {
- incr i
- if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
- && ![info exists idrangedrawn($id,$i)]} {
- drawlineseg $id $i
- set idrangedrawn($id,$i) 1
- }
+ foreach id [lindex $linesegends [expr {$r+1}]] {
+ set i -1
+ foreach {s e} [rowranges $id] {
+ incr i
+ if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
+ && ![info exists idrangedrawn($id,$i)]} {
+ drawlineseg $id $i
+ set idrangedrawn($id,$i) 1
}
}
}
drawcmitrow $row
incr row
}
+ if {[info exists pending_select] &&
+ [info exists commitrow($curview,$pending_select)] &&
+ $commitrow($curview,$pending_select) < $numcommits} {
+ selectline $commitrow($curview,$pending_select) 1
+ }
+ if {![info exists selectedline] && ![info exists pending_select]} {
+ selectline 0 1
+ }
}
proc layoutrows {row endrow last} {
global uparrowlen downarrowlen maxwidth mingaplen
global childlist parentlist
global idrowranges linesegends
- global commitidx
- global idinlist rowchk
+ global commitidx curview
+ global idinlist rowchk rowrangelist
set idlist [lindex $rowidlist $row]
set offs [lindex $rowoffsets $row]
lappend oldolds $p
}
}
+ set lse {}
set nev [expr {[llength $idlist] + [llength $newolds]
+ [llength $oldolds] - $maxwidth + 1}]
if {$nev > 0} {
- if {!$last && $row + $uparrowlen + $mingaplen >= $commitidx} break
+ if {!$last &&
+ $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
set i [lindex $idlist $x]
if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
set offs [incrange $offs $x 1]
set idinlist($i) 0
set rm1 [expr {$row - 1}]
- lappend linesegends($rm1) $i
+ lappend lse $i
lappend idrowranges($i) $rm1
if {[incr nev -1] <= 0} break
continue
lset rowidlist $row $idlist
lset rowoffsets $row $offs
}
+ lappend linesegends $lse
set col [lsearch -exact $idlist $id]
if {$col < 0} {
set col [llength $idlist]
} else {
unset idinlist($id)
}
+ set ranges {}
if {[info exists idrowranges($id)]} {
- lappend idrowranges($id) $row
+ set ranges $idrowranges($id)
+ lappend ranges $row
+ unset idrowranges($id)
}
+ lappend rowrangelist $ranges
incr row
set offs [ntimes [llength $idlist] 0]
set l [llength $newolds]
proc addextraid {id row} {
global displayorder commitrow commitinfo
global commitidx commitlisted
- global parentlist childlist children
+ global parentlist childlist children curview
- incr commitidx
+ incr commitidx($curview)
lappend displayorder $id
lappend commitlisted 0
lappend parentlist {}
- set commitrow($id) $row
+ set commitrow($curview,$id) $row
readcommit $id
if {![info exists commitinfo($id)]} {
set commitinfo($id) {"No commit information available"}
}
- if {[info exists children($id)]} {
- lappend childlist $children($id)
- } else {
- lappend childlist {}
+ if {![info exists children($curview,$id)]} {
+ set children($curview,$id) {}
}
+ lappend childlist $children($curview,$id)
}
proc layouttail {} {
- global rowidlist rowoffsets idinlist commitidx
- global idrowranges
+ global rowidlist rowoffsets idinlist commitidx curview
+ global idrowranges rowrangelist
- set row $commitidx
+ set row $commitidx($curview)
set idlist [lindex $rowidlist $row]
while {$idlist ne {}} {
set col [expr {[llength $idlist] - 1}]
addextraid $id $row
unset idinlist($id)
lappend idrowranges($id) $row
+ lappend rowrangelist $idrowranges($id)
+ unset idrowranges($id)
incr row
set offs [ntimes $col 0]
set idlist [lreplace $idlist $col $col]
lset rowoffsets $row 0
makeuparrow $id 0 $row 0
lappend idrowranges($id) $row
+ lappend rowrangelist $idrowranges($id)
+ unset idrowranges($id)
incr row
lappend rowidlist {}
lappend rowoffsets {}
}
proc optimize_rows {row col endrow} {
- global rowidlist rowoffsets idrowranges linesegends displayorder
+ global rowidlist rowoffsets idrowranges displayorder
for {} {$row < $endrow} {incr row} {
set idlist [lindex $rowidlist $row]
set z0 [lindex $rowoffsets $y0 $x0]
if {$z0 eq {}} {
set id [lindex $idlist $col]
- if {[info exists idrowranges($id)] &&
- $y0 > [lindex $idrowranges($id) 0]} {
+ set ranges [rowranges $id]
+ if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
set isarrow 1
}
}
if {$o eq {}} {
# check if this is the link to the first child
set id [lindex $idlist $col]
- if {[info exists idrowranges($id)] &&
- $row == [lindex $idrowranges($id) 0]} {
+ set ranges [rowranges $id]
+ if {$ranges ne {} && $row == [lindex $ranges 0]} {
# it is, work out offset to child
set y0 [expr {$row - 1}]
set id [lindex $displayorder $y0]
return $wid
}
+proc rowranges {id} {
+ global phase idrowranges commitrow rowlaidout rowrangelist curview
+
+ set ranges {}
+ if {$phase eq {} ||
+ ([info exists commitrow($curview,$id)]
+ && $commitrow($curview,$id) < $rowlaidout)} {
+ set ranges [lindex $rowrangelist $commitrow($curview,$id)]
+ } elseif {[info exists idrowranges($id)]} {
+ set ranges $idrowranges($id)
+ }
+ return $ranges
+}
+
proc drawlineseg {id i} {
- global rowoffsets rowidlist idrowranges
+ global rowoffsets rowidlist
global displayorder
global canv colormap linespc
+ global numcommits commitrow curview
- set startrow [lindex $idrowranges($id) [expr {2 * $i}]]
- set row [lindex $idrowranges($id) [expr {2 * $i + 1}]]
+ set ranges [rowranges $id]
+ set downarrow 1
+ if {[info exists commitrow($curview,$id)]
+ && $commitrow($curview,$id) < $numcommits} {
+ set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
+ } else {
+ set downarrow 1
+ }
+ set startrow [lindex $ranges [expr {2 * $i}]]
+ set row [lindex $ranges [expr {2 * $i + 1}]]
if {$startrow == $row} return
assigncolor $id
set coords {}
}
}
if {[llength $coords] < 4} return
- set last [expr {[llength $idrowranges($id)] / 2 - 1}]
- if {$i < $last} {
+ if {$downarrow} {
# This line has an arrow at the lower end: check if the arrow is
# on a diagonal segment, and if so, work around the Tk 8.4
# refusal to draw arrows on diagonal lines.
}
}
}
- set arrow [expr {2 * ($i > 0) + ($i < $last)}]
+ set arrow [expr {2 * ($i > 0) + $downarrow}]
set arrow [lindex {none first last both} $arrow]
set t [$canv create line $coords -width [linewidth $id] \
-fill $colormap($id) -tags lines.$id -arrow $arrow]
}
proc drawparentlinks {id row col olds} {
- global rowidlist canv colormap idrowranges
+ global rowidlist canv colormap
set row2 [expr {$row + 1}]
set x [xc $row $col]
if {$x2 > $rmx} {
set rmx $x2
}
- if {[info exists idrowranges($p)] &&
- $row2 == [lindex $idrowranges($p) 0] &&
- $row2 < [lindex $idrowranges($p) 1]} {
+ set ranges [rowranges $p]
+ if {$ranges ne {} && $row2 == [lindex $ranges 0]
+ && $row2 < [lindex $ranges 1]} {
# drawlineseg will do this one for us
continue
}
proc drawlines {id} {
global colormap canv
- global idrowranges idrangedrawn
- global childlist iddrawn commitrow rowidlist
+ global idrangedrawn
+ global children iddrawn commitrow rowidlist curview
$canv delete lines.$id
- set nr [expr {[llength $idrowranges($id)] / 2}]
+ set nr [expr {[llength [rowranges $id]] / 2}]
for {set i 0} {$i < $nr} {incr i} {
if {[info exists idrangedrawn($id,$i)]} {
drawlineseg $id $i
}
}
- foreach child [lindex $childlist $commitrow($id)] {
+ foreach child $children($curview,$id) {
if {[info exists iddrawn($child)]} {
- set row $commitrow($child)
+ set row $commitrow($curview,$child)
set col [lsearch -exact [lindex $rowidlist $row] $child]
if {$col >= 0} {
drawparentlinks $child $row $col [list $id]
global commitlisted commitinfo rowidlist
global rowtextx idpos idtags idheads idotherrefs
global linehtag linentag linedtag
- global mainfont namefont canvxmax
+ global mainfont canvxmax
+ global hlview commitrow highlightedrows
set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
set x [xc $row $col]
set name [lindex $commitinfo($id) 1]
set date [lindex $commitinfo($id) 2]
set date [formatdate $date]
+ set font $mainfont
+ if {[info exists hlview] && [info exists commitrow($hlview,$id)]} {
+ lappend font bold
+ lappend highlightedrows $row
+ }
set linehtag($row) [$canv create text $xt $y -anchor w \
- -text $headline -font $mainfont ]
+ -text $headline -font $font]
$canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
set linentag($row) [$canv2 create text 3 $y -anchor w \
- -text $name -font $namefont]
+ -text $name -font $mainfont]
set linedtag($row) [$canv3 create text 3 $y -anchor w \
-text $date -font $mainfont]
set xr [expr {$xt + [font measure $mainfont $headline]}]
proc drawcmitrow {row} {
global displayorder rowidlist
- global idrowranges idrangedrawn iddrawn
+ global idrangedrawn iddrawn
global commitinfo parentlist numcommits
if {$row >= $numcommits} return
foreach id [lindex $rowidlist $row] {
- if {![info exists idrowranges($id)]} continue
+ if {$id eq {}} continue
set i -1
- foreach {s e} $idrowranges($id) {
+ foreach {s e} [rowranges $id] {
incr i
if {$row < $s} continue
if {$e eq {}} break
catch {unset idrangedrawn}
}
+proc findcrossings {id} {
+ global rowidlist parentlist numcommits rowoffsets displayorder
+
+ set cross {}
+ set ccross {}
+ foreach {s e} [rowranges $id] {
+ if {$e >= $numcommits} {
+ set e [expr {$numcommits - 1}]
+ }
+ if {$e <= $s} continue
+ set x [lsearch -exact [lindex $rowidlist $e] $id]
+ if {$x < 0} {
+ puts "findcrossings: oops, no [shortids $id] in row $e"
+ continue
+ }
+ for {set row $e} {[incr row -1] >= $s} {} {
+ set olds [lindex $parentlist $row]
+ set kid [lindex $displayorder $row]
+ set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
+ if {$kidx < 0} continue
+ set nextrow [lindex $rowidlist [expr {$row + 1}]]
+ foreach p $olds {
+ set px [lsearch -exact $nextrow $p]
+ if {$px < 0} continue
+ if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
+ if {[lsearch -exact $ccross $p] >= 0} continue
+ if {$x == $px + ($kidx < $px? -1: 1)} {
+ lappend ccross $p
+ } elseif {[lsearch -exact $cross $p] < 0} {
+ lappend cross $p
+ }
+ }
+ }
+ set inc [lindex $rowoffsets $row $x]
+ if {$inc eq {}} break
+ incr x $inc
+ }
+ }
+ return [concat $ccross {{}} $cross]
+}
+
proc assigncolor {id} {
global colormap colors nextcolor
- global commitrow parentlist children childlist
- global cornercrossings crossings
+ global commitrow parentlist children children curview
if {[info exists colormap($id)]} return
set ncolors [llength $colors]
- if {[info exists commitrow($id)]} {
- set kids [lindex $childlist $commitrow($id)]
- } elseif {[info exists children($id)]} {
- set kids $children($id)
+ if {[info exists children($curview,$id)]} {
+ set kids $children($curview,$id)
} else {
set kids {}
}
if {[llength $kids] == 1} {
set child [lindex $kids 0]
if {[info exists colormap($child)]
- && [llength [lindex $parentlist $commitrow($child)]] == 1} {
+ && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
set colormap($id) $colormap($child)
return
}
}
set badcolors {}
- if {[info exists cornercrossings($id)]} {
- foreach x $cornercrossings($id) {
- if {[info exists colormap($x)]
- && [lsearch -exact $badcolors $colormap($x)] < 0} {
- lappend badcolors $colormap($x)
- }
+ set origbad {}
+ foreach x [findcrossings $id] {
+ if {$x eq {}} {
+ # delimiter between corner crossings and other crossings
+ if {[llength $badcolors] >= $ncolors - 1} break
+ set origbad $badcolors
}
- if {[llength $badcolors] >= $ncolors} {
- set badcolors {}
+ if {[info exists colormap($x)]
+ && [lsearch -exact $badcolors $colormap($x)] < 0} {
+ lappend badcolors $colormap($x)
}
}
- set origbad $badcolors
- if {[llength $badcolors] < $ncolors - 1} {
- if {[info exists crossings($id)]} {
- foreach x $crossings($id) {
- if {[info exists colormap($x)]
- && [lsearch -exact $badcolors $colormap($x)] < 0} {
- lappend badcolors $colormap($x)
- }
- }
- if {[llength $badcolors] >= $ncolors} {
- set badcolors $origbad
- }
- }
- set origbad $badcolors
+ if {[llength $badcolors] >= $ncolors} {
+ set badcolors $origbad
}
+ set origbad $badcolors
if {[llength $badcolors] < $ncolors - 1} {
foreach child $kids {
if {[info exists colormap($child)]
&& [lsearch -exact $badcolors $colormap($child)] < 0} {
lappend badcolors $colormap($child)
}
- foreach p [lindex $parentlist $commitrow($child)] {
+ foreach p [lindex $parentlist $commitrow($curview,$child)] {
if {[info exists colormap($p)]
&& [lsearch -exact $badcolors $colormap($p)] < 0} {
lappend badcolors $colormap($p)
proc drawtags {id x xt y1} {
global idtags idheads idotherrefs
global linespc lthickness
- global canv mainfont commitrow rowtextx
+ global canv mainfont commitrow rowtextx curview
set marks {}
set ntags 0
$xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
-width 1 -outline black -fill yellow -tags tag.$id]
$canv bind $t <1> [list showtag $tag 1]
- set rowtextx($commitrow($id)) [expr {$xr + $linespc}]
+ set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
} else {
# draw a head or other ref
if {[incr nheads -1] >= 0} {
set xl [expr {$xl - $delta/2}]
$canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
-width 1 -outline black -fill $col -tags tag.$id
+ if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
+ set rwid [font measure $mainfont $remoteprefix]
+ set xi [expr {$x + 1}]
+ set yti [expr {$yt + 1}]
+ set xri [expr {$x + $rwid}]
+ $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
+ -width 0 -fill "#ffddaa" -tags tag.$id
+ }
}
set t [$canv create text $xl $y1 -anchor w -text $tag \
-font $mainfont -tags tag.$id]
return $xt
}
-proc checkcrossings {row endrow} {
- global displayorder parentlist rowidlist
-
- for {} {$row < $endrow} {incr row} {
- set id [lindex $displayorder $row]
- set i [lsearch -exact [lindex $rowidlist $row] $id]
- if {$i < 0} continue
- set idlist [lindex $rowidlist [expr {$row+1}]]
- foreach p [lindex $parentlist $row] {
- set j [lsearch -exact $idlist $p]
- if {$j > 0} {
- if {$j < $i - 1} {
- notecrossings $row $p $j $i [expr {$j+1}]
- } elseif {$j > $i + 1} {
- notecrossings $row $p $i $j [expr {$j-1}]
- }
- }
- }
- }
-}
-
-proc notecrossings {row id lo hi corner} {
- global rowidlist crossings cornercrossings
-
- for {set i $lo} {[incr i] < $hi} {} {
- set p [lindex [lindex $rowidlist $row] $i]
- if {$p == {}} continue
- if {$i == $corner} {
- if {![info exists cornercrossings($id)]
- || [lsearch -exact $cornercrossings($id) $p] < 0} {
- lappend cornercrossings($id) $p
- }
- if {![info exists cornercrossings($p)]
- || [lsearch -exact $cornercrossings($p) $id] < 0} {
- lappend cornercrossings($p) $id
- }
- } else {
- if {![info exists crossings($id)]
- || [lsearch -exact $crossings($id) $p] < 0} {
- lappend crossings($id) $p
- }
- if {![info exists crossings($p)]
- || [lsearch -exact $crossings($p) $id] < 0} {
- lappend crossings($p) $id
- }
- }
- }
-}
-
proc xcoord {i level ln} {
global canvx0 xspc1 xspc2
return $x
}
+proc show_status {msg} {
+ global canv mainfont
+
+ clear_display
+ $canv create text 3 3 -anchor nw -text $msg -font $mainfont -tags textitems
+}
+
proc finishcommits {} {
- global commitidx phase
+ global commitidx phase curview
global canv mainfont ctext maincursor textcursor
- global findinprogress
+ global findinprogress pending_select
- if {$commitidx > 0} {
+ if {$commitidx($curview) > 0} {
drawrest
} else {
- $canv delete all
- $canv create text 3 3 -anchor nw -text "No commits selected" \
- -font $mainfont -tags textitems
- }
- if {![info exists findinprogress]} {
- . config -cursor $maincursor
- settextcursor $textcursor
+ show_status "No commits selected"
}
set phase {}
+ catch {unset pending_select}
}
# Don't change the text pane cursor if it is currently the hand cursor,
set curtextcursor $c
}
+proc nowbusy {what} {
+ global isbusy
+
+ if {[array names isbusy] eq {}} {
+ . config -cursor watch
+ settextcursor watch
+ }
+ set isbusy($what) 1
+}
+
+proc notbusy {what} {
+ global isbusy maincursor textcursor
+
+ catch {unset isbusy($what)}
+ if {[array names isbusy] eq {}} {
+ . config -cursor $maincursor
+ settextcursor $textcursor
+ }
+}
+
proc drawrest {} {
global numcommits
global startmsecs
global canvy0 numcommits linespc
- global rowlaidout commitidx
+ global rowlaidout commitidx curview
+ global pending_select
set row $rowlaidout
- layoutrows $rowlaidout $commitidx 1
+ layoutrows $rowlaidout $commitidx($curview) 1
layouttail
- optimize_rows $row 0 $commitidx
- showstuff $commitidx
+ optimize_rows $row 0 $commitidx($curview)
+ showstuff $commitidx($curview)
+ if {[info exists pending_select]} {
+ selectline 0 1
+ }
set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
#puts "overall $drawmsecs ms for $numcommits commits"
proc dofind {} {
global findtype findloc findstring markedmatches commitinfo
global numcommits displayorder linehtag linentag linedtag
- global mainfont namefont canv canv2 canv3 selectedline
+ global mainfont canv canv2 canv3 selectedline
global matchinglines foundstring foundstrlen matchstring
global commitdata
markmatches $canv $l $f $linehtag($l) $matches $mainfont
} elseif {$ty == "Author"} {
drawcmitrow $l
- markmatches $canv2 $l $f $linentag($l) $matches $namefont
+ markmatches $canv2 $l $f $linentag($l) $matches $mainfont
} elseif {$ty == "Date"} {
drawcmitrow $l
markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
catch {close $findprocfile}
unset findprocpid
}
- if {[info exists findinprogress]} {
- unset findinprogress
- if {$phase != "incrdraw"} {
- . config -cursor $maincursor
- settextcursor $textcursor
- }
- }
+ catch {unset findinprogress}
+ notbusy find
}
proc findpatches {} {
fconfigure $f -blocking 0
fileevent $f readable readfindproc
set finddidsel 0
- . config -cursor watch
- settextcursor watch
+ nowbusy find
set findinprogress 1
}
proc readfindproc {} {
global findprocfile finddidsel
- global commitrow matchinglines findinsertpos
+ global commitrow matchinglines findinsertpos curview
set n [gets $findprocfile line]
if {$n < 0} {
stopfindproc
return
}
- if {![info exists commitrow($id)]} {
+ if {![info exists commitrow($curview,$id)]} {
puts stderr "spurious id: $id"
return
}
- set l $commitrow($id)
+ set l $commitrow($curview,$id)
insertmatch $l $id
}
set finddidsel 0
set findinsertpos end
set id [lindex $displayorder $l]
- . config -cursor watch
- settextcursor watch
+ nowbusy find
set findinprogress 1
findcont
update
# append some text to the ctext widget, and make any SHA1 ID
# that we know about be a clickable link.
proc appendwithlinks {text} {
- global ctext commitrow linknum
+ global ctext commitrow linknum curview
set start [$ctext index "end - 1c"]
$ctext insert end $text
set s [lindex $l 0]
set e [lindex $l 1]
set linkid [string range $text $s $e]
- if {![info exists commitrow($linkid)]} continue
+ if {![info exists commitrow($curview,$linkid)]} continue
incr e
$ctext tag add link "$start + $s c" "$start + $e c"
$ctext tag add link$linknum "$start + $s c" "$start + $e c"
- $ctext tag bind link$linknum <1> [list selectline $commitrow($linkid) 1]
+ $ctext tag bind link$linknum <1> \
+ [list selectline $commitrow($curview,$linkid) 1]
incr linknum
}
$ctext tag conf link -foreground blue -underline 1
global canv canv2 canv3 ctext commitinfo selectedline
global displayorder linehtag linentag linedtag
global canvy0 linespc parentlist childlist
- global cflist currentid sha1entry
+ global currentid sha1entry
global commentend idtags linknum
- global mergemax numcommits
+ global mergemax numcommits pending_select
+ global cmitmode
+ catch {unset pending_select}
$canv delete hover
normalline
if {$l < 0 || $l >= $numcommits} return
$ctext conf -state normal
$ctext delete 0.0 end
set linknum 0
- $ctext mark set fmark.0 0.0
- $ctext mark gravity fmark.0 left
set info $commitinfo($id)
set date [formatdate [lindex $info 2]]
$ctext insert end "Author: [lindex $info 1] $date\n"
$ctext conf -state disabled
set commentend [$ctext index "end - 1c"]
- $cflist delete 0 end
- $cflist insert end "Comments"
- if {[llength $olds] <= 1} {
+ init_flist "Comments"
+ if {$cmitmode eq "tree"} {
+ gettree $id
+ } elseif {[llength $olds] <= 1} {
startdiff $id
} else {
mergediff $id $l
}
proc unselectline {} {
- global selectedline
+ global selectedline currentid
catch {unset selectedline}
+ catch {unset currentid}
allcanvs delete secsel
}
+proc reselectline {} {
+ global selectedline
+
+ if {[info exists selectedline]} {
+ selectline $selectedline 0
+ }
+}
+
proc addtohistory {cmd} {
- global history historyindex
+ global history historyindex curview
+ set elt [list $curview $cmd]
if {$historyindex > 0
- && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
+ && [lindex $history [expr {$historyindex - 1}]] == $elt} {
return
}
if {$historyindex < [llength $history]} {
- set history [lreplace $history $historyindex end $cmd]
+ set history [lreplace $history $historyindex end $elt]
} else {
- lappend history $cmd
+ lappend history $elt
}
incr historyindex
if {$historyindex > 1} {
.ctop.top.bar.rightbut conf -state disabled
}
+proc godo {elt} {
+ global curview
+
+ set view [lindex $elt 0]
+ set cmd [lindex $elt 1]
+ if {$curview != $view} {
+ showview $view
+ }
+ eval $cmd
+}
+
proc goback {} {
global history historyindex
if {$historyindex > 1} {
incr historyindex -1
- set cmd [lindex $history [expr {$historyindex - 1}]]
- eval $cmd
+ godo [lindex $history [expr {$historyindex - 1}]]
.ctop.top.bar.rightbut conf -state normal
}
if {$historyindex <= 1} {
if {$historyindex < [llength $history]} {
set cmd [lindex $history $historyindex]
incr historyindex
- eval $cmd
+ godo $cmd
.ctop.top.bar.leftbut conf -state normal
}
if {$historyindex >= [llength $history]} {
}
}
+proc gettree {id} {
+ global treefilelist treeidlist diffids diffmergeid treepending
+
+ set diffids $id
+ catch {unset diffmergeid}
+ if {![info exists treefilelist($id)]} {
+ if {![info exists treepending]} {
+ if {[catch {set gtf [open [concat | git-ls-tree -r $id] r]}]} {
+ return
+ }
+ set treepending $id
+ set treefilelist($id) {}
+ set treeidlist($id) {}
+ fconfigure $gtf -blocking 0
+ fileevent $gtf readable [list gettreeline $gtf $id]
+ }
+ } else {
+ setfilelist $id
+ }
+}
+
+proc gettreeline {gtf id} {
+ global treefilelist treeidlist treepending cmitmode diffids
+
+ while {[gets $gtf line] >= 0} {
+ if {[lindex $line 1] ne "blob"} continue
+ set sha1 [lindex $line 2]
+ set fname [lindex $line 3]
+ lappend treefilelist($id) $fname
+ lappend treeidlist($id) $sha1
+ }
+ if {![eof $gtf]} return
+ close $gtf
+ unset treepending
+ if {$cmitmode ne "tree"} {
+ if {![info exists diffmergeid]} {
+ gettreediffs $diffids
+ }
+ } elseif {$id ne $diffids} {
+ gettree $diffids
+ } else {
+ setfilelist $id
+ }
+}
+
+proc showfile {f} {
+ global treefilelist treeidlist diffids
+ global ctext commentend
+
+ set i [lsearch -exact $treefilelist($diffids) $f]
+ if {$i < 0} {
+ puts "oops, $f not in list for id $diffids"
+ return
+ }
+ set blob [lindex $treeidlist($diffids) $i]
+ if {[catch {set bf [open [concat | git-cat-file blob $blob] r]} err]} {
+ puts "oops, error reading blob $blob: $err"
+ return
+ }
+ fconfigure $bf -blocking 0
+ fileevent $bf readable [list getblobline $bf $diffids]
+ $ctext config -state normal
+ $ctext delete $commentend end
+ $ctext insert end "\n"
+ $ctext insert end "$f\n" filesep
+ $ctext config -state disabled
+ $ctext yview $commentend
+}
+
+proc getblobline {bf id} {
+ global diffids cmitmode ctext
+
+ if {$id ne $diffids || $cmitmode ne "tree"} {
+ catch {close $bf}
+ return
+ }
+ $ctext config -state normal
+ while {[gets $bf line] >= 0} {
+ $ctext insert end "$line\n"
+ }
+ if {[eof $bf]} {
+ # delete last newline
+ $ctext delete "end - 2c" "end - 1c"
+ close $bf
+ }
+ $ctext config -state disabled
+}
+
proc mergediff {id l} {
global diffmergeid diffopts mdifffd
- global difffilestart diffids
+ global diffids
global parentlist
set diffmergeid $id
set diffids $id
- catch {unset difffilestart}
# this doesn't seem to actually affect anything...
set env(GIT_DIFF_OPTS) $diffopts
set cmd [concat | git-diff-tree --no-commit-id --cc $id]
# start of a new file
$ctext insert end "\n"
set here [$ctext index "end - 1c"]
- set i [$cflist index end]
- $ctext mark set fmark.$i $here
- $ctext mark gravity fmark.$i left
- set difffilestart([expr {$i-1}]) $here
- $cflist insert end $fname
+ lappend difffilestart $here
+ add_flist [list $fname]
set l [expr {(78 - [string length $fname]) / 2}]
set pad [string range "----------------------------------------" 1 $l]
$ctext insert end "$pad $fname $pad\n" filesep
proc addtocflist {ids} {
global treediffs cflist
- foreach f $treediffs($ids) {
- $cflist insert end $f
- }
+ add_flist $treediffs($ids)
getblobdiffs $ids
}
proc gettreediffline {gdtf ids} {
global treediff treediffs treepending diffids diffmergeid
+ global cmitmode
set n [gets $gdtf line]
if {$n < 0} {
close $gdtf
set treediffs($ids) $treediff
unset treepending
- if {$ids != $diffids} {
+ if {$cmitmode eq "tree"} {
+ gettree $diffids
+ } elseif {$ids != $diffids} {
if {![info exists diffmergeid]} {
gettreediffs $diffids
}
proc getblobdiffs {ids} {
global diffopts blobdifffd diffids env curdifftag curtagstart
- global difffilestart nextupdate diffinhdr treediffs
+ global nextupdate diffinhdr treediffs
set env(GIT_DIFF_OPTS) $diffopts
set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
set blobdifffd($ids) $bdf
set curdifftag Comments
set curtagstart 0.0
- catch {unset difffilestart}
fileevent $bdf readable [list getblobdiffline $bdf $diffids]
set nextupdate [expr {[clock clicks -milliseconds] + 100}]
}
+proc setinlist {var i val} {
+ global $var
+
+ while {[llength [set $var]] < $i} {
+ lappend $var {}
+ }
+ if {[llength [set $var]] == $i} {
+ lappend $var $val
+ } else {
+ lset $var $i $val
+ }
+}
+
proc getblobdiffline {bdf ids} {
global diffids blobdifffd ctext curdifftag curtagstart
global diffnexthead diffnextnote difffilestart
# start of a new file
$ctext insert end "\n"
$ctext tag add $curdifftag $curtagstart end
- set curtagstart [$ctext index "end - 1c"]
- set header $newname
set here [$ctext index "end - 1c"]
- set i [lsearch -exact $treediffs($diffids) $fname]
+ set curtagstart $here
+ set header $newname
+ set i [lsearch -exact $treediffs($ids) $fname]
if {$i >= 0} {
- set difffilestart($i) $here
- incr i
- $ctext mark set fmark.$i $here
- $ctext mark gravity fmark.$i left
+ setinlist difffilestart $i $here
}
- if {$newname != $fname} {
- set i [lsearch -exact $treediffs($diffids) $newname]
+ if {$newname ne $fname} {
+ set i [lsearch -exact $treediffs($ids) $newname]
if {$i >= 0} {
- set difffilestart($i) $here
- incr i
- $ctext mark set fmark.$i $here
- $ctext mark gravity fmark.$i left
+ setinlist difffilestart $i $here
}
}
set curdifftag "f:$fname"
proc nextfile {} {
global difffilestart ctext
set here [$ctext index @0,0]
- for {set i 0} {[info exists difffilestart($i)]} {incr i} {
- if {[$ctext compare $difffilestart($i) > $here]} {
- if {![info exists pos]
- || [$ctext compare $difffilestart($i) < $pos]} {
- set pos $difffilestart($i)
- }
+ foreach loc $difffilestart {
+ if {[$ctext compare $loc > $here]} {
+ $ctext yview $loc
}
}
- if {[info exists pos]} {
- $ctext yview $pos
- }
-}
-
-proc listboxsel {} {
- global ctext cflist currentid
- if {![info exists currentid]} return
- set sel [lsort [$cflist curselection]]
- if {$sel eq {}} return
- set first [lindex $sel 0]
- catch {$ctext yview fmark.$first}
}
proc setcoords {} {
}
proc incrfont {inc} {
- global mainfont namefont textfont ctext canv phase
+ global mainfont textfont ctext canv phase
global stopped entries
unmarkmatches
set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
- set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
setcoords
$ctext conf -font $textfont
foreach e $entries {
$e conf -font $mainfont
}
- if {$phase == "getcommits"} {
+ if {$phase eq "getcommits"} {
$canv itemconf textitems -font $mainfont
}
redisplay
proc gotocommit {} {
global sha1string currentid commitrow tagids headids
- global displayorder numcommits
+ global displayorder numcommits curview
if {$sha1string == {}
|| ([info exists currentid] && $sha1string == $currentid)} return
}
}
}
- if {[info exists commitrow($id)]} {
- selectline $commitrow($id) 1
+ if {[info exists commitrow($curview,$id)]} {
+ selectline $commitrow($curview,$id) 1
return
}
if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
}
proc clickisonarrow {id y} {
- global lthickness idrowranges
+ global lthickness
+ set ranges [rowranges $id]
set thresh [expr {2 * $lthickness + 6}]
- set n [expr {[llength $idrowranges($id)] - 1}]
+ set n [expr {[llength $ranges] - 1}]
for {set i 1} {$i < $n} {incr i} {
- set row [lindex $idrowranges($id) $i]
+ set row [lindex $ranges $i]
if {abs([yc $row] - $y) < $thresh} {
return $i
}
}
proc arrowjump {id n y} {
- global idrowranges canv
+ global canv
# 1 <-> 2, 3 <-> 4, etc...
set n [expr {(($n - 1) ^ 1) + 1}]
- set row [lindex $idrowranges($id) $n]
+ set row [lindex [rowranges $id] $n]
set yt [yc $row]
set ymax [lindex [$canv cget -scrollregion] 3]
if {$ymax eq {} || $ymax <= 0} return
}
proc lineclick {x y id isnew} {
- global ctext commitinfo childlist commitrow cflist canv thickerline
+ global ctext commitinfo children canv thickerline curview
if {![info exists commitinfo($id)] && ![getcommit $id]} return
unmarkmatches
$ctext insert end "\tAuthor:\t[lindex $info 1]\n"
set date [formatdate [lindex $info 2]]
$ctext insert end "\tDate:\t$date\n"
- set kids [lindex $childlist $commitrow($id)]
+ set kids $children($curview,$id)
if {$kids ne {}} {
$ctext insert end "\nChildren:"
set i 0
}
}
$ctext conf -state disabled
-
- $cflist delete 0 end
+ init_flist {}
}
proc normalline {} {
}
proc selbyid {id} {
- global commitrow
- if {[info exists commitrow($id)]} {
- selectline $commitrow($id) 1
+ global commitrow curview
+ if {[info exists commitrow($curview,$id)]} {
+ selectline $commitrow($curview,$id) 1
}
}
}
proc rowmenu {x y id} {
- global rowctxmenu commitrow selectedline rowmenuid
+ global rowctxmenu commitrow selectedline rowmenuid curview
- if {![info exists selectedline] || $commitrow($id) eq $selectedline} {
+ if {![info exists selectedline]
+ || $commitrow($curview,$id) eq $selectedline} {
set state disabled
} else {
set state normal
}
proc doseldiff {oldid newid} {
- global ctext cflist
+ global ctext
global commitinfo
$ctext conf -state normal
$ctext delete 0.0 end
- $ctext mark set fmark.0 0.0
- $ctext mark gravity fmark.0 left
- $cflist delete 0 end
- $cflist insert end "Top"
+ init_flist "Top"
$ctext insert end "From "
$ctext tag conf link -foreground blue -underline 1
$ctext tag bind link <Enter> { %W configure -cursor hand2 }
}
proc redrawtags {id} {
- global canv linehtag commitrow idpos selectedline
+ global canv linehtag commitrow idpos selectedline curview
- if {![info exists commitrow($id)]} return
- drawcmitrow $commitrow($id)
+ if {![info exists commitrow($curview,$id)]} return
+ drawcmitrow $commitrow($curview,$id)
$canv delete tag.$id
set xt [eval drawtags $id $idpos($id)]
- $canv coords $linehtag($commitrow($id)) $xt [lindex $idpos($id) 2]
- if {[info exists selectedline] && $selectedline == $commitrow($id)} {
+ $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
+ if {[info exists selectedline]
+ && $selectedline == $commitrow($curview,$id)} {
selectline $selectedline 0
}
}
}
proc showtag {tag isnew} {
- global ctext cflist tagcontents tagids linknum
+ global ctext tagcontents tagids linknum
if {$isnew} {
addtohistory [list showtag $tag 0]
}
appendwithlinks $text
$ctext conf -state disabled
- $cflist delete 0 end
+ init_flist {}
}
proc doquit {} {
set uparrowlen 7
set downarrowlen 7
set mingaplen 30
+set flistmode "flat"
+set cmitmode "patch"
set colors {green red blue magenta darkgrey brown orange}
catch {source ~/.gitk}
-set namefont $mainfont
-
font create optionfont -family sans-serif -size -12
set revtreeargs {}
# check that we can find a .git directory somewhere...
set gitdir [gitdir]
if {![file isdirectory $gitdir]} {
- error_popup "Cannot find the git directory \"$gitdir\"."
+ show_error . "Cannot find the git directory \"$gitdir\"."
exit 1
}
+set cmdline_files {}
+set i [lsearch -exact $revtreeargs "--"]
+if {$i >= 0} {
+ set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
+ set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
+} elseif {$revtreeargs ne {}} {
+ if {[catch {
+ set f [eval exec git-rev-parse --no-revs --no-flags $revtreeargs]
+ set cmdline_files [split $f "\n"]
+ set n [llength $cmdline_files]
+ set revtreeargs [lrange $revtreeargs 0 end-$n]
+ } err]} {
+ # unfortunately we get both stdout and stderr in $err,
+ # so look for "fatal:".
+ set i [string first "fatal:" $err]
+ if {$i > 0} {
+ set err [string range [expr {$i + 6}] end]
+ }
+ show_error . "Bad arguments to gitk:\n$err"
+ exit 1
+ }
+}
+
set history {}
set historyindex 0
set optim_delay 16
+set nextviewnum 1
+set curview 0
+set selectedview 0
+set selectedhlview {}
+set viewfiles(0) {}
+set viewperm(0) 0
+set viewargs(0) {}
+
+set cmdlineok 0
set stopped 0
set stuffsaved 0
set patchnum 0
setcoords
-makewindow $revtreeargs
+makewindow
readrefs
-getcommits $revtreeargs
+
+if {$cmdline_files ne {} || $revtreeargs ne {}} {
+ # create a view for the files/dirs specified on the command line
+ set curview 1
+ set selectedview 1
+ set nextviewnum 2
+ set viewname(1) "Command line"
+ set viewfiles(1) $cmdline_files
+ set viewargs(1) $revtreeargs
+ set viewperm(1) 0
+ addviewmenu 1
+ .bar.view entryconf 2 -state normal
+ .bar.view entryconf 3 -state normal
+}
+
+if {[info exists permviews]} {
+ foreach v $permviews {
+ set n $nextviewnum
+ incr nextviewnum
+ set viewname($n) [lindex $v 0]
+ set viewfiles($n) [lindex $v 1]
+ set viewargs($n) [lindex $v 2]
+ set viewperm($n) 1
+ addviewmenu $n
+ }
+}
+getcommits
#include "commit.h"
#include "log-tree.h"
+static void show_parents(struct commit *commit, int abbrev)
+{
+ struct commit_list *p;
+ for (p = commit->parents; p ; p = p->next) {
+ struct commit *parent = p->item;
+ printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev));
+ }
+}
+
void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
{
static char this_header[16384];
opt->loginfo = NULL;
if (!opt->verbose_header) {
- puts(sha1_to_hex(commit->object.sha1));
+ fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
+ if (opt->parents)
+ show_parents(commit, abbrev_commit);
+ putchar('\n');
return;
}
printf("%s%s",
opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
diff_unique_abbrev(commit->object.sha1, abbrev_commit));
- if (parent)
+ if (opt->parents)
+ show_parents(commit, abbrev_commit);
+ if (parent)
printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
if (argc < 2)
usage(ls_tree_usage);
- if (get_sha1(argv[1], sha1) < 0)
- usage(ls_tree_usage);
+ if (get_sha1(argv[1], sha1))
+ die("Not a valid object name %s", argv[1]);
pathspec = get_pathspec(prefix, argv + 2);
tree = parse_tree_indirect(sha1);
usage(merge_base_usage);
argc--; argv++;
}
- if (argc != 3 ||
- get_sha1(argv[1], rev1key) ||
- get_sha1(argv[2], rev2key))
+ if (argc != 3)
usage(merge_base_usage);
+ if (get_sha1(argv[1], rev1key))
+ die("Not a valid object name %s", argv[1]);
+ if (get_sha1(argv[2], rev2key))
+ die("Not a valid object name %s", argv[2]);
rev1 = lookup_commit_reference(rev1key);
rev2 = lookup_commit_reference(rev2key);
if (!rev1 || !rev2)
static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
{
- char branch1_sha1[50];
-
/* If it's already branch1, don't bother showing it */
if (!branch1)
return;
- memcpy(branch1_sha1, sha1_to_hex_zero(branch1->sha1), 41);
printf("0 %06o->%06o %s->%s %s%s\n",
branch1->mode, result->mode,
- branch1_sha1, sha1_to_hex_zero(result->sha1),
+ sha1_to_hex_zero(branch1->sha1),
+ sha1_to_hex_zero(result->sha1),
base, result->path);
}
unsigned char sha1[20];
void *buf;
- if (get_sha1(rev, sha1) < 0)
+ if (get_sha1(rev, sha1))
die("unknown rev %s", rev);
buf = fill_tree_descriptor(desc, sha1);
if (!buf)
struct unpacked {
struct object_entry *entry;
void *data;
+ struct delta_index *index;
};
/*
* more importantly, the bigger file is likely the more recent
* one.
*/
-static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_depth)
+static int try_delta(struct unpacked *trg, struct unpacked *src,
+ struct delta_index *src_index, unsigned max_depth)
{
- struct object_entry *cur_entry = cur->entry;
- struct object_entry *old_entry = old->entry;
- unsigned long size, oldsize, delta_size, sizediff;
- long max_size;
+ struct object_entry *trg_entry = trg->entry;
+ struct object_entry *src_entry = src->entry;
+ unsigned long size, src_size, delta_size, sizediff, max_size;
void *delta_buf;
/* Don't bother doing diffs between different types */
- if (cur_entry->type != old_entry->type)
+ if (trg_entry->type != src_entry->type)
return -1;
/* We do not compute delta to *create* objects we are not
* going to pack.
*/
- if (cur_entry->preferred_base)
+ if (trg_entry->preferred_base)
return -1;
- /* If the current object is at pack edge, take the depth the
+ /*
+ * If the current object is at pack edge, take the depth the
* objects that depend on the current object into account --
* otherwise they would become too deep.
*/
- if (cur_entry->delta_child) {
- if (max_depth <= cur_entry->delta_limit)
+ if (trg_entry->delta_child) {
+ if (max_depth <= trg_entry->delta_limit)
return 0;
- max_depth -= cur_entry->delta_limit;
+ max_depth -= trg_entry->delta_limit;
}
-
- size = cur_entry->size;
- oldsize = old_entry->size;
- sizediff = oldsize > size ? oldsize - size : size - oldsize;
-
- if (size < 50)
- return -1;
- if (old_entry->depth >= max_depth)
+ if (src_entry->depth >= max_depth)
return 0;
- /*
- * NOTE!
- *
- * We always delta from the bigger to the smaller, since that's
- * more space-efficient (deletes don't have to say _what_ they
- * delete).
- */
+ /* Now some size filtering euristics. */
+ size = trg_entry->size;
max_size = size / 2 - 20;
- if (cur_entry->delta)
- max_size = cur_entry->delta_size-1;
+ if (trg_entry->delta)
+ max_size = trg_entry->delta_size-1;
+ src_size = src_entry->size;
+ sizediff = src_size < size ? size - src_size : 0;
if (sizediff >= max_size)
return 0;
- delta_buf = diff_delta(old->data, oldsize,
- cur->data, size, &delta_size, max_size);
+
+ delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size);
if (!delta_buf)
return 0;
- cur_entry->delta = old_entry;
- cur_entry->delta_size = delta_size;
- cur_entry->depth = old_entry->depth + 1;
+
+ trg_entry->delta = src_entry;
+ trg_entry->delta_size = delta_size;
+ trg_entry->depth = src_entry->depth + 1;
free(delta_buf);
- return 0;
+ return 1;
}
static void progress_interval(int signum)
*/
continue;
+ if (entry->size < 50)
+ continue;
+ if (n->index)
+ free_delta_index(n->index);
free(n->data);
n->entry = entry;
n->data = read_sha1_file(entry->sha1, type, &size);
if (size != entry->size)
- die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size);
+ die("object %s inconsistent object length (%lu vs %lu)",
+ sha1_to_hex(entry->sha1), size, entry->size);
+ n->index = create_delta_index(n->data, size);
+ if (!n->index)
+ die("out of memory");
j = window;
while (--j > 0) {
m = array + other_idx;
if (!m->entry)
break;
- if (try_delta(n, m, depth) < 0)
+ if (try_delta(n, m, m->index, depth) < 0)
break;
}
#if 0
if (progress)
fputc('\n', stderr);
- for (i = 0; i < window; ++i)
+ for (i = 0; i < window; ++i) {
+ if (array[i].index)
+ free_delta_index(array[i].index);
free(array[i].data);
+ }
free(array);
}
setup_git_directory();
+ progress = isatty(2);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
usage(pack_usage);
continue;
}
+ if (!strcmp("--progress", arg)) {
+ progress = 1;
+ continue;
+ }
if (!strcmp("-q", arg)) {
progress = 0;
continue;
#include <string.h>
#include "delta.h"
-void *patch_delta(void *src_buf, unsigned long src_size,
- void *delta_buf, unsigned long delta_size,
+void *patch_delta(const void *src_buf, unsigned long src_size,
+ const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size)
{
const unsigned char *data, *top;
active_nr = ntohl(hdr->hdr_entries);
active_alloc = alloc_nr(active_nr);
- active_cache = calloc(active_alloc, sizeof(struct cache_entry *));
+ active_cache = xcalloc(active_alloc, sizeof(struct cache_entry *));
offset = sizeof(*hdr);
for (i = 0; i < active_nr; i++) {
if (1 < index_only + update)
usage(read_tree_usage);
- if (get_sha1(arg, sha1) < 0)
- usage(read_tree_usage);
+ if (get_sha1(arg, sha1))
+ die("Not a valid object name %s", arg);
if (list_tree(sha1) < 0)
die("failed to unpack tree object %s", arg);
stage++;
char ref[1000];
int fd, len, written;
-#ifdef USE_SYMLINK_HEAD
- if (!only_use_symrefs) {
+#ifndef NO_SYMLINK_HEAD
+ if (prefer_symlink_refs) {
unlink(git_HEAD);
if (!symlink(refs_heads_master, git_HEAD))
return 0;
#include <regex.h>
static const char git_config_set_usage[] =
-"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
+"git-repo-config [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
static char* key = NULL;
-static char* value = NULL;
+static regex_t* key_regexp = NULL;
static regex_t* regexp = NULL;
+static int show_keys = 0;
+static int use_key_regexp = 0;
static int do_all = 0;
static int do_not_match = 0;
static int seen = 0;
static int show_config(const char* key_, const char* value_)
{
+ char value[256];
+ const char *vptr = value;
+ int dup_error = 0;
+
if (value_ == NULL)
value_ = "";
- if (!strcmp(key_, key) &&
- (regexp == NULL ||
+ if (!use_key_regexp && strcmp(key_, key))
+ return 0;
+ if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+ return 0;
+ if (regexp != NULL &&
(do_not_match ^
- !regexec(regexp, value_, 0, NULL, 0)))) {
- if (do_all) {
- printf("%s\n", value_);
- return 0;
- }
- if (seen > 0) {
- fprintf(stderr, "More than one value: %s\n", value);
- free(value);
- }
+ regexec(regexp, value_, 0, NULL, 0)))
+ return 0;
- if (type == T_INT) {
- value = malloc(256);
- sprintf(value, "%d", git_config_int(key_, value_));
- } else if (type == T_BOOL) {
- value = malloc(256);
- sprintf(value, "%s", git_config_bool(key_, value_)
- ? "true" : "false");
- } else {
- value = strdup(value_);
- }
- seen++;
+ if (show_keys)
+ printf("%s ", key_);
+ if (seen && !do_all)
+ dup_error = 1;
+ if (type == T_INT)
+ sprintf(value, "%d", git_config_int(key_, value_));
+ else if (type == T_BOOL)
+ vptr = git_config_bool(key_, value_) ? "true" : "false";
+ else
+ vptr = value_;
+ seen++;
+ if (dup_error) {
+ error("More than one value for the key %s: %s",
+ key_, vptr);
}
+ else
+ printf("%s\n", vptr);
+
return 0;
}
key[i] = tolower(key_[i]);
key[i] = 0;
+ if (use_key_regexp) {
+ key_regexp = (regex_t*)malloc(sizeof(regex_t));
+ if (regcomp(key_regexp, key, REG_EXTENDED)) {
+ fprintf(stderr, "Invalid key pattern: %s\n", key_);
+ return -1;
+ }
+ }
+
if (regex_) {
if (regex_[0] == '!') {
do_not_match = 1;
}
git_config(show_config);
- if (value) {
- printf("%s\n", value);
- free(value);
- }
free(key);
if (regexp) {
regfree(regexp);
}
if (do_all)
- return 0;
+ return !seen;
- return seen == 1 ? 0 : 1;
+ return (seen == 1) ? 0 : 1;
}
int main(int argc, const char **argv)
type = T_INT;
else if (!strcmp(argv[1], "--bool"))
type = T_BOOL;
+ else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
+ return git_config(show_all_config);
else
break;
argc--;
argv++;
}
- if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
- return git_config(show_all_config);
-
switch (argc) {
case 2:
return get_value(argv[1], NULL);
else if (!strcmp(argv[1], "--get-all")) {
do_all = 1;
return get_value(argv[2], NULL);
+ } else if (!strcmp(argv[1], "--get-regexp")) {
+ show_keys = 1;
+ use_key_regexp = 1;
+ do_all = 1;
+ return get_value(argv[2], NULL);
} else
return git_config_set(argv[1], argv[2]);
else if (!strcmp(argv[1], "--get-all")) {
do_all = 1;
return get_value(argv[2], argv[3]);
+ } else if (!strcmp(argv[1], "--get-regexp")) {
+ show_keys = 1;
+ use_key_regexp = 1;
+ do_all = 1;
+ return get_value(argv[2], argv[3]);
} else if (!strcmp(argv[1], "--replace-all"))
return git_config_set_multivar(argv[2], argv[3], NULL, 1);
for_each_ref(handle_one_ref);
}
+static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
+{
+ unsigned char sha1[20];
+ struct object *it;
+ struct commit *commit;
+ struct commit_list *parents;
+
+ if (*arg == '^') {
+ flags ^= UNINTERESTING;
+ arg++;
+ }
+ if (get_sha1(arg, sha1))
+ return 0;
+ while (1) {
+ it = get_reference(revs, arg, sha1, 0);
+ if (strcmp(it->type, tag_type))
+ break;
+ memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
+ }
+ if (strcmp(it->type, commit_type))
+ return 0;
+ commit = (struct commit *)it;
+ for (parents = commit->parents; parents; parents = parents->next) {
+ it = &parents->item->object;
+ it->flags |= flags;
+ add_pending_object(revs, it, arg);
+ }
+ return 1;
+}
+
void init_revisions(struct rev_info *revs)
{
memset(revs, 0, sizeof(*revs));
revs->max_count = atoi(arg + 12);
continue;
}
- /* accept -<digit>, like traditilnal "head" */
+ /* accept -<digit>, like traditional "head" */
if ((*arg == '-') && isdigit(arg[1])) {
revs->max_count = atoi(arg + 1);
continue;
}
if (!strcmp(arg, "-c")) {
revs->diff = 1;
+ revs->dense_combined_merges = 0;
revs->combine_merges = 1;
continue;
}
include = get_reference(revs, next, sha1, flags);
if (!exclude || !include)
die("Invalid revision range %s..%s", arg, next);
+
+ if (!seen_dashdash) {
+ *dotdot = '.';
+ verify_non_filename(revs->prefix, arg);
+ }
add_pending_object(revs, exclude, this);
add_pending_object(revs, include, next);
continue;
}
*dotdot = '.';
}
+ dotdot = strstr(arg, "^@");
+ if (dotdot && !dotdot[2]) {
+ *dotdot = 0;
+ if (add_parents_only(revs, arg, flags))
+ continue;
+ *dotdot = '^';
+ }
local_flags = 0;
if (*arg == '^') {
local_flags = UNINTERESTING;
arg++;
}
- if (get_sha1(arg, sha1) < 0) {
+ if (get_sha1(arg, sha1)) {
int j;
if (seen_dashdash || local_flags)
die("bad revision '%s'", arg);
- /* If we didn't have a "--", all filenames must exist */
+ /* If we didn't have a "--":
+ * (1) all filenames must exist;
+ * (2) all rev-args must not be interpretable
+ * as a valid filename.
+ * but the latter we have checked in the main loop.
+ */
for (j = i; j < argc; j++)
verify_filename(revs->prefix, argv[j]);
revs->prune_data = get_pathspec(revs->prefix, argv + i);
break;
}
+ if (!seen_dashdash)
+ verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, sha1, flags ^ local_flags);
add_pending_object(revs, object, arg);
}
if (def && !revs->pending_objects) {
unsigned char sha1[20];
struct object *object;
- if (get_sha1(def, sha1) < 0)
+ if (get_sha1(def, sha1))
die("bad default revision '%s'", def);
object = get_reference(revs, def, sha1, 0);
add_pending_object(revs, object, def);
if (!lstat(name, &st))
return;
if (errno == ENOENT)
- die("ambiguous argument '%s': unknown revision or filename\n"
- "Use '--' to separate filenames from revisions", arg);
+ die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+ "Use '--' to separate paths from revisions", arg);
die("'%s': %s", arg, strerror(errno));
}
+/*
+ * Opposite of the above: the command line did not have -- marker
+ * and we parsed the arg as a refname. It should not be interpretable
+ * as a filename.
+ */
+void verify_non_filename(const char *prefix, const char *arg)
+{
+ const char *name;
+ struct stat st;
+
+ if (*arg == '-')
+ return; /* flag */
+ name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
+ if (!lstat(name, &st))
+ die("ambiguous argument '%s': both revision and filename\n"
+ "Use '--' to separate filenames from revisions", arg);
+ if (errno != ENOENT)
+ die("'%s': %s", arg, strerror(errno));
+}
+
const char **get_pathspec(const char *prefix, const char **pathspec)
{
const char *entry = *pathspec;
char * sha1_to_hex(const unsigned char *sha1)
{
- static char buffer[50];
+ static int bufno;
+ static char hexbuffer[4][50];
static const char hex[] = "0123456789abcdef";
- char *buf = buffer;
+ char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
int i;
for (i = 0; i < 20; i++) {
#include "refs.h"
static const char show_branch_usage[] =
-"git-show-branch [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
+"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
static int default_num = 0;
static int default_alloc = 0;
return git_default_config(var, value);
}
+static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
+{
+ /* If the commit is tip of the named branches, do not
+ * omit it.
+ * Otherwise, if it is a merge that is reachable from only one
+ * tip, it is not that interesting.
+ */
+ int i, flag, count;
+ for (i = 0; i < n; i++)
+ if (rev[i] == commit)
+ return 0;
+ flag = commit->object.flags;
+ for (i = count = 0; i < n; i++) {
+ if (flag & (1u << (i + REV_SHIFT)))
+ count++;
+ }
+ if (count == 1)
+ return 1;
+ return 0;
+}
+
int main(int ac, char **av)
{
struct commit *rev[MAX_REVS], *commit;
int with_current_branch = 0;
int head_at = -1;
int topics = 0;
+ int dense = 1;
setup_git_directory();
git_config(git_show_branch_config);
lifo = 1;
else if (!strcmp(arg, "--topics"))
topics = 1;
+ else if (!strcmp(arg, "--sparse"))
+ dense = 0;
else if (!strcmp(arg, "--date-order"))
lifo = 0;
else
shown_merge_point |= is_merge_point;
if (1 < num_rev) {
- int is_merge = !!(commit->parents && commit->parents->next);
+ int is_merge = !!(commit->parents &&
+ commit->parents->next);
if (topics &&
!is_merge_point &&
(this_flag & (1u << REV_SHIFT)))
continue;
-
+ if (dense && is_merge &&
+ omit_in_dense(commit, rev, num_rev))
+ continue;
for (i = 0; i < num_rev; i++) {
int mark;
if (!(this_flag & (1u << (i + REV_SHIFT))))
commit_id = argv[arg];
url = argv[arg + 1];
if (get_sha1(commit_id, sha1))
- usage(ssh_push_usage);
+ die("Not a valid object name %s", commit_id);
memcpy(hex, sha1_to_hex(sha1), sizeof(hex));
argv[arg] = hex;
test_expect_success 'hierarchical section value' 'cmp .git/config expect'
+cat > expect << EOF
+beta.noindent=sillyValue
+nextsection.nonewline=wow2 for me
+123456.a123=987
+1.2.3.alpha=beta
+EOF
+
+test_expect_success 'working --list' \
+ 'git-repo-config --list > output && cmp output expect'
+
+cat > expect << EOF
+beta.noindent sillyValue
+nextsection.nonewline wow2 for me
+EOF
+
+test_expect_success '--get-regexp' \
+ 'git-repo-config --get-regexp in > output && cmp output expect'
+
cat > .git/config << EOF
[novalue]
variable
test_expect_success 'get variable with no value' \
'git-repo-config --get novalue.variable ^$'
+git-repo-config > output 2>&1
+
+test_expect_success 'no arguments, but no crash' \
+ "test $? = 129 && grep usage output"
+
+cat > .git/config << EOF
+[a.b]
+ c = d
+EOF
+
+git-repo-config a.x y
+
+cat > expect << EOF
+[a.b]
+ c = d
+[a]
+ x = y
+EOF
+
+test_expect_success 'new section is partial match of another' 'cmp .git/config expect'
+
+git-repo-config b.x y
+git-repo-config a.b c
+
+cat > expect << EOF
+[a.b]
+ c = d
+[a]
+ x = y
+ b = c
+[b]
+ x = y
+EOF
+
+test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
+
test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git-update-index --again test.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'update-index --add' \
+ 'echo hello world >file1 &&
+ echo goodbye people >file2 &&
+ git-update-index --add file1 file2 &&
+ git-ls-files -s >current &&
+ cmp current - <<\EOF
+100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 file1
+100644 9db8893856a8a02eaa73470054b7c1c5a7c82e47 0 file2
+EOF'
+
+test_expect_success 'update-index --again' \
+ 'rm -f file1 &&
+ echo hello everybody >file2 &&
+ if git-update-index --again
+ then
+ echo should have refused to remove file1
+ exit 1
+ else
+ echo happy - failed as expected
+ fi &&
+ git-ls-files -s >current &&
+ cmp current - <<\EOF
+100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 file1
+100644 9db8893856a8a02eaa73470054b7c1c5a7c82e47 0 file2
+EOF'
+
+test_expect_success 'update-index --remove --again' \
+ 'git-update-index --remove --again &&
+ git-ls-files -s >current &&
+ cmp current - <<\EOF
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF'
+
+test_expect_success 'first commit' 'git-commit -m initial'
+
+test_expect_success 'update-index again' \
+ 'mkdir -p dir1 &&
+ echo hello world >dir1/file3 &&
+ echo goodbye people >file2 &&
+ git-update-index --add file2 dir1/file3 &&
+ echo hello everybody >file2
+ echo happy >dir1/file3 &&
+ git-update-index --again &&
+ git-ls-files -s >current &&
+ cmp current - <<\EOF
+100644 53ab446c3f4e42ce9bb728a0ccb283a101be4979 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF'
+
+test_expect_success 'update-index --update from subdir' \
+ 'echo not so happy >file2 &&
+ cd dir1 &&
+ cat ../file2 >file3 &&
+ git-update-index --again &&
+ cd .. &&
+ git-ls-files -s >current &&
+ cmp current - <<\EOF
+100644 d7fb3f695f06c759dbf3ab00046e7cc2da22d10f 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF'
+
+test_expect_success 'update-index --update with pathspec' \
+ 'echo very happy >file2 &&
+ cat file2 >dir1/file3 &&
+ git-update-index --again dir1/ &&
+ git-ls-files -s >current &&
+ cmp current - <<\EOF
+100644 594fb5bb1759d90998e2bf2a38261ae8e243c760 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF'
+
+test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Binary diff and apply
+'
+
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+ 'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
+ git-update-index --add a b c d &&
+ echo git >a &&
+ cat ../test4012.png >b &&
+ echo git >c &&
+ cat b b >d'
+
+test_expect_success 'diff without --binary' \
+ 'git-diff | git-apply --stat --summary >current &&
+ cmp current - <<\EOF
+ a | 2 +-
+ b | Bin
+ c | 2 +-
+ d | Bin
+ 4 files changed, 2 insertions(+), 2 deletions(-)
+EOF'
+
+test_expect_success 'diff with --binary' \
+ 'git-diff --binary | git-apply --stat --summary >current &&
+ cmp current - <<\EOF
+ a | 2 +-
+ b | Bin
+ c | 2 +-
+ d | Bin
+ 4 files changed, 2 insertions(+), 2 deletions(-)
+EOF'
+
+# apply needs to be able to skip the binary material correctly
+# in order to report the line number of a corrupt patch.
+test_expect_success 'apply detecting corrupt patch correctly' \
+ 'git-diff | sed -e 's/-CIT/xCIT/' >broken &&
+ if git-apply --stat --summary broken 2>detected
+ then
+ echo unhappy - should have detected an error
+ (exit 1)
+ else
+ echo happy
+ fi &&
+ detected=`cat detected` &&
+ detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+ detected=`sed -ne "${detected}p" broken` &&
+ test "$detected" = xCIT'
+
+test_expect_success 'apply detecting corrupt patch correctly' \
+ 'git-diff --binary | sed -e 's/-CIT/xCIT/' >broken &&
+ if git-apply --stat --summary broken 2>detected
+ then
+ echo unhappy - should have detected an error
+ (exit 1)
+ else
+ echo happy
+ fi &&
+ detected=`cat detected` &&
+ detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+ detected=`sed -ne "${detected}p" broken` &&
+ test "$detected" = xCIT'
+
+test_expect_success 'initial commit' 'git-commit -a -m initial'
+
+# Try removal (b), modification (d), and creation (e).
+test_expect_success 'diff-index with --binary' \
+ 'echo AIT >a && mv b e && echo CIT >c && cat e >d &&
+ git-update-index --add --remove a b c d e &&
+ tree0=`git-write-tree` &&
+ git-diff --cached --binary >current &&
+ git-apply --stat --summary current'
+
+test_expect_success 'apply binary patch' \
+ 'git-reset --hard &&
+ git-apply --binary --index <current &&
+ tree1=`git-write-tree` &&
+ test "$tree1" = "$tree0"'
+
+test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
+#
+
+test_description='test clone --reference'
+. ./test-lib.sh
+
+base_dir=`pwd`
+
+test_expect_success 'preparing first repository' \
+'test_create_repo A && cd A &&
+echo first > file1 &&
+git add file1 &&
+git commit -m initial'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone A B && cd B &&
+echo second > file2 &&
+git add file2 &&
+git commit -m addition &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'cloning with reference' \
+'git clone -l -s --reference B A C'
+
+cd "$base_dir"
+
+test_expect_success 'existance of info/alternates' \
+'test `wc -l <C/.git/objects/info/alternates` = 2'
+
+cd "$base_dir"
+
+test_expect_success 'pulling from reference' \
+'cd C &&
+git pull ../B'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used' \
+'cd C &&
+echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_expect_success 'updating origin' \
+'cd A &&
+echo third > file3 &&
+git add file3 &&
+git commit -m update &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'pulling changes from origin' \
+'cd C &&
+git pull origin'
+
+cd "$base_dir"
+
+# the 2 local objects are commit and tree from the merge
+test_expect_success 'that alternate to origin gets used' \
+'cd C &&
+echo "2 objects" > expected &&
+git count-objects | cut -d, -f1 > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_done
strbuf_append_string(¤t_path, "/");
/* FALLTHROUGH */
case 2:
- if (get_sha1(argv[1], sha1) < 0)
- usage(tar_tree_usage);
+ if (get_sha1(argv[1], sha1))
+ die("Not a valid object name %s", argv[1]);
break;
default:
usage(tar_tree_usage);
{
unsigned char sha1[20];
- if (argc != 2 || get_sha1(argv[1], sha1))
+ if (argc != 2)
usage("git-unpack-file <sha1>");
+ if (get_sha1(argv[1], sha1))
+ die("Not a valid object name %s", argv[1]);
setup_git_directory();
git_config(git_default_config);
const char *p = prefix_path(prefix, prefix_length, path);
if (!verify_path(p)) {
fprintf(stderr, "Ignoring path %s\n", path);
- return;
+ goto free_return;
}
if (mark_valid_only) {
if (mark_valid(p))
die("Unable to mark file %s", path);
- return;
+ goto free_return;
}
if (force_remove) {
if (remove_file_from_cache(p))
die("git-update-index: unable to remove %s", path);
report("remove '%s'", path);
- return;
+ goto free_return;
}
if (add_file_to_cache(p))
die("Unable to process file %s", path);
report("add '%s'", path);
+ free_return:
+ if (p < path || p > path + strlen(path))
+ free((char*)p);
}
static void read_index_info(int line_termination)
}
static const char update_index_usage[] =
-"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--cacheinfo] [--chmod=(+|-)x] [--info-only] [--force-remove] [--stdin] [--index-info] [--ignore-missing] [-z] [--verbose] [--] <file>...";
+"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again] [--ignore-missing] [-z] [--verbose] [--] <file>...";
static unsigned char head_sha1[20];
static unsigned char merge_head_sha1[20];
struct cache_entry *ce;
if (get_tree_entry(ent, path, sha1, &mode)) {
- error("%s: not in %s branch.", path, which);
+ if (which)
+ error("%s: not in %s branch.", path, which);
return NULL;
}
if (mode == S_IFDIR) {
- error("%s: not a blob in %s branch.", path, which);
+ if (which)
+ error("%s: not a blob in %s branch.", path, which);
return NULL;
}
size = cache_entry_size(namelen);
}
}
-static int do_unresolve(int ac, const char **av)
+static int do_unresolve(int ac, const char **av,
+ const char *prefix, int prefix_length)
{
int i;
int err = 0;
for (i = 1; i < ac; i++) {
const char *arg = av[i];
- err |= unresolve_one(arg);
+ const char *p = prefix_path(prefix, prefix_length, arg);
+ err |= unresolve_one(p);
+ if (p < arg || p > arg + strlen(arg))
+ free((char*)p);
}
return err;
}
+static int do_reupdate(int ac, const char **av,
+ const char *prefix, int prefix_length)
+{
+ /* Read HEAD and run update-index on paths that are
+ * merged and already different between index and HEAD.
+ */
+ int pos;
+ int has_head = 1;
+ const char **pathspec = get_pathspec(prefix, av + 1);
+
+ if (read_ref(git_path("HEAD"), head_sha1))
+ /* If there is no HEAD, that means it is an initial
+ * commit. Update everything in the index.
+ */
+ has_head = 0;
+ redo:
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+ struct cache_entry *old = NULL;
+ int save_nr;
+
+ if (ce_stage(ce) || !ce_path_match(ce, pathspec))
+ continue;
+ if (has_head)
+ old = read_one_ent(NULL, head_sha1,
+ ce->name, ce_namelen(ce), 0);
+ if (old && ce->ce_mode == old->ce_mode &&
+ !memcmp(ce->sha1, old->sha1, 20)) {
+ free(old);
+ continue; /* unchanged */
+ }
+ /* Be careful. The working tree may not have the
+ * path anymore, in which case, under 'allow_remove',
+ * or worse yet 'allow_replace', active_nr may decrease.
+ */
+ save_nr = active_nr;
+ update_one(ce->name + prefix_length, prefix, prefix_length);
+ if (save_nr != active_nr)
+ goto redo;
+ }
+ return 0;
+}
+
int main(int argc, const char **argv)
{
int i, newfd, entries, has_errors = 0, line_termination = '\n';
break;
}
if (!strcmp(path, "--unresolve")) {
- has_errors = do_unresolve(argc - i, argv + i);
+ has_errors = do_unresolve(argc - i, argv + i,
+ prefix, prefix_length);
+ if (has_errors)
+ active_cache_changed = 0;
+ goto finish;
+ }
+ if (!strcmp(path, "--again")) {
+ has_errors = do_reupdate(argc - i, argv + i,
+ prefix, prefix_length);
if (has_errors)
active_cache_changed = 0;
goto finish;
strbuf_init(&buf);
while (1) {
char *path_name;
+ const char *p;
read_line(&buf, stdin, line_termination);
if (buf.eof)
break;
path_name = unquote_c_style(buf.buf, NULL);
else
path_name = buf.buf;
- update_one(path_name, prefix, prefix_length);
- if (set_executable_bit) {
- const char *p = prefix_path(prefix, prefix_length, path_name);
+ p = prefix_path(prefix, prefix_length, path_name);
+ update_one(p, NULL, 0);
+ if (set_executable_bit)
chmod_path(set_executable_bit, p);
- }
+ if (p < path_name || p > path_name + strlen(path_name))
+ free((char*) p);
if (path_name != buf.buf)
free(path_name);
}
refname = argv[1];
value = argv[2];
oldval = argv[3];
- if (get_sha1(value, sha1) < 0)
+ if (get_sha1(value, sha1))
die("%s: not a valid SHA1", value);
memset(oldsha1, 0, 20);
- if (oldval && get_sha1(oldval, oldsha1) < 0)
+ if (oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
path = resolve_ref(git_path("%s", refname), currsha1, !!oldval);