mirror of
https://github.com/git/git.git
synced 2026-04-01 20:40:08 +02:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
@@ -129,3 +129,6 @@ For C programs:
|
||||
used in the git core command set (unless your command is clearly
|
||||
separate from it, such as an importer to convert random-scm-X
|
||||
repositories to git).
|
||||
|
||||
- When we pass <string, length> pair to functions, we should try to
|
||||
pass them in that order.
|
||||
|
||||
@@ -30,8 +30,18 @@ Updates since v1.6.2
|
||||
|
||||
(performance)
|
||||
|
||||
* many uses of lstat(2) in the codepath for "git checkout" have been
|
||||
optimized out.
|
||||
|
||||
(usability, bells and whistles)
|
||||
|
||||
* rsync:/path/to/repo can be used to run git over rsync for local
|
||||
repositories. It may not be useful in practice; meant primarily for
|
||||
testing.
|
||||
|
||||
* (msysgit) progress output that is sent over the sideband protocol can
|
||||
be handled appropriately in Windows console.
|
||||
|
||||
* "--pretty=<style>" option to the log family of commands can now be
|
||||
spelled as "--format=<style>". In addition, --format=%formatstring
|
||||
is a short-hand for --pretty=tformat:%formatstring.
|
||||
@@ -62,6 +72,8 @@ Updates since v1.6.2
|
||||
|
||||
* git-format-patch can be told to produce deep or shallow message threads.
|
||||
|
||||
* git-grep learned to highlight the found substrings in color.
|
||||
|
||||
* git-imap-send learned to work around Thunderbird's inability to easily
|
||||
disable format=flowed with a new configuration, imap.preformattedHTML.
|
||||
|
||||
@@ -71,6 +83,8 @@ Updates since v1.6.2
|
||||
|
||||
* git-rebase can be told to report diffstat with the --stat option.
|
||||
|
||||
* Output from git-remote command has been vastly improved.
|
||||
|
||||
* git-send-email learned --confirm option to review the Cc: list before
|
||||
sending the messages out.
|
||||
|
||||
@@ -90,15 +104,30 @@ release, unless otherwise noted.
|
||||
Here are fixes that this release has, but have not been backported to
|
||||
v1.6.2.X series.
|
||||
|
||||
* 'git-submodule add' did not tolerate extra slashes and ./ in the
|
||||
path it accepted from the command line; it now is more lenient
|
||||
(if needed, backport by merging db75ada).
|
||||
* "git diff --pickaxe-regexp" did not count overlapping matches
|
||||
correctly (backport by cherry-picking 50fd699).
|
||||
|
||||
* "git-fetch" in a repository that was not cloned from anywhere said
|
||||
it cannot find 'origin', which was hard to understand for new people.
|
||||
|
||||
* git-gc spent excessive amount of time to decide if an object appears
|
||||
in a locally existing pack (if needed, backport by merging 69e020a).
|
||||
|
||||
* "git-ls-files --deleted" did not work well with GIT_DIR&GIT_WORK_TREE
|
||||
(backport by cherry-picking 8ad3dae).
|
||||
|
||||
* "git-read-tree A B C..." without -m option has been broken for a long time
|
||||
(backport by merging jc/maint-1.6.0-read-tree-overlay)
|
||||
|
||||
* 'git-submodule add' did not tolerate extra slashes and ./ in the
|
||||
path it accepted from the command line; it now is more lenient
|
||||
(if needed, backport by merging db75ada).
|
||||
|
||||
* git-send-email ignored --in-reply-to when --no-thread was given
|
||||
(backport by merging tr/maint-1.6.0-send-email-irt)
|
||||
|
||||
---
|
||||
exec >/var/tmp/1
|
||||
O=v1.6.2.1-135-g7d65c21
|
||||
O=v1.6.2.1-213-g7d4e3a7
|
||||
echo O=$(git describe master)
|
||||
git shortlog --no-merges $O..master ^maint
|
||||
|
||||
@@ -25,7 +25,7 @@ blank lines are ignored.
|
||||
The file consists of sections and variables. A section begins with
|
||||
the name of the section in square brackets and continues until the next
|
||||
section begins. Section names are not case sensitive. Only alphanumeric
|
||||
characters, '`-`' and '`.`' are allowed in section names. Each variable
|
||||
characters, `-` and `.` are allowed in section names. Each variable
|
||||
must belong to some section, which means that there must be section
|
||||
header before first setting of a variable.
|
||||
|
||||
@@ -39,7 +39,7 @@ in the section header, like in example below:
|
||||
--------
|
||||
|
||||
Subsection names can contain any characters except newline (doublequote
|
||||
'`"`' and backslash have to be escaped as '`\"`' and '`\\`',
|
||||
`"` and backslash have to be escaped as `\"` and `\\`,
|
||||
respectively) and are case sensitive. Section header cannot span multiple
|
||||
lines. Variables may belong directly to a section or to a given subsection.
|
||||
You can have `[section]` if you have `[section "subsection"]`, but you
|
||||
@@ -53,7 +53,7 @@ All the other lines are recognized as setting variables, in the form
|
||||
'name = value'. If there is no equal sign on the line, the entire line
|
||||
is taken as 'name' and the variable is recognized as boolean "true".
|
||||
The variable names are case-insensitive and only alphanumeric
|
||||
characters and '`-`' are allowed. There can be more than one value
|
||||
characters and `-` are allowed. There can be more than one value
|
||||
for a given variable; we say then that variable is multivalued.
|
||||
|
||||
Leading and trailing whitespace in a variable value is discarded.
|
||||
@@ -69,15 +69,15 @@ String values may be entirely or partially enclosed in double quotes.
|
||||
You need to enclose variable value in double quotes if you want to
|
||||
preserve leading or trailing whitespace, or if variable value contains
|
||||
beginning of comment characters (if it contains '#' or ';').
|
||||
Double quote '`"`' and backslash '`\`' characters in variable value must
|
||||
be escaped: use '`\"`' for '`"`' and '`\\`' for '`\`'.
|
||||
Double quote `"` and backslash `\` characters in variable value must
|
||||
be escaped: use `\"` for `"` and `\\` for `\`.
|
||||
|
||||
The following escape sequences (beside '`\"`' and '`\\`') are recognized:
|
||||
'`\n`' for newline character (NL), '`\t`' for horizontal tabulation (HT, TAB)
|
||||
and '`\b`' for backspace (BS). No other char escape sequence, nor octal
|
||||
The following escape sequences (beside `\"` and `\\`) are recognized:
|
||||
`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
|
||||
and `\b` for backspace (BS). No other char escape sequence, nor octal
|
||||
char sequences are valid.
|
||||
|
||||
Variable value ending in a '`\`' is continued on the next line in the
|
||||
Variable value ending in a `\` is continued on the next line in the
|
||||
customary UNIX fashion.
|
||||
|
||||
Some variables may require special value format.
|
||||
@@ -221,6 +221,11 @@ core.gitProxy::
|
||||
Can be overridden by the 'GIT_PROXY_COMMAND' environment variable
|
||||
(which always applies universally, without the special "for"
|
||||
handling).
|
||||
+
|
||||
The special string `none` can be used as the proxy command to
|
||||
specify that no proxy be used for a given domain pattern.
|
||||
This is useful for excluding servers inside a firewall from
|
||||
proxy use, while defaulting to a common proxy for external domains.
|
||||
|
||||
core.ignoreStat::
|
||||
If true, commands which modify both the working tree and the index
|
||||
@@ -382,9 +387,9 @@ core.pager::
|
||||
to override git's default settings this way, you need
|
||||
to be explicit. For example, to disable the S option
|
||||
in a backward compatible manner, set `core.pager`
|
||||
to "`less -+$LESS -FRX`". This will be passed to the
|
||||
to `less -+$LESS -FRX`. This will be passed to the
|
||||
shell by git, which will translate the final command to
|
||||
"`LESS=FRSX less -+FRSX -FRX`".
|
||||
`LESS=FRSX less -+FRSX -FRX`.
|
||||
|
||||
core.whitespace::
|
||||
A comma separated list of common whitespace problems to
|
||||
@@ -548,6 +553,25 @@ color.diff.<slot>::
|
||||
whitespace errors). The values of these variables may be specified as
|
||||
in color.branch.<slot>.
|
||||
|
||||
color.grep::
|
||||
When set to `always`, always highlight matches. When `false` (or
|
||||
`never`), never. When set to `true` or `auto`, use color only
|
||||
when the output is written to the terminal. Defaults to `false`.
|
||||
|
||||
color.grep.external::
|
||||
The string value of this variable is passed to an external 'grep'
|
||||
command as a command line option if match highlighting is turned
|
||||
on. If set to an empty string, no option is passed at all,
|
||||
turning off coloring for external 'grep' calls; this is the default.
|
||||
For GNU grep, set it to `--color=always` to highlight matches even
|
||||
when a pager is used.
|
||||
|
||||
color.grep.match::
|
||||
Use customized color for matches. The value of this variable
|
||||
may be specified as in color.branch.<slot>. It is passed using
|
||||
the environment variables 'GREP_COLOR' and 'GREP_COLORS' when
|
||||
calling an external 'grep'.
|
||||
|
||||
color.interactive::
|
||||
When set to `always`, always use colors for interactive prompts
|
||||
and displays (such as those used by "git-add --interactive").
|
||||
@@ -1161,7 +1185,7 @@ pager.<cmd>::
|
||||
particular git subcommand when writing to a tty. If
|
||||
`\--paginate` or `\--no-pager` is specified on the command line,
|
||||
it takes precedence over this option. To disable pagination for
|
||||
all commands, set `core.pager` or 'GIT_PAGER' to "`cat`".
|
||||
all commands, set `core.pager` or `GIT_PAGER` to `cat`.
|
||||
|
||||
pull.octopus::
|
||||
The default merge strategy to use when pulling multiple branches
|
||||
|
||||
@@ -76,8 +76,8 @@ OPTIONS
|
||||
based sha1 expressions such as "<branchname>@\{yesterday}".
|
||||
|
||||
-f::
|
||||
Force the creation of a new branch even if it means deleting
|
||||
a branch that already exists with the same name.
|
||||
Reset <branchname> to <startpoint> if <branchname> exists
|
||||
already. Without `-f` 'git-branch' refuses to change an existing branch.
|
||||
|
||||
-m::
|
||||
Move/rename a branch and the corresponding reflog.
|
||||
|
||||
@@ -133,9 +133,9 @@ the conflicted merge in the specified paths.
|
||||
When this parameter names a non-branch (but still a valid commit object),
|
||||
your HEAD becomes 'detached'.
|
||||
+
|
||||
As a special case, the "`@\{-N\}`" syntax for the N-th last branch
|
||||
As a special case, the `"@\{-N\}"` syntax for the N-th last branch
|
||||
checks out the branch (instead of detaching). You may also specify
|
||||
"`-`" which is synonymous with "`@\{-1\}`".
|
||||
`-` which is synonymous with `"@\{-1\}"`.
|
||||
|
||||
|
||||
Detached HEAD
|
||||
|
||||
@@ -17,6 +17,7 @@ SYNOPSIS
|
||||
[-l | --files-with-matches] [-L | --files-without-match]
|
||||
[-z | --null]
|
||||
[-c | --count] [--all-match]
|
||||
[--color | --no-color]
|
||||
[-A <post-context>] [-B <pre-context>] [-C <context>]
|
||||
[-f <file>] [-e] <pattern>
|
||||
[--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
|
||||
@@ -105,6 +106,13 @@ OPTIONS
|
||||
Instead of showing every matched line, show the number of
|
||||
lines that match.
|
||||
|
||||
--color::
|
||||
Show colored matches.
|
||||
|
||||
--no-color::
|
||||
Turn off match highlighting, even when the configuration file
|
||||
gives the default to color output.
|
||||
|
||||
-[ABC] <context>::
|
||||
Show `context` trailing (`A` -- after), or leading (`B`
|
||||
-- before), or both (`C` -- context) lines, and place a
|
||||
|
||||
@@ -146,7 +146,7 @@ And here is another line that is cleanly resolved or unmodified.
|
||||
------------
|
||||
|
||||
The area where a pair of conflicting changes happened is marked with markers
|
||||
"`<<<<<<<`", "`=======`", and "`>>>>>>>`". The part before the "`=======`"
|
||||
`<<<<<<<`, `=======`, and `>>>>>>>`. The part before the `=======`
|
||||
is typically your side, and the part afterwards is typically their side.
|
||||
|
||||
The default format does not show what the original said in the conflicting
|
||||
@@ -173,8 +173,8 @@ Git makes conflict resolution easy.
|
||||
And here is another line that is cleanly resolved or unmodified.
|
||||
------------
|
||||
|
||||
In addition to the "`<<<<<<<`", "`=======`", and "`>>>>>>>`" markers, it uses
|
||||
another "`|||||||`" marker that is followed by the original text. You can
|
||||
In addition to the `<<<<<<<`, `=======`, and `>>>>>>>` markers, it uses
|
||||
another `|||||||` marker that is followed by the original text. You can
|
||||
tell that the original just stated a fact, and your side simply gave in to
|
||||
that statement and gave up, while the other side tried to have a more
|
||||
positive attitude. You can sometimes come up with a better resolution by
|
||||
|
||||
@@ -24,8 +24,8 @@ every time you push into it, by setting up 'hooks' there. See
|
||||
documentation for linkgit:git-receive-pack[1].
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
OPTIONS[[OPTIONS]]
|
||||
------------------
|
||||
<repository>::
|
||||
The "remote" repository that is destination of a push
|
||||
operation. This parameter can be either a URL
|
||||
@@ -187,6 +187,28 @@ reason::
|
||||
Examples
|
||||
--------
|
||||
|
||||
git push::
|
||||
Works like `git push <remote>`, where <remote> is the
|
||||
current branch's remote (or `origin`, if no remote is
|
||||
configured for the current branch).
|
||||
|
||||
git push origin::
|
||||
Without additional configuration, works like
|
||||
`git push origin :`.
|
||||
+
|
||||
The default behavior of this command when no <refspec> is given can be
|
||||
configured by setting the `push` option of the remote.
|
||||
+
|
||||
For example, to default to pushing only the current branch to `origin`
|
||||
use `git config remote.origin.push HEAD`. Any valid <refspec> (like
|
||||
the ones in the examples below) can be configured as the default for
|
||||
`git push origin`.
|
||||
|
||||
git push origin :::
|
||||
Push "matching" branches to `origin`. See
|
||||
<refspec> in the <<OPTIONS,OPTIONS>> section above for a
|
||||
description of "matching" branches.
|
||||
|
||||
git push origin master::
|
||||
Find a ref that matches `master` in the source repository
|
||||
(most likely, it would find `refs/heads/master`), and update
|
||||
|
||||
@@ -13,6 +13,7 @@ SYNOPSIS
|
||||
'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
|
||||
'git remote rename' <old> <new>
|
||||
'git remote rm' <name>
|
||||
'git remote set-head' <name> [-a | -d | <branch>]
|
||||
'git remote show' [-n] <name>
|
||||
'git remote prune' [-n | --dry-run] <name>
|
||||
'git remote update' [group]
|
||||
@@ -53,8 +54,7 @@ is created. You can give more than one `-t <branch>` to track
|
||||
multiple branches without grabbing all branches.
|
||||
+
|
||||
With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
|
||||
up to point at remote's `<master>` branch instead of whatever
|
||||
branch the `HEAD` at the remote repository actually points at.
|
||||
up to point at remote's `<master>` branch. See also the set-head command.
|
||||
+
|
||||
In mirror mode, enabled with `\--mirror`, the refs will not be stored
|
||||
in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option
|
||||
@@ -76,6 +76,30 @@ the configuration file format.
|
||||
Remove the remote named <name>. All remote tracking branches and
|
||||
configuration settings for the remote are removed.
|
||||
|
||||
'set-head'::
|
||||
|
||||
Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for
|
||||
the named remote. Having a default branch for a remote is not required,
|
||||
but allows the name of the remote to be specified in lieu of a specific
|
||||
branch. For example, if the default branch for `origin` is set to
|
||||
`master`, then `origin` may be specified wherever you would normally
|
||||
specify `origin/master`.
|
||||
+
|
||||
With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted.
|
||||
+
|
||||
With `-a`, the remote is queried to determine its `HEAD`, then
|
||||
`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
|
||||
`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set
|
||||
`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
|
||||
only work if `refs/remotes/origin/next` already exists; if not it must be
|
||||
fetched first.
|
||||
+
|
||||
Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git
|
||||
remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
|
||||
`refs/remotes/origin/master`. This will only work if
|
||||
`refs/remotes/origin/master` already exists; if not it must be fetched first.
|
||||
+
|
||||
|
||||
'show'::
|
||||
|
||||
Gives some information about the remote <name>.
|
||||
|
||||
@@ -299,18 +299,18 @@ previous section means the set of commits reachable from that
|
||||
commit, following the commit ancestry chain.
|
||||
|
||||
To exclude commits reachable from a commit, a prefix `{caret}`
|
||||
notation is used. E.g. "`{caret}r1 r2`" means commits reachable
|
||||
notation is used. E.g. `{caret}r1 r2` means commits reachable
|
||||
from `r2` but exclude the ones reachable from `r1`.
|
||||
|
||||
This set operation appears so often that there is a shorthand
|
||||
for it. When you have two commits `r1` and `r2` (named according
|
||||
to the syntax explained in SPECIFYING REVISIONS above), you can ask
|
||||
for commits that are reachable from r2 excluding those that are reachable
|
||||
from r1 by "`{caret}r1 r2`" and it can be written as "`r1..r2`".
|
||||
from r1 by `{caret}r1 r2` and it can be written as `r1..r2`.
|
||||
|
||||
A similar notation "`r1\...r2`" is called symmetric difference
|
||||
A similar notation `r1\...r2` is called symmetric difference
|
||||
of `r1` and `r2` and is defined as
|
||||
"`r1 r2 --not $(git merge-base --all r1 r2)`".
|
||||
`r1 r2 --not $(git merge-base --all r1 r2)`.
|
||||
It is the set of commits that are reachable from either one of
|
||||
`r1` or `r2` but not from both.
|
||||
|
||||
|
||||
@@ -60,14 +60,13 @@ The --cc option must be repeated for each user you want on the cc list.
|
||||
Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
|
||||
introductory message for the patch series.
|
||||
+
|
||||
When '--compose' is used, git send-email gets less interactive will use the
|
||||
values of the headers you set there. If the body of the email (what you type
|
||||
after the headers and a blank line) only contains blank (or GIT: prefixed)
|
||||
lines, the summary won't be sent, but git-send-email will still use the
|
||||
Headers values if you don't removed them.
|
||||
When '--compose' is used, git send-email will use the From, Subject, and
|
||||
In-Reply-To headers specified in the message. If the body of the message
|
||||
(what you type after the headers and a blank line) only contains blank
|
||||
(or GIT: prefixed) lines the summary won't be sent, but From, Subject,
|
||||
and In-Reply-To headers will be used unless they are removed.
|
||||
+
|
||||
If it wasn't able to see a header in the summary it will ask you about it
|
||||
interactively after quitting your editor.
|
||||
Missing From or In-Reply-To headers will be prompted for.
|
||||
|
||||
--from::
|
||||
Specify the sender of the emails. This will default to
|
||||
|
||||
@@ -46,20 +46,20 @@ Here are the rules regarding the "flags" that you should follow when you are
|
||||
scripting git:
|
||||
|
||||
* it's preferred to use the non dashed form of git commands, which means that
|
||||
you should prefer `"git foo"` to `"git-foo"`.
|
||||
you should prefer `git foo` to `git-foo`.
|
||||
|
||||
* splitting short options to separate words (prefer `"git foo -a -b"`
|
||||
to `"git foo -ab"`, the latter may not even work).
|
||||
* splitting short options to separate words (prefer `git foo -a -b`
|
||||
to `git foo -ab`, the latter may not even work).
|
||||
|
||||
* when a command line option takes an argument, use the 'sticked' form. In
|
||||
other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short
|
||||
options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"`
|
||||
other words, write `git foo -oArg` instead of `git foo -o Arg` for short
|
||||
options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg`
|
||||
for long options. An option that takes optional option-argument must be
|
||||
written in the 'sticked' form.
|
||||
|
||||
* when you give a revision parameter to a command, make sure the parameter is
|
||||
not ambiguous with a name of a file in the work tree. E.g. do not write
|
||||
`"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work
|
||||
`git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work
|
||||
if you happen to have a file called `HEAD` in the work tree.
|
||||
|
||||
|
||||
@@ -99,17 +99,17 @@ usage: git-describe [options] <committish>*
|
||||
|
||||
Negating options
|
||||
~~~~~~~~~~~~~~~~
|
||||
Options with long option names can be negated by prefixing `"--no-"`. For
|
||||
example, `"git branch"` has the option `"--track"` which is 'on' by default. You
|
||||
can use `"--no-track"` to override that behaviour. The same goes for `"--color"`
|
||||
and `"--no-color"`.
|
||||
Options with long option names can be negated by prefixing `--no-`. For
|
||||
example, `git branch` has the option `--track` which is 'on' by default. You
|
||||
can use `--no-track` to override that behaviour. The same goes for `--color`
|
||||
and `--no-color`.
|
||||
|
||||
|
||||
Aggregating short options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Commands that support the enhanced option parser allow you to aggregate short
|
||||
options. This means that you can for example use `"git rm -rf"` or
|
||||
`"git clean -fdx"`.
|
||||
options. This means that you can for example use `git rm -rf` or
|
||||
`git clean -fdx`.
|
||||
|
||||
|
||||
Separating argument from the option
|
||||
|
||||
@@ -262,7 +262,7 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||
'origin' is used for that purpose. New upstream updates
|
||||
will be fetched into remote <<def_tracking_branch,tracking branches>> named
|
||||
origin/name-of-upstream-branch, which you can see using
|
||||
"`git branch -r`".
|
||||
`git branch -r`.
|
||||
|
||||
[[def_pack]]pack::
|
||||
A set of objects which have been compressed into one file (to save space
|
||||
|
||||
@@ -1136,10 +1136,10 @@ Ignoring files
|
||||
A project will often generate files that you do 'not' want to track with git.
|
||||
This typically includes files generated by a build process or temporary
|
||||
backup files made by your editor. Of course, 'not' tracking files with git
|
||||
is just a matter of 'not' calling "`git-add`" on them. But it quickly becomes
|
||||
is just a matter of 'not' calling `git-add` on them. But it quickly becomes
|
||||
annoying to have these untracked files lying around; e.g. they make
|
||||
"`git add .`" practically useless, and they keep showing up in the output of
|
||||
"`git status`".
|
||||
`git add .` practically useless, and they keep showing up in the output of
|
||||
`git status`.
|
||||
|
||||
You can tell git to ignore certain files by creating a file called .gitignore
|
||||
in the top level of your working directory, with contents such as:
|
||||
|
||||
19
Makefile
19
Makefile
@@ -126,6 +126,12 @@ all::
|
||||
# randomly break unless your underlying filesystem supports those sub-second
|
||||
# times (my ext3 doesn't).
|
||||
#
|
||||
# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
|
||||
# "st_ctim"
|
||||
#
|
||||
# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec"
|
||||
# available. This automatically turns USE_NSEC off.
|
||||
#
|
||||
# Define USE_STDEV below if you want git to care about the underlying device
|
||||
# change being considered an inode change from the update-index perspective.
|
||||
#
|
||||
@@ -657,6 +663,7 @@ ifeq ($(uname_S),Darwin)
|
||||
endif
|
||||
NO_MEMMEM = YesPlease
|
||||
THREADED_DELTA_SEARCH = YesPlease
|
||||
USE_ST_TIMESPEC = YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),SunOS)
|
||||
NEEDS_SOCKET = YesPlease
|
||||
@@ -734,6 +741,7 @@ ifeq ($(uname_S),AIX)
|
||||
NO_MEMMEM = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_STRLCPY = YesPlease
|
||||
NO_NSEC = YesPlease
|
||||
FREAD_READS_DIRECTORIES = UnfortunatelyYes
|
||||
INTERNAL_QSORT = UnfortunatelyYes
|
||||
NEEDS_LIBICONV=YesPlease
|
||||
@@ -799,6 +807,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
RUNTIME_PREFIX = YesPlease
|
||||
NO_POSIX_ONLY_PROGRAMS = YesPlease
|
||||
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
|
||||
NO_NSEC = YesPlease
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
|
||||
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
|
||||
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
|
||||
@@ -920,6 +929,15 @@ endif
|
||||
ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
|
||||
BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
|
||||
endif
|
||||
ifdef USE_NSEC
|
||||
BASIC_CFLAGS += -DUSE_NSEC
|
||||
endif
|
||||
ifdef USE_ST_TIMESPEC
|
||||
BASIC_CFLAGS += -DUSE_ST_TIMESPEC
|
||||
endif
|
||||
ifdef NO_NSEC
|
||||
BASIC_CFLAGS += -DNO_NSEC
|
||||
endif
|
||||
ifdef NO_C99_FORMAT
|
||||
BASIC_CFLAGS += -DNO_C99_FORMAT
|
||||
endif
|
||||
@@ -1363,6 +1381,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
|
||||
GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
|
||||
@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
|
||||
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
|
||||
@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
|
||||
|
||||
### Detect Tck/Tk interpreter path changes
|
||||
ifndef NO_TCLTK
|
||||
|
||||
@@ -148,7 +148,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
|
||||
if (pathspec) {
|
||||
const char **p;
|
||||
for (p = pathspec; *p; p++) {
|
||||
if (has_symlink_leading_path(strlen(*p), *p)) {
|
||||
if (has_symlink_leading_path(*p, strlen(*p))) {
|
||||
int len = prefix ? strlen(prefix) : 0;
|
||||
die("'%s' is beyond a symbolic link", *p + len);
|
||||
}
|
||||
|
||||
@@ -2360,7 +2360,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
|
||||
* In such a case, path "new_name" does not exist as
|
||||
* far as git is concerned.
|
||||
*/
|
||||
if (has_symlink_leading_path(strlen(new_name), new_name))
|
||||
if (has_symlink_leading_path(new_name, strlen(new_name)))
|
||||
return 0;
|
||||
|
||||
return error("%s: already exists in working directory", new_name);
|
||||
|
||||
@@ -52,7 +52,7 @@ static int run_remote_archiver(int argc, const char **argv,
|
||||
die("git archive: expected a flush");
|
||||
|
||||
/* Now, start reading from fd[0] and spit it out to stdout */
|
||||
rv = recv_sideband("archive", fd[0], 1, 2);
|
||||
rv = recv_sideband("archive", fd[0], 1);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
rv |= finish_connect(conn);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "dir.h"
|
||||
#include "pack-refs.h"
|
||||
#include "sigchain.h"
|
||||
#include "remote.h"
|
||||
#include "run-command.h"
|
||||
|
||||
/*
|
||||
@@ -294,43 +295,6 @@ static void remove_junk_on_signal(int signo)
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
static const struct ref *locate_head(const struct ref *refs,
|
||||
const struct ref *mapped_refs,
|
||||
const struct ref **remote_head_p)
|
||||
{
|
||||
const struct ref *remote_head = NULL;
|
||||
const struct ref *remote_master = NULL;
|
||||
const struct ref *r;
|
||||
for (r = refs; r; r = r->next)
|
||||
if (!strcmp(r->name, "HEAD"))
|
||||
remote_head = r;
|
||||
|
||||
for (r = mapped_refs; r; r = r->next)
|
||||
if (!strcmp(r->name, "refs/heads/master"))
|
||||
remote_master = r;
|
||||
|
||||
if (remote_head_p)
|
||||
*remote_head_p = remote_head;
|
||||
|
||||
/* If there's no HEAD value at all, never mind. */
|
||||
if (!remote_head)
|
||||
return NULL;
|
||||
|
||||
/* If refs/heads/master could be right, it is. */
|
||||
if (remote_master && !hashcmp(remote_master->old_sha1,
|
||||
remote_head->old_sha1))
|
||||
return remote_master;
|
||||
|
||||
/* Look for another ref that points there */
|
||||
for (r = mapped_refs; r; r = r->next)
|
||||
if (r != remote_head &&
|
||||
!hashcmp(r->old_sha1, remote_head->old_sha1))
|
||||
return r;
|
||||
|
||||
/* Nothing is the same */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct ref *write_remote_refs(const struct ref *refs,
|
||||
struct refspec *refspec, const char *reflog)
|
||||
{
|
||||
@@ -541,7 +505,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
|
||||
mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
|
||||
|
||||
head_points_at = locate_head(refs, mapped_refs, &remote_head);
|
||||
remote_head = find_ref_by_name(refs, "HEAD");
|
||||
head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
|
||||
}
|
||||
else {
|
||||
warning("You appear to have cloned an empty repository.");
|
||||
|
||||
@@ -482,7 +482,7 @@ static int sideband_demux(int fd, void *data)
|
||||
{
|
||||
int *xd = data;
|
||||
|
||||
return recv_sideband("fetch-pack", xd[0], fd, 2);
|
||||
return recv_sideband("fetch-pack", xd[0], fd);
|
||||
}
|
||||
|
||||
static int get_pack(int xd[2], char **pack_lockfile)
|
||||
@@ -800,15 +800,13 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
|
||||
int fd;
|
||||
|
||||
mtime.sec = st.st_mtime;
|
||||
#ifdef USE_NSEC
|
||||
mtime.usec = st.st_mtim.usec;
|
||||
#endif
|
||||
mtime.nsec = ST_MTIME_NSEC(st);
|
||||
if (stat(shallow, &st)) {
|
||||
if (mtime.sec)
|
||||
die("shallow file was removed during fetch");
|
||||
} else if (st.st_mtime != mtime.sec
|
||||
#ifdef USE_NSEC
|
||||
|| st.st_mtim.usec != mtime.usec
|
||||
|| ST_MTIME_NSEC(st) != mtime.nsec
|
||||
#endif
|
||||
)
|
||||
die("shallow file was changed during fetch");
|
||||
|
||||
@@ -636,6 +636,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
else
|
||||
remote = remote_get(argv[0]);
|
||||
|
||||
if (!remote)
|
||||
die("Where do you want to fetch from today?");
|
||||
|
||||
transport = transport_get(remote, remote->url[0]);
|
||||
if (verbosity >= 2)
|
||||
transport->verbose = 1;
|
||||
@@ -648,9 +651,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
if (depth)
|
||||
set_option(TRANS_OPT_DEPTH, depth);
|
||||
|
||||
if (!transport->url)
|
||||
die("Where do you want to fetch from today?");
|
||||
|
||||
if (argc > 1) {
|
||||
int j = 0;
|
||||
refs = xcalloc(argc + 1, sizeof(const char *));
|
||||
|
||||
@@ -22,6 +22,28 @@
|
||||
|
||||
static int builtin_grep;
|
||||
|
||||
static int grep_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
struct grep_opt *opt = cb;
|
||||
|
||||
if (!strcmp(var, "grep.color") || !strcmp(var, "color.grep")) {
|
||||
opt->color = git_config_colorbool(var, value, -1);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "grep.color.external") ||
|
||||
!strcmp(var, "color.grep.external")) {
|
||||
return git_config_string(&(opt->color_external), var, value);
|
||||
}
|
||||
if (!strcmp(var, "grep.color.match") ||
|
||||
!strcmp(var, "color.grep.match")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
color_parse(value, var, opt->color_match);
|
||||
return 0;
|
||||
}
|
||||
return git_color_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
/*
|
||||
* git grep pathspecs are somewhat different from diff-tree pathspecs;
|
||||
* pathname wildcards are allowed.
|
||||
@@ -269,6 +291,21 @@ static int flush_grep(struct grep_opt *opt,
|
||||
return status;
|
||||
}
|
||||
|
||||
static void grep_add_color(struct strbuf *sb, const char *escape_seq)
|
||||
{
|
||||
size_t orig_len = sb->len;
|
||||
|
||||
while (*escape_seq) {
|
||||
if (*escape_seq == 'm')
|
||||
strbuf_addch(sb, ';');
|
||||
else if (*escape_seq != '\033' && *escape_seq != '[')
|
||||
strbuf_addch(sb, *escape_seq);
|
||||
escape_seq++;
|
||||
}
|
||||
if (sb->len > orig_len && sb->buf[sb->len - 1] == ';')
|
||||
strbuf_setlen(sb, sb->len - 1);
|
||||
}
|
||||
|
||||
static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
{
|
||||
int i, nr, argc, hit, len, status;
|
||||
@@ -339,6 +376,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
push_arg("-e");
|
||||
push_arg(p->pattern);
|
||||
}
|
||||
if (opt->color) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
grep_add_color(&sb, opt->color_match);
|
||||
setenv("GREP_COLOR", sb.buf, 1);
|
||||
|
||||
strbuf_reset(&sb);
|
||||
strbuf_addstr(&sb, "mt=");
|
||||
grep_add_color(&sb, opt->color_match);
|
||||
strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se=");
|
||||
setenv("GREP_COLORS", sb.buf, 1);
|
||||
|
||||
strbuf_release(&sb);
|
||||
|
||||
if (opt->color_external && strlen(opt->color_external) > 0)
|
||||
push_arg(opt->color_external);
|
||||
}
|
||||
|
||||
hit = 0;
|
||||
argc = nr;
|
||||
@@ -536,6 +590,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
opt.pattern_tail = &opt.pattern_list;
|
||||
opt.regflags = REG_NEWLINE;
|
||||
|
||||
strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
|
||||
opt.color = -1;
|
||||
git_config(grep_config, &opt);
|
||||
if (opt.color == -1)
|
||||
opt.color = git_use_color_default;
|
||||
|
||||
/*
|
||||
* If there is no -- then the paths must exist in the working
|
||||
* tree. If there is no explicit pattern specified with -e or
|
||||
@@ -732,6 +792,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
opt.relative = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--color", arg)) {
|
||||
opt.color = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--no-color", arg)) {
|
||||
opt.color = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("--", arg)) {
|
||||
/* later processing wants to have this at argv[1] */
|
||||
argv--;
|
||||
@@ -757,6 +825,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
}
|
||||
|
||||
if (opt.color && !opt.color_external)
|
||||
builtin_grep = 1;
|
||||
if (!opt.pattern_list)
|
||||
die("no pattern given.");
|
||||
if ((opt.regflags != REG_NEWLINE) && opt.fixed)
|
||||
|
||||
@@ -419,6 +419,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
|
||||
show_deleted = 1;
|
||||
require_work_tree = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
|
||||
|
||||
@@ -53,8 +53,11 @@ static int do_push(const char *repo, int flags)
|
||||
int i, errs;
|
||||
struct remote *remote = remote_get(repo);
|
||||
|
||||
if (!remote)
|
||||
die("bad repository '%s'", repo);
|
||||
if (!remote) {
|
||||
if (repo)
|
||||
die("bad repository '%s'", repo);
|
||||
die("No destination configured to push to.");
|
||||
}
|
||||
|
||||
if (remote->mirror)
|
||||
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
|
||||
|
||||
562
builtin-remote.c
562
builtin-remote.c
@@ -12,12 +12,17 @@ static const char * const builtin_remote_usage[] = {
|
||||
"git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
|
||||
"git remote rename <old> <new>",
|
||||
"git remote rm <name>",
|
||||
"git remote set-head <name> [-a | -d | <branch>]",
|
||||
"git remote show [-n] <name>",
|
||||
"git remote prune [-n | --dry-run] <name>",
|
||||
"git remote [-v | --verbose] update [group]",
|
||||
NULL
|
||||
};
|
||||
|
||||
#define GET_REF_STATES (1<<0)
|
||||
#define GET_HEAD_NAMES (1<<1)
|
||||
#define GET_PUSH_REF_STATES (1<<2)
|
||||
|
||||
static int verbose;
|
||||
|
||||
static int show_all(void);
|
||||
@@ -143,8 +148,9 @@ static int add(int argc, const char **argv)
|
||||
}
|
||||
|
||||
struct branch_info {
|
||||
char *remote;
|
||||
char *remote_name;
|
||||
struct string_list merge;
|
||||
int rebase;
|
||||
};
|
||||
|
||||
static struct string_list branch_list;
|
||||
@@ -161,10 +167,11 @@ static const char *abbrev_ref(const char *name, const char *prefix)
|
||||
static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
{
|
||||
if (!prefixcmp(key, "branch.")) {
|
||||
const char *orig_key = key;
|
||||
char *name;
|
||||
struct string_list_item *item;
|
||||
struct branch_info *info;
|
||||
enum { REMOTE, MERGE } type;
|
||||
enum { REMOTE, MERGE, REBASE } type;
|
||||
|
||||
key += 7;
|
||||
if (!postfixcmp(key, ".remote")) {
|
||||
@@ -173,6 +180,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
} else if (!postfixcmp(key, ".merge")) {
|
||||
name = xstrndup(key, strlen(key) - 6);
|
||||
type = MERGE;
|
||||
} else if (!postfixcmp(key, ".rebase")) {
|
||||
name = xstrndup(key, strlen(key) - 7);
|
||||
type = REBASE;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
@@ -182,10 +192,10 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
item->util = xcalloc(sizeof(struct branch_info), 1);
|
||||
info = item->util;
|
||||
if (type == REMOTE) {
|
||||
if (info->remote)
|
||||
warning("more than one branch.%s", key);
|
||||
info->remote = xstrdup(value);
|
||||
} else {
|
||||
if (info->remote_name)
|
||||
warning("more than one %s", orig_key);
|
||||
info->remote_name = xstrdup(value);
|
||||
} else if (type == MERGE) {
|
||||
char *space = strchr(value, ' ');
|
||||
value = abbrev_branch(value);
|
||||
while (space) {
|
||||
@@ -196,7 +206,8 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
space = strchr(value, ' ');
|
||||
}
|
||||
string_list_append(xstrdup(value), &info->merge);
|
||||
}
|
||||
} else
|
||||
info->rebase = git_config_bool(orig_key, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -206,12 +217,12 @@ static void read_branches(void)
|
||||
if (branch_list.nr)
|
||||
return;
|
||||
git_config(config_read_branches, NULL);
|
||||
sort_string_list(&branch_list);
|
||||
}
|
||||
|
||||
struct ref_states {
|
||||
struct remote *remote;
|
||||
struct string_list new, stale, tracked;
|
||||
struct string_list new, stale, tracked, heads, push;
|
||||
int queried;
|
||||
};
|
||||
|
||||
static int handle_one_branch(const char *refname,
|
||||
@@ -227,10 +238,8 @@ static int handle_one_branch(const char *refname,
|
||||
const char *name = abbrev_branch(refspec.src);
|
||||
/* symbolic refs pointing nowhere were handled already */
|
||||
if ((flags & REF_ISSYMREF) ||
|
||||
unsorted_string_list_has_string(&states->tracked,
|
||||
name) ||
|
||||
unsorted_string_list_has_string(&states->new,
|
||||
name))
|
||||
string_list_has_string(&states->tracked, name) ||
|
||||
string_list_has_string(&states->new, name))
|
||||
return 0;
|
||||
item = string_list_append(name, &states->stale);
|
||||
item->util = xstrdup(refname);
|
||||
@@ -238,39 +247,162 @@ static int handle_one_branch(const char *refname,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ref_states(const struct ref *ref, struct ref_states *states)
|
||||
static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
|
||||
{
|
||||
struct ref *fetch_map = NULL, **tail = &fetch_map;
|
||||
struct ref *ref;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < states->remote->fetch_refspec_nr; i++)
|
||||
if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
|
||||
if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
|
||||
die("Could not get fetch map for refspec %s",
|
||||
states->remote->fetch_refspec[i]);
|
||||
|
||||
states->new.strdup_strings = states->tracked.strdup_strings = 1;
|
||||
for (ref = fetch_map; ref; ref = ref->next) {
|
||||
struct string_list *target = &states->tracked;
|
||||
unsigned char sha1[20];
|
||||
void *util = NULL;
|
||||
|
||||
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
|
||||
target = &states->new;
|
||||
else {
|
||||
target = &states->tracked;
|
||||
if (hashcmp(sha1, ref->new_sha1))
|
||||
util = &states;
|
||||
}
|
||||
string_list_append(abbrev_branch(ref->name), target)->util = util;
|
||||
string_list_append(abbrev_branch(ref->name), &states->new);
|
||||
else
|
||||
string_list_append(abbrev_branch(ref->name), &states->tracked);
|
||||
}
|
||||
free_refs(fetch_map);
|
||||
|
||||
sort_string_list(&states->new);
|
||||
sort_string_list(&states->tracked);
|
||||
for_each_ref(handle_one_branch, states);
|
||||
sort_string_list(&states->stale);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct push_info {
|
||||
char *dest;
|
||||
int forced;
|
||||
enum {
|
||||
PUSH_STATUS_CREATE = 0,
|
||||
PUSH_STATUS_DELETE,
|
||||
PUSH_STATUS_UPTODATE,
|
||||
PUSH_STATUS_FASTFORWARD,
|
||||
PUSH_STATUS_OUTOFDATE,
|
||||
PUSH_STATUS_NOTQUERIED,
|
||||
} status;
|
||||
};
|
||||
|
||||
static int get_push_ref_states(const struct ref *remote_refs,
|
||||
struct ref_states *states)
|
||||
{
|
||||
struct remote *remote = states->remote;
|
||||
struct ref *ref, *local_refs, *push_map, **push_tail;
|
||||
if (remote->mirror)
|
||||
return 0;
|
||||
|
||||
local_refs = get_local_heads();
|
||||
ref = push_map = copy_ref_list(remote_refs);
|
||||
while (ref->next)
|
||||
ref = ref->next;
|
||||
push_tail = &ref->next;
|
||||
|
||||
match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr,
|
||||
remote->push_refspec, MATCH_REFS_NONE);
|
||||
|
||||
states->push.strdup_strings = 1;
|
||||
for (ref = push_map; ref; ref = ref->next) {
|
||||
struct string_list_item *item;
|
||||
struct push_info *info;
|
||||
|
||||
if (!ref->peer_ref)
|
||||
continue;
|
||||
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
|
||||
|
||||
item = string_list_append(abbrev_branch(ref->peer_ref->name),
|
||||
&states->push);
|
||||
item->util = xcalloc(sizeof(struct push_info), 1);
|
||||
info = item->util;
|
||||
info->forced = ref->force;
|
||||
info->dest = xstrdup(abbrev_branch(ref->name));
|
||||
|
||||
if (is_null_sha1(ref->new_sha1)) {
|
||||
info->status = PUSH_STATUS_DELETE;
|
||||
} else if (!hashcmp(ref->old_sha1, ref->new_sha1))
|
||||
info->status = PUSH_STATUS_UPTODATE;
|
||||
else if (is_null_sha1(ref->old_sha1))
|
||||
info->status = PUSH_STATUS_CREATE;
|
||||
else if (has_sha1_file(ref->old_sha1) &&
|
||||
ref_newer(ref->new_sha1, ref->old_sha1))
|
||||
info->status = PUSH_STATUS_FASTFORWARD;
|
||||
else
|
||||
info->status = PUSH_STATUS_OUTOFDATE;
|
||||
}
|
||||
free_refs(local_refs);
|
||||
free_refs(push_map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_push_ref_states_noquery(struct ref_states *states)
|
||||
{
|
||||
int i;
|
||||
struct remote *remote = states->remote;
|
||||
struct string_list_item *item;
|
||||
struct push_info *info;
|
||||
|
||||
if (remote->mirror)
|
||||
return 0;
|
||||
|
||||
states->push.strdup_strings = 1;
|
||||
if (!remote->push_refspec_nr) {
|
||||
item = string_list_append("(matching)", &states->push);
|
||||
info = item->util = xcalloc(sizeof(struct push_info), 1);
|
||||
info->status = PUSH_STATUS_NOTQUERIED;
|
||||
info->dest = xstrdup(item->string);
|
||||
}
|
||||
for (i = 0; i < remote->push_refspec_nr; i++) {
|
||||
struct refspec *spec = remote->push + i;
|
||||
char buf[PATH_MAX];
|
||||
if (spec->matching)
|
||||
item = string_list_append("(matching)", &states->push);
|
||||
else if (spec->pattern) {
|
||||
snprintf(buf, (sizeof(buf)), "%s*", spec->src);
|
||||
item = string_list_append(buf, &states->push);
|
||||
snprintf(buf, (sizeof(buf)), "%s*", spec->dst);
|
||||
} else if (strlen(spec->src))
|
||||
item = string_list_append(spec->src, &states->push);
|
||||
else
|
||||
item = string_list_append("(delete)", &states->push);
|
||||
|
||||
info = item->util = xcalloc(sizeof(struct push_info), 1);
|
||||
info->forced = spec->force;
|
||||
info->status = PUSH_STATUS_NOTQUERIED;
|
||||
if (spec->pattern)
|
||||
info->dest = xstrdup(buf);
|
||||
else
|
||||
info->dest = xstrdup(spec->dst ? spec->dst : item->string);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
|
||||
{
|
||||
struct ref *ref, *matches;
|
||||
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
|
||||
struct refspec refspec;
|
||||
|
||||
refspec.force = 0;
|
||||
refspec.pattern = 1;
|
||||
refspec.src = refspec.dst = "refs/heads/";
|
||||
states->heads.strdup_strings = 1;
|
||||
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
|
||||
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
|
||||
fetch_map, 1);
|
||||
for(ref = matches; ref; ref = ref->next)
|
||||
string_list_append(abbrev_branch(ref->name), &states->heads);
|
||||
|
||||
free_refs(fetch_map);
|
||||
free_refs(matches);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct known_remote {
|
||||
struct known_remote *next;
|
||||
struct remote *remote;
|
||||
@@ -466,7 +598,7 @@ static int mv(int argc, const char **argv)
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct string_list_item *item = branch_list.items + i;
|
||||
struct branch_info *info = item->util;
|
||||
if (info->remote && !strcmp(info->remote, rename.old)) {
|
||||
if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "branch.%s.remote", item->string);
|
||||
if (git_config_set(buf.buf, rename.new)) {
|
||||
@@ -575,7 +707,7 @@ static int rm(int argc, const char **argv)
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct string_list_item *item = branch_list.items + i;
|
||||
struct branch_info *info = item->util;
|
||||
if (info->remote && !strcmp(info->remote, remote->name)) {
|
||||
if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
|
||||
const char *keys[] = { "remote", "merge", NULL }, **k;
|
||||
for (k = keys; *k; k++) {
|
||||
strbuf_reset(&buf);
|
||||
@@ -617,18 +749,37 @@ static int rm(int argc, const char **argv)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void show_list(const char *title, struct string_list *list,
|
||||
const char *extra_arg)
|
||||
void clear_push_info(void *util, const char *string)
|
||||
{
|
||||
int i;
|
||||
struct push_info *info = util;
|
||||
free(info->dest);
|
||||
free(info);
|
||||
}
|
||||
|
||||
if (!list->nr)
|
||||
return;
|
||||
static void free_remote_ref_states(struct ref_states *states)
|
||||
{
|
||||
string_list_clear(&states->new, 0);
|
||||
string_list_clear(&states->stale, 0);
|
||||
string_list_clear(&states->tracked, 0);
|
||||
string_list_clear(&states->heads, 0);
|
||||
string_list_clear_func(&states->push, clear_push_info);
|
||||
}
|
||||
|
||||
printf(title, list->nr > 1 ? "es" : "", extra_arg);
|
||||
printf("\n");
|
||||
for (i = 0; i < list->nr; i++)
|
||||
printf(" %s\n", list->items[i].string);
|
||||
static int append_ref_to_tracked_list(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct ref_states *states = cb_data;
|
||||
struct refspec refspec;
|
||||
|
||||
if (flags & REF_ISSYMREF)
|
||||
return 0;
|
||||
|
||||
memset(&refspec, 0, sizeof(refspec));
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec))
|
||||
string_list_append(abbrev_branch(refspec.src), &states->tracked);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_remote_ref_states(const char *name,
|
||||
@@ -636,7 +787,7 @@ static int get_remote_ref_states(const char *name,
|
||||
int query)
|
||||
{
|
||||
struct transport *transport;
|
||||
const struct ref *ref;
|
||||
const struct ref *remote_refs;
|
||||
|
||||
states->remote = remote_get(name);
|
||||
if (!states->remote)
|
||||
@@ -647,105 +798,321 @@ static int get_remote_ref_states(const char *name,
|
||||
if (query) {
|
||||
transport = transport_get(NULL, states->remote->url_nr > 0 ?
|
||||
states->remote->url[0] : NULL);
|
||||
ref = transport_get_remote_refs(transport);
|
||||
remote_refs = transport_get_remote_refs(transport);
|
||||
transport_disconnect(transport);
|
||||
|
||||
get_ref_states(ref, states);
|
||||
states->queried = 1;
|
||||
if (query & GET_REF_STATES)
|
||||
get_ref_states(remote_refs, states);
|
||||
if (query & GET_HEAD_NAMES)
|
||||
get_head_names(remote_refs, states);
|
||||
if (query & GET_PUSH_REF_STATES)
|
||||
get_push_ref_states(remote_refs, states);
|
||||
} else {
|
||||
for_each_ref(append_ref_to_tracked_list, states);
|
||||
sort_string_list(&states->tracked);
|
||||
get_push_ref_states_noquery(states);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_ref_to_tracked_list(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
struct show_info {
|
||||
struct string_list *list;
|
||||
struct ref_states *states;
|
||||
int width, width2;
|
||||
int any_rebase;
|
||||
};
|
||||
|
||||
int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct ref_states *states = cb_data;
|
||||
struct refspec refspec;
|
||||
struct show_info *info = cb_data;
|
||||
int n = strlen(item->string);
|
||||
if (n > info->width)
|
||||
info->width = n;
|
||||
string_list_insert(item->string, info->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&refspec, 0, sizeof(refspec));
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec))
|
||||
string_list_append(abbrev_branch(refspec.src), &states->tracked);
|
||||
int show_remote_info_item(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct show_info *info = cb_data;
|
||||
struct ref_states *states = info->states;
|
||||
const char *name = item->string;
|
||||
|
||||
if (states->queried) {
|
||||
const char *fmt = "%s";
|
||||
const char *arg = "";
|
||||
if (string_list_has_string(&states->new, name)) {
|
||||
fmt = " new (next fetch will store in remotes/%s)";
|
||||
arg = states->remote->name;
|
||||
} else if (string_list_has_string(&states->tracked, name))
|
||||
arg = " tracked";
|
||||
else if (string_list_has_string(&states->stale, name))
|
||||
arg = " stale (use 'git remote prune' to remove)";
|
||||
else
|
||||
arg = " ???";
|
||||
printf(" %-*s", info->width, name);
|
||||
printf(fmt, arg);
|
||||
printf("\n");
|
||||
} else
|
||||
printf(" %s\n", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct ref_states *states = show_info->states;
|
||||
struct branch_info *branch_info = branch_item->util;
|
||||
struct string_list_item *item;
|
||||
int n;
|
||||
|
||||
if (!branch_info->merge.nr || !branch_info->remote_name ||
|
||||
strcmp(states->remote->name, branch_info->remote_name))
|
||||
return 0;
|
||||
if ((n = strlen(branch_item->string)) > show_info->width)
|
||||
show_info->width = n;
|
||||
if (branch_info->rebase)
|
||||
show_info->any_rebase = 1;
|
||||
|
||||
item = string_list_insert(branch_item->string, show_info->list);
|
||||
item->util = branch_info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int show_local_info_item(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct branch_info *branch_info = item->util;
|
||||
struct string_list *merge = &branch_info->merge;
|
||||
const char *also;
|
||||
int i;
|
||||
|
||||
if (branch_info->rebase && branch_info->merge.nr > 1) {
|
||||
error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
|
||||
item->string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf(" %-*s ", show_info->width, item->string);
|
||||
if (branch_info->rebase) {
|
||||
printf("rebases onto remote %s\n", merge->items[0].string);
|
||||
return 0;
|
||||
} else if (show_info->any_rebase) {
|
||||
printf(" merges with remote %s\n", merge->items[0].string);
|
||||
also = " and with remote";
|
||||
} else {
|
||||
printf("merges with remote %s\n", merge->items[0].string);
|
||||
also = " and with remote";
|
||||
}
|
||||
for (i = 1; i < merge->nr; i++)
|
||||
printf(" %-*s %s %s\n", show_info->width, "", also,
|
||||
merge->items[i].string);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct push_info *push_info = push_item->util;
|
||||
struct string_list_item *item;
|
||||
int n;
|
||||
if ((n = strlen(push_item->string)) > show_info->width)
|
||||
show_info->width = n;
|
||||
if ((n = strlen(push_info->dest)) > show_info->width2)
|
||||
show_info->width2 = n;
|
||||
item = string_list_append(push_item->string, show_info->list);
|
||||
item->util = push_item->util;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int show_push_info_item(struct string_list_item *item, void *cb_data)
|
||||
{
|
||||
struct show_info *show_info = cb_data;
|
||||
struct push_info *push_info = item->util;
|
||||
char *src = item->string, *status = NULL;
|
||||
|
||||
switch (push_info->status) {
|
||||
case PUSH_STATUS_CREATE:
|
||||
status = "create";
|
||||
break;
|
||||
case PUSH_STATUS_DELETE:
|
||||
status = "delete";
|
||||
src = "(none)";
|
||||
break;
|
||||
case PUSH_STATUS_UPTODATE:
|
||||
status = "up to date";
|
||||
break;
|
||||
case PUSH_STATUS_FASTFORWARD:
|
||||
status = "fast forwardable";
|
||||
break;
|
||||
case PUSH_STATUS_OUTOFDATE:
|
||||
status = "local out of date";
|
||||
break;
|
||||
case PUSH_STATUS_NOTQUERIED:
|
||||
break;
|
||||
}
|
||||
if (status)
|
||||
printf(" %-*s %s to %-*s (%s)\n", show_info->width, src,
|
||||
push_info->forced ? "forces" : "pushes",
|
||||
show_info->width2, push_info->dest, status);
|
||||
else
|
||||
printf(" %-*s %s to %s\n", show_info->width, src,
|
||||
push_info->forced ? "forces" : "pushes",
|
||||
push_info->dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show(int argc, const char **argv)
|
||||
{
|
||||
int no_query = 0, result = 0;
|
||||
int no_query = 0, result = 0, query_flag = 0;
|
||||
struct option options[] = {
|
||||
OPT_GROUP("show specific options"),
|
||||
OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
|
||||
OPT_END()
|
||||
};
|
||||
struct ref_states states;
|
||||
struct string_list info_list = { NULL, 0, 0, 0 };
|
||||
struct show_info info;
|
||||
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
|
||||
if (argc < 1)
|
||||
return show_all();
|
||||
|
||||
if (!no_query)
|
||||
query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
|
||||
|
||||
memset(&states, 0, sizeof(states));
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.states = &states;
|
||||
info.list = &info_list;
|
||||
for (; argc; argc--, argv++) {
|
||||
int i;
|
||||
|
||||
get_remote_ref_states(*argv, &states, !no_query);
|
||||
get_remote_ref_states(*argv, &states, query_flag);
|
||||
|
||||
printf("* remote %s\n URL: %s\n", *argv,
|
||||
states.remote->url_nr > 0 ?
|
||||
states.remote->url[0] : "(no URL)");
|
||||
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct string_list_item *branch = branch_list.items + i;
|
||||
struct branch_info *info = branch->util;
|
||||
int j;
|
||||
|
||||
if (!info->merge.nr || strcmp(*argv, info->remote))
|
||||
continue;
|
||||
printf(" Remote branch%s merged with 'git pull' "
|
||||
"while on branch %s\n ",
|
||||
info->merge.nr > 1 ? "es" : "",
|
||||
branch->string);
|
||||
for (j = 0; j < info->merge.nr; j++)
|
||||
printf(" %s", info->merge.items[j].string);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (!no_query) {
|
||||
show_list(" New remote branch%s (next fetch "
|
||||
"will store in remotes/%s)",
|
||||
&states.new, states.remote->name);
|
||||
show_list(" Stale tracking branch%s (use 'git remote "
|
||||
"prune')", &states.stale, "");
|
||||
}
|
||||
|
||||
if (no_query)
|
||||
for_each_ref(append_ref_to_tracked_list, &states);
|
||||
show_list(" Tracked remote branch%s", &states.tracked, "");
|
||||
|
||||
if (states.remote->push_refspec_nr) {
|
||||
printf(" Local branch%s pushed with 'git push'\n",
|
||||
states.remote->push_refspec_nr > 1 ?
|
||||
"es" : "");
|
||||
for (i = 0; i < states.remote->push_refspec_nr; i++) {
|
||||
struct refspec *spec = states.remote->push + i;
|
||||
printf(" %s%s%s%s\n",
|
||||
spec->force ? "+" : "",
|
||||
abbrev_branch(spec->src),
|
||||
spec->dst ? ":" : "",
|
||||
spec->dst ? abbrev_branch(spec->dst) : "");
|
||||
}
|
||||
printf(" HEAD branch: (not queried)\n");
|
||||
else if (!states.heads.nr)
|
||||
printf(" HEAD branch: (unknown)\n");
|
||||
else if (states.heads.nr == 1)
|
||||
printf(" HEAD branch: %s\n", states.heads.items[0].string);
|
||||
else {
|
||||
printf(" HEAD branch (remote HEAD is ambiguous,"
|
||||
" may be one of the following):\n");
|
||||
for (i = 0; i < states.heads.nr; i++)
|
||||
printf(" %s\n", states.heads.items[i].string);
|
||||
}
|
||||
|
||||
/* NEEDSWORK: free remote */
|
||||
string_list_clear(&states.new, 0);
|
||||
string_list_clear(&states.stale, 0);
|
||||
string_list_clear(&states.tracked, 0);
|
||||
/* remote branch info */
|
||||
info.width = 0;
|
||||
for_each_string_list(add_remote_to_show_info, &states.new, &info);
|
||||
for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
|
||||
for_each_string_list(add_remote_to_show_info, &states.stale, &info);
|
||||
if (info.list->nr)
|
||||
printf(" Remote branch%s:%s\n",
|
||||
info.list->nr > 1 ? "es" : "",
|
||||
no_query ? " (status not queried)" : "");
|
||||
for_each_string_list(show_remote_info_item, info.list, &info);
|
||||
string_list_clear(info.list, 0);
|
||||
|
||||
/* git pull info */
|
||||
info.width = 0;
|
||||
info.any_rebase = 0;
|
||||
for_each_string_list(add_local_to_show_info, &branch_list, &info);
|
||||
if (info.list->nr)
|
||||
printf(" Local branch%s configured for 'git pull':\n",
|
||||
info.list->nr > 1 ? "es" : "");
|
||||
for_each_string_list(show_local_info_item, info.list, &info);
|
||||
string_list_clear(info.list, 0);
|
||||
|
||||
/* git push info */
|
||||
if (states.remote->mirror)
|
||||
printf(" Local refs will be mirrored by 'git push'\n");
|
||||
|
||||
info.width = info.width2 = 0;
|
||||
for_each_string_list(add_push_to_show_info, &states.push, &info);
|
||||
sort_string_list(info.list);
|
||||
if (info.list->nr)
|
||||
printf(" Local ref%s configured for 'git push'%s:\n",
|
||||
info.list->nr > 1 ? "s" : "",
|
||||
no_query ? " (status not queried)" : "");
|
||||
for_each_string_list(show_push_info_item, info.list, &info);
|
||||
string_list_clear(info.list, 0);
|
||||
|
||||
free_remote_ref_states(&states);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int set_head(int argc, const char **argv)
|
||||
{
|
||||
int i, opt_a = 0, opt_d = 0, result = 0;
|
||||
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
|
||||
char *head_name = NULL;
|
||||
|
||||
struct option options[] = {
|
||||
OPT_GROUP("set-head specific options"),
|
||||
OPT_BOOLEAN('a', "auto", &opt_a,
|
||||
"set refs/remotes/<name>/HEAD according to remote"),
|
||||
OPT_BOOLEAN('d', "delete", &opt_d,
|
||||
"delete refs/remotes/<name>/HEAD"),
|
||||
OPT_END()
|
||||
};
|
||||
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
|
||||
if (argc)
|
||||
strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
|
||||
|
||||
if (!opt_a && !opt_d && argc == 2) {
|
||||
head_name = xstrdup(argv[1]);
|
||||
} else if (opt_a && !opt_d && argc == 1) {
|
||||
struct ref_states states;
|
||||
memset(&states, 0, sizeof(states));
|
||||
get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
|
||||
if (!states.heads.nr)
|
||||
result |= error("Cannot determine remote HEAD");
|
||||
else if (states.heads.nr > 1) {
|
||||
result |= error("Multiple remote HEAD branches. "
|
||||
"Please choose one explicitly with:");
|
||||
for (i = 0; i < states.heads.nr; i++)
|
||||
fprintf(stderr, " git remote set-head %s %s\n",
|
||||
argv[0], states.heads.items[i].string);
|
||||
} else
|
||||
head_name = xstrdup(states.heads.items[0].string);
|
||||
free_remote_ref_states(&states);
|
||||
} else if (opt_d && !opt_a && argc == 1) {
|
||||
if (delete_ref(buf.buf, NULL, REF_NODEREF))
|
||||
result |= error("Could not delete %s", buf.buf);
|
||||
} else
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
if (head_name) {
|
||||
unsigned char sha1[20];
|
||||
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
|
||||
/* make sure it's valid */
|
||||
if (!resolve_ref(buf2.buf, sha1, 1, NULL))
|
||||
result |= error("Not a valid ref: %s", buf2.buf);
|
||||
else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
|
||||
result |= error("Could not setup %s", buf.buf);
|
||||
if (opt_a)
|
||||
printf("%s/HEAD set to %s\n", argv[0], head_name);
|
||||
free(head_name);
|
||||
}
|
||||
|
||||
strbuf_release(&buf);
|
||||
strbuf_release(&buf2);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int prune(int argc, const char **argv)
|
||||
{
|
||||
int dry_run = 0, result = 0;
|
||||
@@ -770,7 +1137,7 @@ static int prune(int argc, const char **argv)
|
||||
for (; argc; argc--, argv++) {
|
||||
int i;
|
||||
|
||||
get_remote_ref_states(*argv, &states, 1);
|
||||
get_remote_ref_states(*argv, &states, GET_REF_STATES);
|
||||
|
||||
if (states.stale.nr) {
|
||||
printf("Pruning %s\n", *argv);
|
||||
@@ -791,10 +1158,7 @@ static int prune(int argc, const char **argv)
|
||||
warn_dangling_symref(dangling_msg, refname);
|
||||
}
|
||||
|
||||
/* NEEDSWORK: free remote */
|
||||
string_list_clear(&states.new, 0);
|
||||
string_list_clear(&states.stale, 0);
|
||||
string_list_clear(&states.tracked, 0);
|
||||
free_remote_ref_states(&states);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -919,6 +1283,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
|
||||
result = mv(argc, argv);
|
||||
else if (!strcmp(argv[0], "rm"))
|
||||
result = rm(argc, argv);
|
||||
else if (!strcmp(argv[0], "set-head"))
|
||||
result = set_head(argc, argv);
|
||||
else if (!strcmp(argv[0], "show"))
|
||||
result = show(argc, argv);
|
||||
else if (!strcmp(argv[0], "prune"))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "tag.h"
|
||||
#include "refs.h"
|
||||
#include "pkt-line.h"
|
||||
#include "run-command.h"
|
||||
@@ -84,82 +83,8 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unmark_and_free(struct commit_list *list, unsigned int mark)
|
||||
{
|
||||
while (list) {
|
||||
struct commit_list *temp = list;
|
||||
temp->item->object.flags &= ~mark;
|
||||
list = temp->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
static int ref_newer(const unsigned char *new_sha1,
|
||||
const unsigned char *old_sha1)
|
||||
{
|
||||
struct object *o;
|
||||
struct commit *old, *new;
|
||||
struct commit_list *list, *used;
|
||||
int found = 0;
|
||||
|
||||
/* Both new and old must be commit-ish and new is descendant of
|
||||
* old. Otherwise we require --force.
|
||||
*/
|
||||
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
old = (struct commit *) o;
|
||||
|
||||
o = deref_tag(parse_object(new_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
new = (struct commit *) o;
|
||||
|
||||
if (parse_commit(new) < 0)
|
||||
return 0;
|
||||
|
||||
used = list = NULL;
|
||||
commit_list_insert(new, &list);
|
||||
while (list) {
|
||||
new = pop_most_recent_commit(&list, 1);
|
||||
commit_list_insert(new, &used);
|
||||
if (new == old) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmark_and_free(list, 1);
|
||||
unmark_and_free(used, 1);
|
||||
return found;
|
||||
}
|
||||
|
||||
static struct ref *local_refs, **local_tail;
|
||||
static struct ref *remote_refs, **remote_tail;
|
||||
|
||||
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct ref *ref;
|
||||
int len;
|
||||
|
||||
/* we already know it starts with refs/ to get here */
|
||||
if (check_ref_format(refname + 5))
|
||||
return 0;
|
||||
|
||||
len = strlen(refname) + 1;
|
||||
ref = xcalloc(1, sizeof(*ref) + len);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
memcpy(ref->name, refname, len);
|
||||
*local_tail = ref;
|
||||
local_tail = &ref->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_local_heads(void)
|
||||
{
|
||||
local_tail = &local_refs;
|
||||
for_each_ref(one_local_ref, NULL);
|
||||
}
|
||||
|
||||
static int receive_status(int in, struct ref *refs)
|
||||
{
|
||||
struct ref *hint;
|
||||
@@ -387,7 +312,7 @@ static int refs_pushed(struct ref *ref)
|
||||
|
||||
static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
|
||||
{
|
||||
struct ref *ref;
|
||||
struct ref *ref, *local_refs;
|
||||
int new_refs;
|
||||
int ask_for_status_report = 0;
|
||||
int allow_deleting_refs = 0;
|
||||
@@ -405,7 +330,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
|
||||
/* No funny business with the matcher */
|
||||
remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL,
|
||||
&extra_have);
|
||||
get_local_heads();
|
||||
local_refs = get_local_heads();
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
if (server_supports("report-status"))
|
||||
|
||||
@@ -195,7 +195,7 @@ static int process_path(const char *path)
|
||||
struct stat st;
|
||||
|
||||
len = strlen(path);
|
||||
if (has_symlink_leading_path(len, path))
|
||||
if (has_symlink_leading_path(path, len))
|
||||
return error("'%s' is beyond a symbolic link", path);
|
||||
|
||||
/*
|
||||
|
||||
21
cache.h
21
cache.h
@@ -140,8 +140,8 @@ struct ondisk_cache_entry_extended {
|
||||
};
|
||||
|
||||
struct cache_entry {
|
||||
unsigned int ce_ctime;
|
||||
unsigned int ce_mtime;
|
||||
struct cache_time ce_ctime;
|
||||
struct cache_time ce_mtime;
|
||||
unsigned int ce_dev;
|
||||
unsigned int ce_ino;
|
||||
unsigned int ce_mode;
|
||||
@@ -282,7 +282,7 @@ struct index_state {
|
||||
struct cache_entry **cache;
|
||||
unsigned int cache_nr, cache_alloc, cache_changed;
|
||||
struct cache_tree *cache_tree;
|
||||
time_t timestamp;
|
||||
struct cache_time timestamp;
|
||||
void *alloc;
|
||||
unsigned name_hash_initialized : 1,
|
||||
initialized : 1;
|
||||
@@ -428,7 +428,7 @@ extern int read_index_preload(struct index_state *, const char **pathspec);
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int is_index_unborn(struct index_state *);
|
||||
extern int read_index_unmerged(struct index_state *);
|
||||
extern int write_index(const struct index_state *, int newfd);
|
||||
extern int write_index(struct index_state *, int newfd);
|
||||
extern int discard_index(struct index_state *);
|
||||
extern int unmerged_index(const struct index_state *);
|
||||
extern int verify_path(const char *path);
|
||||
@@ -443,6 +443,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt
|
||||
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
|
||||
extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
|
||||
extern int remove_index_entry_at(struct index_state *, int pos);
|
||||
extern void remove_marked_cache_entries(struct index_state *istate);
|
||||
extern int remove_file_from_index(struct index_state *, const char *path);
|
||||
#define ADD_CACHE_VERBOSE 1
|
||||
#define ADD_CACHE_PRETEND 2
|
||||
@@ -725,11 +726,13 @@ struct checkout {
|
||||
};
|
||||
|
||||
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
|
||||
extern int has_symlink_leading_path(int len, const char *name);
|
||||
extern int has_symlink_or_noent_leading_path(int len, const char *name);
|
||||
extern int has_dirs_only_path(int len, const char *name, int prefix_len);
|
||||
extern void invalidate_lstat_cache(int len, const char *name);
|
||||
extern int has_symlink_leading_path(const char *name, int len);
|
||||
extern int has_symlink_or_noent_leading_path(const char *name, int len);
|
||||
extern int has_dirs_only_path(const char *name, int len, int prefix_len);
|
||||
extern void invalidate_lstat_cache(const char *name, int len);
|
||||
extern void clear_lstat_cache(void);
|
||||
extern void schedule_dir_for_removal(const char *name, int len);
|
||||
extern void remove_scheduled_dirs(void);
|
||||
|
||||
extern struct alternate_object_database {
|
||||
struct alternate_object_database *next;
|
||||
@@ -802,7 +805,7 @@ struct ref {
|
||||
#define REF_HEADS (1u << 1)
|
||||
#define REF_TAGS (1u << 2)
|
||||
|
||||
extern struct ref *find_ref_by_name(struct ref *list, const char *name);
|
||||
extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
|
||||
|
||||
#define CONNECT_VERBOSE (1u << 0)
|
||||
extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
|
||||
|
||||
@@ -712,9 +712,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
|
||||
result_size = buf.len;
|
||||
result = strbuf_detach(&buf, NULL);
|
||||
elem->mode = canon_mode(st.st_mode);
|
||||
}
|
||||
else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
|
||||
!fstat(fd, &st)) {
|
||||
} else if (0 <= (fd = open(elem->path, O_RDONLY))) {
|
||||
size_t len = xsize_t(st.st_size);
|
||||
ssize_t done;
|
||||
int is_file, i;
|
||||
|
||||
134
compat/mingw.c
134
compat/mingw.c
@@ -4,6 +4,119 @@
|
||||
|
||||
unsigned int _CRT_fmode = _O_BINARY;
|
||||
|
||||
static int err_win_to_posix(DWORD winerr)
|
||||
{
|
||||
int error = ENOSYS;
|
||||
switch(winerr) {
|
||||
case ERROR_ACCESS_DENIED: error = EACCES; break;
|
||||
case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
|
||||
case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
|
||||
case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
|
||||
case ERROR_ALREADY_EXISTS: error = EEXIST; break;
|
||||
case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
|
||||
case ERROR_BAD_COMMAND: error = EIO; break;
|
||||
case ERROR_BAD_DEVICE: error = ENODEV; break;
|
||||
case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
|
||||
case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
|
||||
case ERROR_BAD_FORMAT: error = ENOEXEC; break;
|
||||
case ERROR_BAD_LENGTH: error = EINVAL; break;
|
||||
case ERROR_BAD_PATHNAME: error = ENOENT; break;
|
||||
case ERROR_BAD_PIPE: error = EPIPE; break;
|
||||
case ERROR_BAD_UNIT: error = ENODEV; break;
|
||||
case ERROR_BAD_USERNAME: error = EINVAL; break;
|
||||
case ERROR_BROKEN_PIPE: error = EPIPE; break;
|
||||
case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
|
||||
case ERROR_BUSY: error = EBUSY; break;
|
||||
case ERROR_BUSY_DRIVE: error = EBUSY; break;
|
||||
case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
|
||||
case ERROR_CANNOT_MAKE: error = EACCES; break;
|
||||
case ERROR_CANTOPEN: error = EIO; break;
|
||||
case ERROR_CANTREAD: error = EIO; break;
|
||||
case ERROR_CANTWRITE: error = EIO; break;
|
||||
case ERROR_CRC: error = EIO; break;
|
||||
case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
|
||||
case ERROR_DEVICE_IN_USE: error = EBUSY; break;
|
||||
case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
|
||||
case ERROR_DIRECTORY: error = EINVAL; break;
|
||||
case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
|
||||
case ERROR_DISK_CHANGE: error = EIO; break;
|
||||
case ERROR_DISK_FULL: error = ENOSPC; break;
|
||||
case ERROR_DRIVE_LOCKED: error = EBUSY; break;
|
||||
case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
|
||||
case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
|
||||
case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
|
||||
case ERROR_FILE_EXISTS: error = EEXIST; break;
|
||||
case ERROR_FILE_INVALID: error = ENODEV; break;
|
||||
case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
|
||||
case ERROR_GEN_FAILURE: error = EIO; break;
|
||||
case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
|
||||
case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
|
||||
case ERROR_INVALID_ACCESS: error = EACCES; break;
|
||||
case ERROR_INVALID_ADDRESS: error = EFAULT; break;
|
||||
case ERROR_INVALID_BLOCK: error = EFAULT; break;
|
||||
case ERROR_INVALID_DATA: error = EINVAL; break;
|
||||
case ERROR_INVALID_DRIVE: error = ENODEV; break;
|
||||
case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
|
||||
case ERROR_INVALID_FLAGS: error = EINVAL; break;
|
||||
case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
|
||||
case ERROR_INVALID_HANDLE: error = EBADF; break;
|
||||
case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
|
||||
case ERROR_INVALID_NAME: error = EINVAL; break;
|
||||
case ERROR_INVALID_OWNER: error = EINVAL; break;
|
||||
case ERROR_INVALID_PARAMETER: error = EINVAL; break;
|
||||
case ERROR_INVALID_PASSWORD: error = EPERM; break;
|
||||
case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
|
||||
case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
|
||||
case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
|
||||
case ERROR_INVALID_WORKSTATION: error = EACCES; break;
|
||||
case ERROR_IO_DEVICE: error = EIO; break;
|
||||
case ERROR_IO_INCOMPLETE: error = EINTR; break;
|
||||
case ERROR_LOCKED: error = EBUSY; break;
|
||||
case ERROR_LOCK_VIOLATION: error = EACCES; break;
|
||||
case ERROR_LOGON_FAILURE: error = EACCES; break;
|
||||
case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
|
||||
case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
|
||||
case ERROR_MORE_DATA: error = EPIPE; break;
|
||||
case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
|
||||
case ERROR_NOACCESS: error = EFAULT; break;
|
||||
case ERROR_NONE_MAPPED: error = EINVAL; break;
|
||||
case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
|
||||
case ERROR_NOT_READY: error = EAGAIN; break;
|
||||
case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
|
||||
case ERROR_NO_DATA: error = EPIPE; break;
|
||||
case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
|
||||
case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
|
||||
case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
|
||||
case ERROR_OPEN_FAILED: error = EIO; break;
|
||||
case ERROR_OPEN_FILES: error = EBUSY; break;
|
||||
case ERROR_OPERATION_ABORTED: error = EINTR; break;
|
||||
case ERROR_OUTOFMEMORY: error = ENOMEM; break;
|
||||
case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
|
||||
case ERROR_PATH_BUSY: error = EBUSY; break;
|
||||
case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
|
||||
case ERROR_PIPE_BUSY: error = EBUSY; break;
|
||||
case ERROR_PIPE_CONNECTED: error = EPIPE; break;
|
||||
case ERROR_PIPE_LISTENING: error = EPIPE; break;
|
||||
case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
|
||||
case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
|
||||
case ERROR_READ_FAULT: error = EIO; break;
|
||||
case ERROR_SEEK: error = EIO; break;
|
||||
case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
|
||||
case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
|
||||
case ERROR_SHARING_VIOLATION: error = EACCES; break;
|
||||
case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
|
||||
case ERROR_SWAPERROR: error = ENOENT; break;
|
||||
case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
|
||||
case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
|
||||
case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
|
||||
case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
|
||||
case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
|
||||
case ERROR_WRITE_FAULT: error = EIO; break;
|
||||
case ERROR_WRITE_PROTECT: error = EROFS; break;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
#undef open
|
||||
int mingw_open (const char *filename, int oflags, ...)
|
||||
{
|
||||
@@ -1005,3 +1118,24 @@ void mingw_open_html(const char *unixpath)
|
||||
printf("Launching default browser to display HTML ...\n");
|
||||
ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
|
||||
}
|
||||
|
||||
int link(const char *oldpath, const char *newpath)
|
||||
{
|
||||
typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
|
||||
static T create_hard_link = NULL;
|
||||
if (!create_hard_link) {
|
||||
create_hard_link = (T) GetProcAddress(
|
||||
GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
|
||||
if (!create_hard_link)
|
||||
create_hard_link = (T)-1;
|
||||
}
|
||||
if (create_hard_link == (T)-1) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
if (!create_hard_link(newpath, oldpath, NULL)) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -67,8 +67,6 @@ static inline int readlink(const char *path, char *buf, size_t bufsiz)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int symlink(const char *oldpath, const char *newpath)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int link(const char *oldpath, const char *newpath)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int fchmod(int fildes, mode_t mode)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int fork(void)
|
||||
@@ -134,6 +132,7 @@ int getpagesize(void); /* defined in MinGW's libgcc.a */
|
||||
struct passwd *getpwuid(int uid);
|
||||
int setitimer(int type, struct itimerval *in, struct itimerval *out);
|
||||
int sigaction(int sig, struct sigaction *in, struct sigaction *out);
|
||||
int link(const char *oldpath, const char *newpath);
|
||||
|
||||
/*
|
||||
* replacements of existing functions
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
This file is git-specific. Therefore, this file does not attempt
|
||||
to implement any codes that are not used by git.
|
||||
|
||||
TODO: K
|
||||
*/
|
||||
|
||||
static HANDLE console;
|
||||
@@ -79,6 +77,20 @@ static void set_console_attr(void)
|
||||
SetConsoleTextAttribute(console, attributes);
|
||||
}
|
||||
|
||||
static void erase_in_line(void)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi;
|
||||
|
||||
if (!console)
|
||||
return;
|
||||
|
||||
GetConsoleScreenBufferInfo(console, &sbi);
|
||||
FillConsoleOutputCharacterA(console, ' ',
|
||||
sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static const char *set_attr(const char *str)
|
||||
{
|
||||
const char *func;
|
||||
@@ -218,7 +230,7 @@ static const char *set_attr(const char *str)
|
||||
set_console_attr();
|
||||
break;
|
||||
case 'K':
|
||||
/* TODO */
|
||||
erase_in_line();
|
||||
break;
|
||||
default:
|
||||
/* Unsupported code */
|
||||
|
||||
@@ -1516,7 +1516,7 @@ _git_config ()
|
||||
|
||||
_git_remote ()
|
||||
{
|
||||
local subcommands="add rename rm show prune update"
|
||||
local subcommands="add rename rm show prune update set-head"
|
||||
local subcommand="$(__git_find_subcommand "$subcommands")"
|
||||
if [ -z "$subcommand" ]; then
|
||||
__gitcomp "$subcommands"
|
||||
|
||||
@@ -31,7 +31,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
if (has_symlink_leading_path(ce_namelen(ce), ce->name))
|
||||
if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
|
||||
return 1;
|
||||
if (S_ISDIR(st->st_mode)) {
|
||||
unsigned char sub[20];
|
||||
|
||||
@@ -25,10 +25,12 @@ static unsigned int contains(struct diff_filespec *one,
|
||||
regmatch_t regmatch;
|
||||
int flags = 0;
|
||||
|
||||
assert(data[sz] == '\0');
|
||||
while (*data && !regexec(regexp, data, 1, ®match, flags)) {
|
||||
flags |= REG_NOTBOL;
|
||||
data += regmatch.rm_so;
|
||||
if (*data) data++;
|
||||
data += regmatch.rm_eo;
|
||||
if (*data && regmatch.rm_so == regmatch.rm_eo)
|
||||
data++;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
|
||||
2
dir.c
2
dir.c
@@ -720,7 +720,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
|
||||
{
|
||||
struct path_simplify *simplify;
|
||||
|
||||
if (has_symlink_leading_path(strlen(path), path))
|
||||
if (has_symlink_leading_path(path, strlen(path)))
|
||||
return dir->nr;
|
||||
|
||||
simplify = create_simplify(pathspec);
|
||||
|
||||
106
entry.c
106
entry.c
@@ -2,15 +2,19 @@
|
||||
#include "blob.h"
|
||||
#include "dir.h"
|
||||
|
||||
static void create_directories(const char *path, const struct checkout *state)
|
||||
static void create_directories(const char *path, int path_len,
|
||||
const struct checkout *state)
|
||||
{
|
||||
int len = strlen(path);
|
||||
char *buf = xmalloc(len + 1);
|
||||
const char *slash = path;
|
||||
char *buf = xmalloc(path_len + 1);
|
||||
int len = 0;
|
||||
|
||||
while ((slash = strchr(slash+1, '/')) != NULL) {
|
||||
len = slash - path;
|
||||
memcpy(buf, path, len);
|
||||
while (len < path_len) {
|
||||
do {
|
||||
buf[len] = path[len];
|
||||
len++;
|
||||
} while (len < path_len && path[len] != '/');
|
||||
if (len >= path_len)
|
||||
break;
|
||||
buf[len] = 0;
|
||||
|
||||
/*
|
||||
@@ -20,7 +24,7 @@ static void create_directories(const char *path, const struct checkout *state)
|
||||
* we test the path components of the prefix with the
|
||||
* stat() function instead of the lstat() function.
|
||||
*/
|
||||
if (has_dirs_only_path(len, buf, state->base_dir_len))
|
||||
if (has_dirs_only_path(buf, len, state->base_dir_len))
|
||||
continue; /* ok, it is already a directory. */
|
||||
|
||||
/*
|
||||
@@ -74,7 +78,7 @@ static int create_file(const char *path, unsigned int mode)
|
||||
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
|
||||
}
|
||||
|
||||
static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)
|
||||
static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
|
||||
{
|
||||
enum object_type type;
|
||||
void *new = read_sha1_file(ce->sha1, &type, size);
|
||||
@@ -89,36 +93,52 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned
|
||||
|
||||
static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
|
||||
{
|
||||
int fd;
|
||||
long wrote;
|
||||
|
||||
switch (ce->ce_mode & S_IFMT) {
|
||||
char *new;
|
||||
struct strbuf buf;
|
||||
unsigned long size;
|
||||
unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
|
||||
int fd, ret, fstat_done = 0;
|
||||
char *new;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
unsigned long size;
|
||||
size_t wrote, newsize = 0;
|
||||
struct stat st;
|
||||
|
||||
switch (ce_mode_s_ifmt) {
|
||||
case S_IFREG:
|
||||
new = read_blob_entry(ce, path, &size);
|
||||
case S_IFLNK:
|
||||
new = read_blob_entry(ce, &size);
|
||||
if (!new)
|
||||
return error("git checkout-index: unable to read sha1 file of %s (%s)",
|
||||
path, sha1_to_hex(ce->sha1));
|
||||
|
||||
if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) {
|
||||
ret = symlink(new, path);
|
||||
free(new);
|
||||
if (ret)
|
||||
return error("git checkout-index: unable to create symlink %s (%s)",
|
||||
path, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert from git internal format to working tree format
|
||||
*/
|
||||
strbuf_init(&buf, 0);
|
||||
if (convert_to_working_tree(ce->name, new, size, &buf)) {
|
||||
size_t newsize = 0;
|
||||
if (ce_mode_s_ifmt == S_IFREG &&
|
||||
convert_to_working_tree(ce->name, new, size, &buf)) {
|
||||
free(new);
|
||||
new = strbuf_detach(&buf, &newsize);
|
||||
size = newsize;
|
||||
}
|
||||
|
||||
if (to_tempfile) {
|
||||
strcpy(path, ".merge_file_XXXXXX");
|
||||
if (ce_mode_s_ifmt == S_IFREG)
|
||||
strcpy(path, ".merge_file_XXXXXX");
|
||||
else
|
||||
strcpy(path, ".merge_link_XXXXXX");
|
||||
fd = mkstemp(path);
|
||||
} else
|
||||
} else if (ce_mode_s_ifmt == S_IFREG) {
|
||||
fd = create_file(path, ce->ce_mode);
|
||||
} else {
|
||||
fd = create_file(path, 0666);
|
||||
}
|
||||
if (fd < 0) {
|
||||
free(new);
|
||||
return error("git checkout-index: unable to create file %s (%s)",
|
||||
@@ -126,41 +146,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
|
||||
}
|
||||
|
||||
wrote = write_in_full(fd, new, size);
|
||||
/* use fstat() only when path == ce->name */
|
||||
if (state->refresh_cache && !to_tempfile && !state->base_dir_len) {
|
||||
fstat(fd, &st);
|
||||
fstat_done = 1;
|
||||
}
|
||||
close(fd);
|
||||
free(new);
|
||||
if (wrote != size)
|
||||
return error("git checkout-index: unable to write file %s", path);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
new = read_blob_entry(ce, path, &size);
|
||||
if (!new)
|
||||
return error("git checkout-index: unable to read sha1 file of %s (%s)",
|
||||
path, sha1_to_hex(ce->sha1));
|
||||
if (to_tempfile || !has_symlinks) {
|
||||
if (to_tempfile) {
|
||||
strcpy(path, ".merge_link_XXXXXX");
|
||||
fd = mkstemp(path);
|
||||
} else
|
||||
fd = create_file(path, 0666);
|
||||
if (fd < 0) {
|
||||
free(new);
|
||||
return error("git checkout-index: unable to create "
|
||||
"file %s (%s)", path, strerror(errno));
|
||||
}
|
||||
wrote = write_in_full(fd, new, size);
|
||||
close(fd);
|
||||
free(new);
|
||||
if (wrote != size)
|
||||
return error("git checkout-index: unable to write file %s",
|
||||
path);
|
||||
} else {
|
||||
wrote = symlink(new, path);
|
||||
free(new);
|
||||
if (wrote)
|
||||
return error("git checkout-index: unable to create "
|
||||
"symlink %s (%s)", path, strerror(errno));
|
||||
}
|
||||
break;
|
||||
case S_IFGITLINK:
|
||||
if (to_tempfile)
|
||||
return error("git checkout-index: cannot create temporary subproject %s", path);
|
||||
@@ -172,8 +167,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
|
||||
}
|
||||
|
||||
if (state->refresh_cache) {
|
||||
struct stat st;
|
||||
lstat(ce->name, &st);
|
||||
if (!fstat_done)
|
||||
lstat(ce->name, &st);
|
||||
fill_stat_cache_info(ce, &st);
|
||||
}
|
||||
return 0;
|
||||
@@ -190,6 +185,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
|
||||
|
||||
memcpy(path, state->base_dir, len);
|
||||
strcpy(path + len, ce->name);
|
||||
len += ce_namelen(ce);
|
||||
|
||||
if (!lstat(path, &st)) {
|
||||
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
|
||||
@@ -218,6 +214,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
|
||||
return error("unable to unlink old '%s' (%s)", path, strerror(errno));
|
||||
} else if (state->not_new)
|
||||
return 0;
|
||||
create_directories(path, state);
|
||||
create_directories(path, len, state);
|
||||
return write_entry(ce, path, state, 0);
|
||||
}
|
||||
|
||||
@@ -388,4 +388,18 @@ void git_qsort(void *base, size_t nmemb, size_t size,
|
||||
# define FORCE_DIR_SET_GID 0
|
||||
#endif
|
||||
|
||||
#ifdef NO_NSEC
|
||||
#undef USE_NSEC
|
||||
#define ST_CTIME_NSEC(st) 0
|
||||
#define ST_MTIME_NSEC(st) 0
|
||||
#else
|
||||
#ifdef USE_ST_TIMESPEC
|
||||
#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
|
||||
#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
|
||||
#else
|
||||
#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
|
||||
#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -821,7 +821,7 @@ Date: $date
|
||||
Message-Id: $message_id
|
||||
X-Mailer: git-send-email $gitversion
|
||||
";
|
||||
if ($thread && $reply_to) {
|
||||
if ($reply_to) {
|
||||
|
||||
$header .= "In-Reply-To: $reply_to\n";
|
||||
$header .= "References: $references\n";
|
||||
|
||||
145
grep.c
145
grep.c
@@ -39,6 +39,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
p->word_regexp = opt->word_regexp;
|
||||
|
||||
if (opt->fixed || is_fixed(p->pattern))
|
||||
p->fixed = 1;
|
||||
if (opt->regflags & REG_ICASE)
|
||||
@@ -251,18 +253,6 @@ static int word_char(char ch)
|
||||
return isalnum(ch) || ch == '_';
|
||||
}
|
||||
|
||||
static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
|
||||
const char *name, unsigned lno, char sign)
|
||||
{
|
||||
if (opt->null_following_name)
|
||||
sign = '\0';
|
||||
if (opt->pathname)
|
||||
printf("%s%c", name, sign);
|
||||
if (opt->linenum)
|
||||
printf("%d%c", lno, sign);
|
||||
printf("%.*s\n", (int)(eol-bol), bol);
|
||||
}
|
||||
|
||||
static void show_name(struct grep_opt *opt, const char *name)
|
||||
{
|
||||
printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
|
||||
@@ -306,11 +296,12 @@ static struct {
|
||||
{ "committer ", 10 },
|
||||
};
|
||||
|
||||
static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
|
||||
static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
|
||||
enum grep_context ctx,
|
||||
regmatch_t *pmatch, int eflags)
|
||||
{
|
||||
int hit = 0;
|
||||
int saved_ch = 0;
|
||||
regmatch_t pmatch[10];
|
||||
|
||||
if ((p->token != GREP_PATTERN) &&
|
||||
((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
|
||||
@@ -329,16 +320,12 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
|
||||
}
|
||||
|
||||
again:
|
||||
if (!p->fixed) {
|
||||
regex_t *exp = &p->regexp;
|
||||
hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
|
||||
pmatch, 0);
|
||||
}
|
||||
else {
|
||||
if (p->fixed)
|
||||
hit = !fixmatch(p->pattern, bol, pmatch);
|
||||
}
|
||||
else
|
||||
hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
|
||||
|
||||
if (hit && opt->word_regexp) {
|
||||
if (hit && p->word_regexp) {
|
||||
if ((pmatch[0].rm_so < 0) ||
|
||||
(eol - bol) <= pmatch[0].rm_so ||
|
||||
(pmatch[0].rm_eo < 0) ||
|
||||
@@ -378,39 +365,33 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int match_expr_eval(struct grep_opt *o,
|
||||
struct grep_expr *x,
|
||||
char *bol, char *eol,
|
||||
enum grep_context ctx,
|
||||
int collect_hits)
|
||||
static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
|
||||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
int h = 0;
|
||||
regmatch_t match;
|
||||
|
||||
switch (x->node) {
|
||||
case GREP_NODE_ATOM:
|
||||
h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
|
||||
h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
|
||||
break;
|
||||
case GREP_NODE_NOT:
|
||||
h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
|
||||
h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0);
|
||||
break;
|
||||
case GREP_NODE_AND:
|
||||
if (!collect_hits)
|
||||
return (match_expr_eval(o, x->u.binary.left,
|
||||
bol, eol, ctx, 0) &&
|
||||
match_expr_eval(o, x->u.binary.right,
|
||||
bol, eol, ctx, 0));
|
||||
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||
h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
|
||||
if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0))
|
||||
return 0;
|
||||
h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0);
|
||||
break;
|
||||
case GREP_NODE_OR:
|
||||
if (!collect_hits)
|
||||
return (match_expr_eval(o, x->u.binary.left,
|
||||
return (match_expr_eval(x->u.binary.left,
|
||||
bol, eol, ctx, 0) ||
|
||||
match_expr_eval(o, x->u.binary.right,
|
||||
match_expr_eval(x->u.binary.right,
|
||||
bol, eol, ctx, 0));
|
||||
h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
|
||||
h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0);
|
||||
x->u.binary.left->hit |= h;
|
||||
h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
|
||||
h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1);
|
||||
break;
|
||||
default:
|
||||
die("Unexpected node type (internal error) %d", x->node);
|
||||
@@ -424,24 +405,104 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol,
|
||||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
struct grep_expr *x = opt->pattern_expression;
|
||||
return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
|
||||
return match_expr_eval(x, bol, eol, ctx, collect_hits);
|
||||
}
|
||||
|
||||
static int match_line(struct grep_opt *opt, char *bol, char *eol,
|
||||
enum grep_context ctx, int collect_hits)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
regmatch_t match;
|
||||
|
||||
if (opt->extended)
|
||||
return match_expr(opt, bol, eol, ctx, collect_hits);
|
||||
|
||||
/* we do not call with collect_hits without being extended */
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
if (match_one_pattern(opt, p, bol, eol, ctx))
|
||||
if (match_one_pattern(p, bol, eol, ctx, &match, 0))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
|
||||
enum grep_context ctx,
|
||||
regmatch_t *pmatch, int eflags)
|
||||
{
|
||||
regmatch_t match;
|
||||
|
||||
if (!match_one_pattern(p, bol, eol, ctx, &match, eflags))
|
||||
return 0;
|
||||
if (match.rm_so < 0 || match.rm_eo < 0)
|
||||
return 0;
|
||||
if (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) {
|
||||
if (match.rm_so > pmatch->rm_so)
|
||||
return 1;
|
||||
if (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo)
|
||||
return 1;
|
||||
}
|
||||
pmatch->rm_so = match.rm_so;
|
||||
pmatch->rm_eo = match.rm_eo;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int next_match(struct grep_opt *opt, char *bol, char *eol,
|
||||
enum grep_context ctx, regmatch_t *pmatch, int eflags)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
int hit = 0;
|
||||
|
||||
pmatch->rm_so = pmatch->rm_eo = -1;
|
||||
if (bol < eol) {
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
switch (p->token) {
|
||||
case GREP_PATTERN: /* atom */
|
||||
case GREP_PATTERN_HEAD:
|
||||
case GREP_PATTERN_BODY:
|
||||
hit |= match_next_pattern(p, bol, eol, ctx,
|
||||
pmatch, eflags);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
static void show_line(struct grep_opt *opt, char *bol, char *eol,
|
||||
const char *name, unsigned lno, char sign)
|
||||
{
|
||||
int rest = eol - bol;
|
||||
|
||||
if (opt->null_following_name)
|
||||
sign = '\0';
|
||||
if (opt->pathname)
|
||||
printf("%s%c", name, sign);
|
||||
if (opt->linenum)
|
||||
printf("%d%c", lno, sign);
|
||||
if (opt->color) {
|
||||
regmatch_t match;
|
||||
enum grep_context ctx = GREP_CONTEXT_BODY;
|
||||
int ch = *eol;
|
||||
int eflags = 0;
|
||||
|
||||
*eol = '\0';
|
||||
while (next_match(opt, bol, eol, ctx, &match, eflags)) {
|
||||
printf("%.*s%s%.*s%s",
|
||||
(int)match.rm_so, bol,
|
||||
opt->color_match,
|
||||
(int)(match.rm_eo - match.rm_so), bol + match.rm_so,
|
||||
GIT_COLOR_RESET);
|
||||
bol += match.rm_eo;
|
||||
rest -= match.rm_eo;
|
||||
eflags = REG_NOTBOL;
|
||||
}
|
||||
*eol = ch;
|
||||
}
|
||||
printf("%.*s\n", rest, bol);
|
||||
}
|
||||
|
||||
static int grep_buffer_1(struct grep_opt *opt, const char *name,
|
||||
char *buf, unsigned long size, int collect_hits)
|
||||
{
|
||||
|
||||
5
grep.h
5
grep.h
@@ -1,5 +1,6 @@
|
||||
#ifndef GREP_H
|
||||
#define GREP_H
|
||||
#include "color.h"
|
||||
|
||||
enum grep_pat_token {
|
||||
GREP_PATTERN,
|
||||
@@ -31,6 +32,7 @@ struct grep_pat {
|
||||
enum grep_header_field field;
|
||||
regex_t regexp;
|
||||
unsigned fixed:1;
|
||||
unsigned word_regexp:1;
|
||||
};
|
||||
|
||||
enum grep_expr_node {
|
||||
@@ -76,6 +78,9 @@ struct grep_opt {
|
||||
unsigned relative:1;
|
||||
unsigned pathname:1;
|
||||
unsigned null_following_name:1;
|
||||
int color;
|
||||
char color_match[COLOR_MAXLEN];
|
||||
const char *color_external;
|
||||
int regflags;
|
||||
unsigned pre_context;
|
||||
unsigned post_context;
|
||||
|
||||
72
http-push.c
72
http-push.c
@@ -1792,21 +1792,8 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct ref *local_refs, **local_tail;
|
||||
static struct ref *remote_refs, **remote_tail;
|
||||
|
||||
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct ref *ref;
|
||||
int len = strlen(refname) + 1;
|
||||
ref = xcalloc(1, sizeof(*ref) + len);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
memcpy(ref->name, refname, len);
|
||||
*local_tail = ref;
|
||||
local_tail = &ref->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void one_remote_ref(char *refname)
|
||||
{
|
||||
struct ref *ref;
|
||||
@@ -1839,12 +1826,6 @@ static void one_remote_ref(char *refname)
|
||||
remote_tail = &ref->next;
|
||||
}
|
||||
|
||||
static void get_local_heads(void)
|
||||
{
|
||||
local_tail = &local_refs;
|
||||
for_each_ref(one_local_ref, NULL);
|
||||
}
|
||||
|
||||
static void get_dav_remote_heads(void)
|
||||
{
|
||||
remote_tail = &remote_refs;
|
||||
@@ -1862,55 +1843,6 @@ static int is_zero_sha1(const unsigned char *sha1)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void unmark_and_free(struct commit_list *list, unsigned int mark)
|
||||
{
|
||||
while (list) {
|
||||
struct commit_list *temp = list;
|
||||
temp->item->object.flags &= ~mark;
|
||||
list = temp->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
static int ref_newer(const unsigned char *new_sha1,
|
||||
const unsigned char *old_sha1)
|
||||
{
|
||||
struct object *o;
|
||||
struct commit *old, *new;
|
||||
struct commit_list *list, *used;
|
||||
int found = 0;
|
||||
|
||||
/* Both new and old must be commit-ish and new is descendant of
|
||||
* old. Otherwise we require --force.
|
||||
*/
|
||||
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
old = (struct commit *) o;
|
||||
|
||||
o = deref_tag(parse_object(new_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
new = (struct commit *) o;
|
||||
|
||||
if (parse_commit(new) < 0)
|
||||
return 0;
|
||||
|
||||
used = list = NULL;
|
||||
commit_list_insert(new, &list);
|
||||
while (list) {
|
||||
new = pop_most_recent_commit(&list, TMP_MARK);
|
||||
commit_list_insert(new, &used);
|
||||
if (new == old) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmark_and_free(list, TMP_MARK);
|
||||
unmark_and_free(used, TMP_MARK);
|
||||
return found;
|
||||
}
|
||||
|
||||
static void add_remote_info_ref(struct remote_ls_ctx *ls)
|
||||
{
|
||||
struct strbuf *buf = (struct strbuf *)ls->userData;
|
||||
@@ -2195,7 +2127,7 @@ int main(int argc, char **argv)
|
||||
int rc = 0;
|
||||
int i;
|
||||
int new_refs;
|
||||
struct ref *ref;
|
||||
struct ref *ref, *local_refs;
|
||||
char *rewritten_url = NULL;
|
||||
|
||||
git_extract_argv0_path(argv[0]);
|
||||
@@ -2302,7 +2234,7 @@ int main(int argc, char **argv)
|
||||
fetch_indices();
|
||||
|
||||
/* Get a list of all local and remote heads to validate refspecs */
|
||||
get_local_heads();
|
||||
local_refs = get_local_heads();
|
||||
fprintf(stderr, "Fetching remote heads...\n");
|
||||
get_dav_remote_heads();
|
||||
|
||||
|
||||
85
read-cache.c
85
read-cache.c
@@ -67,8 +67,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
|
||||
*/
|
||||
void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
|
||||
{
|
||||
ce->ce_ctime = st->st_ctime;
|
||||
ce->ce_mtime = st->st_mtime;
|
||||
ce->ce_ctime.sec = (unsigned int)st->st_ctime;
|
||||
ce->ce_mtime.sec = (unsigned int)st->st_mtime;
|
||||
ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
|
||||
ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
|
||||
ce->ce_dev = st->st_dev;
|
||||
ce->ce_ino = st->st_ino;
|
||||
ce->ce_uid = st->st_uid;
|
||||
@@ -196,11 +198,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
|
||||
default:
|
||||
die("internal error: ce_mode is %o", ce->ce_mode);
|
||||
}
|
||||
if (ce->ce_mtime != (unsigned int) st->st_mtime)
|
||||
if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
|
||||
changed |= MTIME_CHANGED;
|
||||
if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime)
|
||||
if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime)
|
||||
changed |= CTIME_CHANGED;
|
||||
|
||||
#ifdef USE_NSEC
|
||||
if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
|
||||
changed |= MTIME_CHANGED;
|
||||
if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
|
||||
changed |= CTIME_CHANGED;
|
||||
#endif
|
||||
|
||||
if (ce->ce_uid != (unsigned int) st->st_uid ||
|
||||
ce->ce_gid != (unsigned int) st->st_gid)
|
||||
changed |= OWNER_CHANGED;
|
||||
@@ -232,8 +241,16 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
|
||||
static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
|
||||
{
|
||||
return (!S_ISGITLINK(ce->ce_mode) &&
|
||||
istate->timestamp &&
|
||||
((unsigned int)istate->timestamp) <= ce->ce_mtime);
|
||||
istate->timestamp.sec &&
|
||||
#ifdef USE_NSEC
|
||||
/* nanosecond timestamped files can also be racy! */
|
||||
(istate->timestamp.sec < ce->ce_mtime.sec ||
|
||||
(istate->timestamp.sec == ce->ce_mtime.sec &&
|
||||
istate->timestamp.nsec <= ce->ce_mtime.nsec))
|
||||
#else
|
||||
istate->timestamp.sec <= ce->ce_mtime.sec
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
int ie_match_stat(const struct index_state *istate,
|
||||
@@ -443,6 +460,26 @@ int remove_index_entry_at(struct index_state *istate, int pos)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all cache ententries marked for removal, that is where
|
||||
* CE_REMOVE is set in ce_flags. This is much more effective than
|
||||
* calling remove_index_entry_at() for each entry to be removed.
|
||||
*/
|
||||
void remove_marked_cache_entries(struct index_state *istate)
|
||||
{
|
||||
struct cache_entry **ce_array = istate->cache;
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = j = 0; i < istate->cache_nr; i++) {
|
||||
if (ce_array[i]->ce_flags & CE_REMOVE)
|
||||
remove_name_hash(ce_array[i]);
|
||||
else
|
||||
ce_array[j++] = ce_array[i];
|
||||
}
|
||||
istate->cache_changed = 1;
|
||||
istate->cache_nr = j;
|
||||
}
|
||||
|
||||
int remove_file_from_index(struct index_state *istate, const char *path)
|
||||
{
|
||||
int pos = index_name_pos(istate, path, strlen(path));
|
||||
@@ -1139,8 +1176,10 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
|
||||
size_t len;
|
||||
const char *name;
|
||||
|
||||
ce->ce_ctime = ntohl(ondisk->ctime.sec);
|
||||
ce->ce_mtime = ntohl(ondisk->mtime.sec);
|
||||
ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
|
||||
ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
|
||||
ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
|
||||
ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
|
||||
ce->ce_dev = ntohl(ondisk->dev);
|
||||
ce->ce_ino = ntohl(ondisk->ino);
|
||||
ce->ce_mode = ntohl(ondisk->mode);
|
||||
@@ -1206,7 +1245,8 @@ int read_index_from(struct index_state *istate, const char *path)
|
||||
return istate->cache_nr;
|
||||
|
||||
errno = ENOENT;
|
||||
istate->timestamp = 0;
|
||||
istate->timestamp.sec = 0;
|
||||
istate->timestamp.nsec = 0;
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
@@ -1258,7 +1298,9 @@ int read_index_from(struct index_state *istate, const char *path)
|
||||
src_offset += ondisk_ce_size(ce);
|
||||
dst_offset += ce_size(ce);
|
||||
}
|
||||
istate->timestamp = st.st_mtime;
|
||||
istate->timestamp.sec = st.st_mtime;
|
||||
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
||||
|
||||
while (src_offset <= mmap_size - 20 - 8) {
|
||||
/* After an array of active_nr index entries,
|
||||
* there can be arbitrary number of extended
|
||||
@@ -1288,14 +1330,15 @@ unmap:
|
||||
|
||||
int is_index_unborn(struct index_state *istate)
|
||||
{
|
||||
return (!istate->cache_nr && !istate->alloc && !istate->timestamp);
|
||||
return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec);
|
||||
}
|
||||
|
||||
int discard_index(struct index_state *istate)
|
||||
{
|
||||
istate->cache_nr = 0;
|
||||
istate->cache_changed = 0;
|
||||
istate->timestamp = 0;
|
||||
istate->timestamp.sec = 0;
|
||||
istate->timestamp.nsec = 0;
|
||||
istate->name_hash_initialized = 0;
|
||||
free_hash(&istate->name_hash);
|
||||
cache_tree_free(&(istate->cache_tree));
|
||||
@@ -1441,10 +1484,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
|
||||
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
|
||||
char *name;
|
||||
|
||||
ondisk->ctime.sec = htonl(ce->ce_ctime);
|
||||
ondisk->ctime.nsec = 0;
|
||||
ondisk->mtime.sec = htonl(ce->ce_mtime);
|
||||
ondisk->mtime.nsec = 0;
|
||||
ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
|
||||
ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
|
||||
ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
|
||||
ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
|
||||
ondisk->dev = htonl(ce->ce_dev);
|
||||
ondisk->ino = htonl(ce->ce_ino);
|
||||
ondisk->mode = htonl(ce->ce_mode);
|
||||
@@ -1466,13 +1509,14 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
|
||||
return ce_write(c, fd, ondisk, size);
|
||||
}
|
||||
|
||||
int write_index(const struct index_state *istate, int newfd)
|
||||
int write_index(struct index_state *istate, int newfd)
|
||||
{
|
||||
git_SHA_CTX c;
|
||||
struct cache_header hdr;
|
||||
int i, err, removed, extended;
|
||||
struct cache_entry **cache = istate->cache;
|
||||
int entries = istate->cache_nr;
|
||||
struct stat st;
|
||||
|
||||
for (i = removed = extended = 0; i < entries; i++) {
|
||||
if (cache[i]->ce_flags & CE_REMOVE)
|
||||
@@ -1516,7 +1560,12 @@ int write_index(const struct index_state *istate, int newfd)
|
||||
if (err)
|
||||
return -1;
|
||||
}
|
||||
return ce_flush(&c, newfd);
|
||||
|
||||
if (ce_flush(&c, newfd) || fstat(newfd, &st))
|
||||
return -1;
|
||||
istate->timestamp.sec = (unsigned int)st.st_mtime;
|
||||
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
4
refs.c
4
refs.c
@@ -1628,10 +1628,10 @@ int update_ref(const char *action, const char *refname,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ref *find_ref_by_name(struct ref *list, const char *name)
|
||||
struct ref *find_ref_by_name(const struct ref *list, const char *name)
|
||||
{
|
||||
for ( ; list; list = list->next)
|
||||
if (!strcmp(list->name, name))
|
||||
return list;
|
||||
return (struct ref *)list;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
160
remote.c
160
remote.c
@@ -5,6 +5,7 @@
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "dir.h"
|
||||
#include "tag.h"
|
||||
|
||||
static struct refspec s_tag_refspec = {
|
||||
0,
|
||||
@@ -38,6 +39,7 @@ static int branches_nr;
|
||||
|
||||
static struct branch *current_branch;
|
||||
static const char *default_remote_name;
|
||||
static int explicit_default_remote_name;
|
||||
|
||||
static struct rewrite **rewrite;
|
||||
static int rewrite_alloc;
|
||||
@@ -330,8 +332,10 @@ static int handle_config(const char *key, const char *value, void *cb)
|
||||
if (!value)
|
||||
return config_error_nonbool(key);
|
||||
branch->remote_name = xstrdup(value);
|
||||
if (branch == current_branch)
|
||||
if (branch == current_branch) {
|
||||
default_remote_name = branch->remote_name;
|
||||
explicit_default_remote_name = 1;
|
||||
}
|
||||
} else if (!strcmp(subkey, ".merge")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(key);
|
||||
@@ -643,10 +647,16 @@ static int valid_remote_nick(const char *name)
|
||||
struct remote *remote_get(const char *name)
|
||||
{
|
||||
struct remote *ret;
|
||||
int name_given = 0;
|
||||
|
||||
read_config();
|
||||
if (!name)
|
||||
if (name)
|
||||
name_given = 1;
|
||||
else {
|
||||
name = default_remote_name;
|
||||
name_given = explicit_default_remote_name;
|
||||
}
|
||||
|
||||
ret = make_remote(name, 0);
|
||||
if (valid_remote_nick(name)) {
|
||||
if (!ret->url)
|
||||
@@ -654,7 +664,7 @@ struct remote *remote_get(const char *name)
|
||||
if (!ret->url)
|
||||
read_branches_file(ret);
|
||||
}
|
||||
if (!ret->url)
|
||||
if (name_given && !ret->url)
|
||||
add_url_alias(ret, name);
|
||||
if (!ret->url)
|
||||
return NULL;
|
||||
@@ -778,10 +788,18 @@ struct ref *alloc_ref(const char *name)
|
||||
|
||||
static struct ref *copy_ref(const struct ref *ref)
|
||||
{
|
||||
struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
|
||||
memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
struct ref *cpy;
|
||||
size_t len;
|
||||
if (!ref)
|
||||
return NULL;
|
||||
len = strlen(ref->name);
|
||||
cpy = xmalloc(sizeof(struct ref) + len + 1);
|
||||
memcpy(cpy, ref, sizeof(struct ref) + len + 1);
|
||||
cpy->next = NULL;
|
||||
cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
|
||||
cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
|
||||
cpy->peer_ref = copy_ref(ref->peer_ref);
|
||||
return cpy;
|
||||
}
|
||||
|
||||
struct ref *copy_ref_list(const struct ref *ref)
|
||||
@@ -800,6 +818,7 @@ static void free_ref(struct ref *ref)
|
||||
{
|
||||
if (!ref)
|
||||
return;
|
||||
free_ref(ref->peer_ref);
|
||||
free(ref->remote_status);
|
||||
free(ref->symref);
|
||||
free(ref);
|
||||
@@ -810,7 +829,6 @@ void free_refs(struct ref *ref)
|
||||
struct ref *next;
|
||||
while (ref) {
|
||||
next = ref->next;
|
||||
free(ref->peer_ref);
|
||||
free_ref(ref);
|
||||
ref = next;
|
||||
}
|
||||
@@ -927,6 +945,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
|
||||
struct refspec *rs)
|
||||
{
|
||||
struct ref *matched_src, *matched_dst;
|
||||
int copy_src;
|
||||
|
||||
const char *dst_value = rs->dst;
|
||||
char *dst_guess;
|
||||
@@ -937,6 +956,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
|
||||
matched_src = matched_dst = NULL;
|
||||
switch (count_refspec_match(rs->src, src, &matched_src)) {
|
||||
case 1:
|
||||
copy_src = 1;
|
||||
break;
|
||||
case 0:
|
||||
/* The source could be in the get_sha1() format
|
||||
@@ -946,6 +966,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
|
||||
matched_src = try_explicit_object_name(rs->src);
|
||||
if (!matched_src)
|
||||
return error("src refspec %s does not match any.", rs->src);
|
||||
copy_src = 0;
|
||||
break;
|
||||
default:
|
||||
return error("src refspec %s matches more than one.", rs->src);
|
||||
@@ -991,7 +1012,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
|
||||
return error("dst ref %s receives from more than one src.",
|
||||
matched_dst->name);
|
||||
else {
|
||||
matched_dst->peer_ref = matched_src;
|
||||
matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
|
||||
matched_dst->force = rs->force;
|
||||
}
|
||||
return 0;
|
||||
@@ -1040,6 +1061,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
struct refspec *rs;
|
||||
int send_all = flags & MATCH_REFS_ALL;
|
||||
int send_mirror = flags & MATCH_REFS_MIRROR;
|
||||
int errs;
|
||||
static const char *default_refspec[] = { ":", 0 };
|
||||
|
||||
if (!nr_refspec) {
|
||||
@@ -1047,8 +1069,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
refspec = default_refspec;
|
||||
}
|
||||
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
|
||||
if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
|
||||
return -1;
|
||||
errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec);
|
||||
|
||||
/* pick the remainder */
|
||||
for ( ; src; src = src->next) {
|
||||
@@ -1099,11 +1120,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
|
||||
dst_peer = make_linked_ref(dst_name, dst_tail);
|
||||
hashcpy(dst_peer->new_sha1, src->new_sha1);
|
||||
}
|
||||
dst_peer->peer_ref = src;
|
||||
dst_peer->peer_ref = copy_ref(src);
|
||||
dst_peer->force = pat->force;
|
||||
free_name:
|
||||
free(dst_name);
|
||||
}
|
||||
if (errs)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1269,6 +1292,54 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void unmark_and_free(struct commit_list *list, unsigned int mark)
|
||||
{
|
||||
while (list) {
|
||||
struct commit_list *temp = list;
|
||||
temp->item->object.flags &= ~mark;
|
||||
list = temp->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
|
||||
{
|
||||
struct object *o;
|
||||
struct commit *old, *new;
|
||||
struct commit_list *list, *used;
|
||||
int found = 0;
|
||||
|
||||
/* Both new and old must be commit-ish and new is descendant of
|
||||
* old. Otherwise we require --force.
|
||||
*/
|
||||
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
old = (struct commit *) o;
|
||||
|
||||
o = deref_tag(parse_object(new_sha1), NULL, 0);
|
||||
if (!o || o->type != OBJ_COMMIT)
|
||||
return 0;
|
||||
new = (struct commit *) o;
|
||||
|
||||
if (parse_commit(new) < 0)
|
||||
return 0;
|
||||
|
||||
used = list = NULL;
|
||||
commit_list_insert(new, &list);
|
||||
while (list) {
|
||||
new = pop_most_recent_commit(&list, TMP_MARK);
|
||||
commit_list_insert(new, &used);
|
||||
if (new == old) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmark_and_free(list, TMP_MARK);
|
||||
unmark_and_free(used, TMP_MARK);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if there is anything to report, otherwise false.
|
||||
*/
|
||||
@@ -1376,3 +1447,68 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
|
||||
base, num_ours, num_theirs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
||||
{
|
||||
struct ref ***local_tail = cb_data;
|
||||
struct ref *ref;
|
||||
int len;
|
||||
|
||||
/* we already know it starts with refs/ to get here */
|
||||
if (check_ref_format(refname + 5))
|
||||
return 0;
|
||||
|
||||
len = strlen(refname) + 1;
|
||||
ref = xcalloc(1, sizeof(*ref) + len);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
memcpy(ref->name, refname, len);
|
||||
**local_tail = ref;
|
||||
*local_tail = &ref->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ref *get_local_heads(void)
|
||||
{
|
||||
struct ref *local_refs, **local_tail = &local_refs;
|
||||
for_each_ref(one_local_ref, &local_tail);
|
||||
return local_refs;
|
||||
}
|
||||
|
||||
struct ref *guess_remote_head(const struct ref *head,
|
||||
const struct ref *refs,
|
||||
int all)
|
||||
{
|
||||
const struct ref *r;
|
||||
struct ref *list = NULL;
|
||||
struct ref **tail = &list;
|
||||
|
||||
if (!head)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Some transports support directly peeking at
|
||||
* where HEAD points; if that is the case, then
|
||||
* we don't have to guess.
|
||||
*/
|
||||
if (head->symref)
|
||||
return copy_ref(find_ref_by_name(refs, head->symref));
|
||||
|
||||
/* If refs/heads/master could be right, it is. */
|
||||
if (!all) {
|
||||
r = find_ref_by_name(refs, "refs/heads/master");
|
||||
if (r && !hashcmp(r->old_sha1, head->old_sha1))
|
||||
return copy_ref(r);
|
||||
}
|
||||
|
||||
/* Look for another ref that points there */
|
||||
for (r = refs; r; r = r->next) {
|
||||
if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) {
|
||||
*tail = copy_ref(r);
|
||||
tail = &((*tail)->next);
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
12
remote.h
12
remote.h
@@ -74,6 +74,7 @@ int check_ref_type(const struct ref *ref, int flags);
|
||||
void free_refs(struct ref *ref);
|
||||
|
||||
int resolve_remote_symref(struct ref *ref, struct ref *list);
|
||||
int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);
|
||||
|
||||
/*
|
||||
* Removes and frees any duplicate refs in the map.
|
||||
@@ -137,4 +138,15 @@ enum match_refs_flags {
|
||||
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
|
||||
int format_tracking_info(struct branch *branch, struct strbuf *sb);
|
||||
|
||||
struct ref *get_local_heads(void);
|
||||
/*
|
||||
* Find refs from a list which are likely to be pointed to by the given HEAD
|
||||
* ref. If 'all' is false, returns the most likely ref; otherwise, returns a
|
||||
* list of all candidate refs. If no match is found (or 'head' is NULL),
|
||||
* returns NULL. All returns are newly allocated and should be freed.
|
||||
*/
|
||||
struct ref *guess_remote_head(const struct ref *head,
|
||||
const struct ref *refs,
|
||||
int all);
|
||||
|
||||
#endif
|
||||
|
||||
19
sideband.c
19
sideband.c
@@ -19,7 +19,7 @@
|
||||
|
||||
#define FIX_SIZE 10 /* large enough for any of the above */
|
||||
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
int recv_sideband(const char *me, int in_stream, int out)
|
||||
{
|
||||
unsigned pf = strlen(PREFIX);
|
||||
unsigned sf;
|
||||
@@ -41,8 +41,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
if (len == 0)
|
||||
break;
|
||||
if (len < 1) {
|
||||
len = sprintf(buf, "%s: protocol error: no band designator\n", me);
|
||||
safe_write(err, buf, len);
|
||||
fprintf(stderr, "%s: protocol error: no band designator\n", me);
|
||||
return SIDEBAND_PROTOCOL_ERROR;
|
||||
}
|
||||
band = buf[pf] & 0xff;
|
||||
@@ -50,8 +49,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
switch (band) {
|
||||
case 3:
|
||||
buf[pf] = ' ';
|
||||
buf[pf+1+len] = '\n';
|
||||
safe_write(err, buf, pf+1+len+1);
|
||||
buf[pf+1+len] = '\0';
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
return SIDEBAND_REMOTE_ERROR;
|
||||
case 2:
|
||||
buf[pf] = ' ';
|
||||
@@ -95,12 +94,12 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
memcpy(save, b + brk, sf);
|
||||
b[brk + sf - 1] = b[brk - 1];
|
||||
memcpy(b + brk - 1, suffix, sf);
|
||||
safe_write(err, b, brk + sf);
|
||||
fprintf(stderr, "%.*s", brk + sf, b);
|
||||
memcpy(b + brk, save, sf);
|
||||
len -= brk;
|
||||
} else {
|
||||
int l = brk ? brk : len;
|
||||
safe_write(err, b, l);
|
||||
fprintf(stderr, "%.*s", l, b);
|
||||
len -= l;
|
||||
}
|
||||
|
||||
@@ -112,10 +111,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
safe_write(out, buf + pf+1, len);
|
||||
continue;
|
||||
default:
|
||||
len = sprintf(buf,
|
||||
"%s: protocol error: bad band #%d\n",
|
||||
me, band);
|
||||
safe_write(err, buf, len);
|
||||
fprintf(stderr, "%s: protocol error: bad band #%d\n",
|
||||
me, band);
|
||||
return SIDEBAND_PROTOCOL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define DEFAULT_PACKET_MAX 1000
|
||||
#define LARGE_PACKET_MAX 65520
|
||||
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err);
|
||||
int recv_sideband(const char *me, int in_stream, int out);
|
||||
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -92,6 +92,16 @@ struct string_list_item *string_list_lookup(const char *string, struct string_li
|
||||
return list->items + i;
|
||||
}
|
||||
|
||||
int for_each_string_list(string_list_each_func_t fn,
|
||||
struct string_list *list, void *cb_data)
|
||||
{
|
||||
int i, ret = 0;
|
||||
for (i = 0; i < list->nr; i++)
|
||||
if ((ret = fn(&list->items[i], cb_data)))
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void string_list_clear(struct string_list *list, int free_util)
|
||||
{
|
||||
if (list->items) {
|
||||
|
||||
@@ -20,6 +20,11 @@ void string_list_clear(struct string_list *list, int free_util);
|
||||
typedef void (*string_list_clear_func_t)(void *p, const char *str);
|
||||
void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
|
||||
|
||||
/* Use this function to iterate over each item */
|
||||
typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
|
||||
int for_each_string_list(string_list_each_func_t,
|
||||
struct string_list *list, void *cb_data);
|
||||
|
||||
/* Use these functions only on sorted lists: */
|
||||
int string_list_has_string(const struct string_list *list, const char *string);
|
||||
int string_list_find_insert_index(const struct string_list *list, const char *string,
|
||||
|
||||
171
symlinks.c
171
symlinks.c
@@ -1,5 +1,37 @@
|
||||
#include "cache.h"
|
||||
|
||||
/*
|
||||
* Returns the length (on a path component basis) of the longest
|
||||
* common prefix match of 'name_a' and 'name_b'.
|
||||
*/
|
||||
static int longest_path_match(const char *name_a, int len_a,
|
||||
const char *name_b, int len_b,
|
||||
int *previous_slash)
|
||||
{
|
||||
int max_len, match_len = 0, match_len_prev = 0, i = 0;
|
||||
|
||||
max_len = len_a < len_b ? len_a : len_b;
|
||||
while (i < max_len && name_a[i] == name_b[i]) {
|
||||
if (name_a[i] == '/') {
|
||||
match_len_prev = match_len;
|
||||
match_len = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
/*
|
||||
* Is 'name_b' a substring of 'name_a', the other way around,
|
||||
* or is 'name_a' and 'name_b' the exact same string?
|
||||
*/
|
||||
if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') ||
|
||||
(len_a < len_b && name_b[len_a] == '/') ||
|
||||
(len_a == len_b))) {
|
||||
match_len_prev = match_len;
|
||||
match_len = i;
|
||||
}
|
||||
*previous_slash = match_len_prev;
|
||||
return match_len;
|
||||
}
|
||||
|
||||
static struct cache_def {
|
||||
char path[PATH_MAX + 1];
|
||||
int len;
|
||||
@@ -8,44 +40,15 @@ static struct cache_def {
|
||||
int prefix_len_stat_func;
|
||||
} cache;
|
||||
|
||||
/*
|
||||
* Returns the length (on a path component basis) of the longest
|
||||
* common prefix match of 'name' and the cached path string.
|
||||
*/
|
||||
static inline int longest_match_lstat_cache(int len, const char *name,
|
||||
int *previous_slash)
|
||||
{
|
||||
int max_len, match_len = 0, match_len_prev = 0, i = 0;
|
||||
|
||||
max_len = len < cache.len ? len : cache.len;
|
||||
while (i < max_len && name[i] == cache.path[i]) {
|
||||
if (name[i] == '/') {
|
||||
match_len_prev = match_len;
|
||||
match_len = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
/* Is the cached path string a substring of 'name'? */
|
||||
if (i == cache.len && cache.len < len && name[cache.len] == '/') {
|
||||
match_len_prev = match_len;
|
||||
match_len = cache.len;
|
||||
/* Is 'name' a substring of the cached path string? */
|
||||
} else if ((i == len && len < cache.len && cache.path[len] == '/') ||
|
||||
(i == len && len == cache.len)) {
|
||||
match_len_prev = match_len;
|
||||
match_len = len;
|
||||
}
|
||||
*previous_slash = match_len_prev;
|
||||
return match_len;
|
||||
}
|
||||
|
||||
static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
|
||||
static inline void reset_lstat_cache(void)
|
||||
{
|
||||
cache.path[0] = '\0';
|
||||
cache.len = 0;
|
||||
cache.flags = 0;
|
||||
cache.track_flags = track_flags;
|
||||
cache.prefix_len_stat_func = prefix_len_stat_func;
|
||||
/*
|
||||
* The track_flags and prefix_len_stat_func members is only
|
||||
* set by the safeguard rule inside lstat_cache()
|
||||
*/
|
||||
}
|
||||
|
||||
#define FL_DIR (1 << 0)
|
||||
@@ -67,7 +70,7 @@ static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
|
||||
* of the prefix, where the cache should use the stat() function
|
||||
* instead of the lstat() function to test each path component.
|
||||
*/
|
||||
static int lstat_cache(int len, const char *name,
|
||||
static int lstat_cache(const char *name, int len,
|
||||
int track_flags, int prefix_len_stat_func)
|
||||
{
|
||||
int match_len, last_slash, last_slash_dir, previous_slash;
|
||||
@@ -77,11 +80,13 @@ static int lstat_cache(int len, const char *name,
|
||||
if (cache.track_flags != track_flags ||
|
||||
cache.prefix_len_stat_func != prefix_len_stat_func) {
|
||||
/*
|
||||
* As a safeguard we clear the cache if the values of
|
||||
* track_flags and/or prefix_len_stat_func does not
|
||||
* match with the last supplied values.
|
||||
* As a safeguard rule we clear the cache if the
|
||||
* values of track_flags and/or prefix_len_stat_func
|
||||
* does not match with the last supplied values.
|
||||
*/
|
||||
reset_lstat_cache(track_flags, prefix_len_stat_func);
|
||||
reset_lstat_cache();
|
||||
cache.track_flags = track_flags;
|
||||
cache.prefix_len_stat_func = prefix_len_stat_func;
|
||||
match_len = last_slash = 0;
|
||||
} else {
|
||||
/*
|
||||
@@ -89,7 +94,8 @@ static int lstat_cache(int len, const char *name,
|
||||
* the 2 "excluding" path types.
|
||||
*/
|
||||
match_len = last_slash =
|
||||
longest_match_lstat_cache(len, name, &previous_slash);
|
||||
longest_path_match(name, len, cache.path, cache.len,
|
||||
&previous_slash);
|
||||
match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
|
||||
if (match_flags && match_len == cache.len)
|
||||
return match_flags;
|
||||
@@ -153,7 +159,7 @@ static int lstat_cache(int len, const char *name,
|
||||
cache.path[last_slash] = '\0';
|
||||
cache.len = last_slash;
|
||||
cache.flags = save_flags;
|
||||
} else if (track_flags & FL_DIR &&
|
||||
} else if ((track_flags & FL_DIR) &&
|
||||
last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
|
||||
/*
|
||||
* We have a separate test for the directory case,
|
||||
@@ -170,7 +176,7 @@ static int lstat_cache(int len, const char *name,
|
||||
cache.len = last_slash_dir;
|
||||
cache.flags = FL_DIR;
|
||||
} else {
|
||||
reset_lstat_cache(track_flags, prefix_len_stat_func);
|
||||
reset_lstat_cache();
|
||||
}
|
||||
return ret_flags;
|
||||
}
|
||||
@@ -179,19 +185,19 @@ static int lstat_cache(int len, const char *name,
|
||||
* Invalidate the given 'name' from the cache, if 'name' matches
|
||||
* completely with the cache.
|
||||
*/
|
||||
void invalidate_lstat_cache(int len, const char *name)
|
||||
void invalidate_lstat_cache(const char *name, int len)
|
||||
{
|
||||
int match_len, previous_slash;
|
||||
|
||||
match_len = longest_match_lstat_cache(len, name, &previous_slash);
|
||||
match_len = longest_path_match(name, len, cache.path, cache.len,
|
||||
&previous_slash);
|
||||
if (len == match_len) {
|
||||
if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
|
||||
cache.path[previous_slash] = '\0';
|
||||
cache.len = previous_slash;
|
||||
cache.flags = FL_DIR;
|
||||
} else
|
||||
reset_lstat_cache(cache.track_flags,
|
||||
cache.prefix_len_stat_func);
|
||||
reset_lstat_cache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +206,7 @@ void invalidate_lstat_cache(int len, const char *name)
|
||||
*/
|
||||
void clear_lstat_cache(void)
|
||||
{
|
||||
reset_lstat_cache(0, 0);
|
||||
reset_lstat_cache();
|
||||
}
|
||||
|
||||
#define USE_ONLY_LSTAT 0
|
||||
@@ -208,9 +214,9 @@ void clear_lstat_cache(void)
|
||||
/*
|
||||
* Return non-zero if path 'name' has a leading symlink component
|
||||
*/
|
||||
int has_symlink_leading_path(int len, const char *name)
|
||||
int has_symlink_leading_path(const char *name, int len)
|
||||
{
|
||||
return lstat_cache(len, name,
|
||||
return lstat_cache(name, len,
|
||||
FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
|
||||
FL_SYMLINK;
|
||||
}
|
||||
@@ -219,9 +225,9 @@ int has_symlink_leading_path(int len, const char *name)
|
||||
* Return non-zero if path 'name' has a leading symlink component or
|
||||
* if some leading path component does not exists.
|
||||
*/
|
||||
int has_symlink_or_noent_leading_path(int len, const char *name)
|
||||
int has_symlink_or_noent_leading_path(const char *name, int len)
|
||||
{
|
||||
return lstat_cache(len, name,
|
||||
return lstat_cache(name, len,
|
||||
FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
|
||||
(FL_SYMLINK|FL_NOENT);
|
||||
}
|
||||
@@ -233,9 +239,68 @@ int has_symlink_or_noent_leading_path(int len, const char *name)
|
||||
* 'prefix_len', thus we then allow for symlinks in the prefix part as
|
||||
* long as those points to real existing directories.
|
||||
*/
|
||||
int has_dirs_only_path(int len, const char *name, int prefix_len)
|
||||
int has_dirs_only_path(const char *name, int len, int prefix_len)
|
||||
{
|
||||
return lstat_cache(len, name,
|
||||
return lstat_cache(name, len,
|
||||
FL_DIR|FL_FULLPATH, prefix_len) &
|
||||
FL_DIR;
|
||||
}
|
||||
|
||||
static struct removal_def {
|
||||
char path[PATH_MAX];
|
||||
int len;
|
||||
} removal;
|
||||
|
||||
static void do_remove_scheduled_dirs(int new_len)
|
||||
{
|
||||
while (removal.len > new_len) {
|
||||
removal.path[removal.len] = '\0';
|
||||
if (rmdir(removal.path))
|
||||
break;
|
||||
do {
|
||||
removal.len--;
|
||||
} while (removal.len > new_len &&
|
||||
removal.path[removal.len] != '/');
|
||||
}
|
||||
removal.len = new_len;
|
||||
return;
|
||||
}
|
||||
|
||||
void schedule_dir_for_removal(const char *name, int len)
|
||||
{
|
||||
int match_len, last_slash, i, previous_slash;
|
||||
|
||||
match_len = last_slash = i =
|
||||
longest_path_match(name, len, removal.path, removal.len,
|
||||
&previous_slash);
|
||||
/* Find last slash inside 'name' */
|
||||
while (i < len) {
|
||||
if (name[i] == '/')
|
||||
last_slash = i;
|
||||
i++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are about to go down the directory tree, we check if
|
||||
* we must first go upwards the tree, such that we then can
|
||||
* remove possible empty directories as we go upwards.
|
||||
*/
|
||||
if (match_len < last_slash && match_len < removal.len)
|
||||
do_remove_scheduled_dirs(match_len);
|
||||
/*
|
||||
* If we go deeper down the directory tree, we only need to
|
||||
* save the new path components as we go down.
|
||||
*/
|
||||
if (match_len < last_slash) {
|
||||
memcpy(&removal.path[match_len], &name[match_len],
|
||||
last_slash - match_len);
|
||||
removal.len = last_slash;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void remove_scheduled_dirs(void)
|
||||
{
|
||||
do_remove_scheduled_dirs(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -94,13 +94,18 @@ prepare_httpd() {
|
||||
}
|
||||
|
||||
start_httpd() {
|
||||
prepare_httpd
|
||||
prepare_httpd >&3 2>&4
|
||||
|
||||
trap 'stop_httpd; die' EXIT
|
||||
|
||||
"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
|
||||
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
|
||||
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start
|
||||
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
|
||||
>&3 2>&4
|
||||
if ! test $? = 0; then
|
||||
say "skipping test, web server setup failed"
|
||||
test_done
|
||||
fi
|
||||
}
|
||||
|
||||
stop_httpd() {
|
||||
|
||||
@@ -144,7 +144,7 @@ EOF
|
||||
|
||||
test_expect_success \
|
||||
'validate git ls-files output for a known tree.' \
|
||||
'diff current expected'
|
||||
'test_cmp expected current'
|
||||
|
||||
test_expect_success \
|
||||
'writing tree out with git write-tree.' \
|
||||
@@ -166,7 +166,7 @@ cat >expected <<\EOF
|
||||
EOF
|
||||
test_expect_success \
|
||||
'git ls-tree output for a known tree.' \
|
||||
'diff current expected'
|
||||
'test_cmp expected current'
|
||||
|
||||
# This changed in ls-tree pathspec change -- recursive does
|
||||
# not show tree nodes anymore.
|
||||
@@ -185,7 +185,7 @@ cat >expected <<\EOF
|
||||
EOF
|
||||
test_expect_success \
|
||||
'git ls-tree -r output for a known tree.' \
|
||||
'diff current expected'
|
||||
'test_cmp expected current'
|
||||
|
||||
# But with -r -t we can have both.
|
||||
test_expect_success \
|
||||
@@ -206,7 +206,7 @@ cat >expected <<\EOF
|
||||
EOF
|
||||
test_expect_success \
|
||||
'git ls-tree -r output for a known tree.' \
|
||||
'diff current expected'
|
||||
'test_cmp expected current'
|
||||
|
||||
test_expect_success \
|
||||
'writing partial tree out with git write-tree --prefix.' \
|
||||
|
||||
31
t/t1008-read-tree-overlay.sh
Executable file
31
t/t1008-read-tree-overlay.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test multi-tree read-tree without merging'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
echo one >a &&
|
||||
git add a &&
|
||||
git commit -m initial &&
|
||||
git tag initial &&
|
||||
echo two >b &&
|
||||
git add b &&
|
||||
git commit -m second &&
|
||||
git checkout -b side initial &&
|
||||
echo three >a &&
|
||||
mkdir b &&
|
||||
echo four >b/c &&
|
||||
git add b/c &&
|
||||
git commit -m third
|
||||
'
|
||||
|
||||
test_expect_success 'multi-read' '
|
||||
git read-tree initial master side &&
|
||||
(echo a; echo b/c) >expect &&
|
||||
git ls-files >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -40,6 +40,6 @@ test_expect_success \
|
||||
|
||||
test_expect_success \
|
||||
'compare commit' \
|
||||
'diff expected commit'
|
||||
'test_cmp expected commit'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -137,7 +137,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
|
||||
EOF
|
||||
test_expect_success \
|
||||
"verifying $m's log" \
|
||||
"diff expect .git/logs/$m"
|
||||
"test_cmp expect .git/logs/$m"
|
||||
rm -rf .git/$m .git/logs expect
|
||||
|
||||
test_expect_success \
|
||||
@@ -168,7 +168,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
|
||||
EOF
|
||||
test_expect_success \
|
||||
"verifying $m's log" \
|
||||
'diff expect .git/logs/$m'
|
||||
'test_cmp expect .git/logs/$m'
|
||||
rm -f .git/$m .git/logs/$m expect
|
||||
|
||||
git update-ref $m $D
|
||||
@@ -272,7 +272,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 c
|
||||
EOF
|
||||
test_expect_success \
|
||||
'git commit logged updates' \
|
||||
"diff expect .git/logs/$m"
|
||||
"test_cmp expect .git/logs/$m"
|
||||
unset h_TEST h_OTHER h_FIXED h_MERGED
|
||||
|
||||
test_expect_success \
|
||||
|
||||
@@ -43,7 +43,7 @@ test_expect_success \
|
||||
|
||||
test_expect_success \
|
||||
'git ls-files --others should pick up symlinks.' \
|
||||
'diff output expected1'
|
||||
'test_cmp expected1 output'
|
||||
|
||||
test_expect_success \
|
||||
'git ls-files --others --directory to show output.' \
|
||||
@@ -52,6 +52,6 @@ test_expect_success \
|
||||
|
||||
test_expect_success \
|
||||
'git ls-files --others --directory should not get confused.' \
|
||||
'diff output expected2'
|
||||
'test_cmp expected2 output'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -82,7 +82,7 @@ EOF
|
||||
|
||||
test_expect_success \
|
||||
'validate git ls-files -k output.' \
|
||||
'diff .output .expected'
|
||||
'test_cmp .expected .output'
|
||||
|
||||
test_expect_success \
|
||||
'git ls-files -m to show modified files.' \
|
||||
@@ -98,6 +98,6 @@ EOF
|
||||
|
||||
test_expect_success \
|
||||
'validate git ls-files -m output.' \
|
||||
'diff .output .expected'
|
||||
'test_cmp .expected .output'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -83,7 +83,7 @@ test_expect_success \
|
||||
|
||||
test_expect_success \
|
||||
'git archive vs. git tar-tree' \
|
||||
'diff b.tar b2.tar'
|
||||
'test_cmp b.tar b2.tar'
|
||||
|
||||
test_expect_success \
|
||||
'git archive in a bare repo' \
|
||||
@@ -103,12 +103,12 @@ test_expect_success \
|
||||
"$TAR" xf b.tar -C extract a/a &&
|
||||
test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime &&
|
||||
echo "1117231200" >expected.mtime &&
|
||||
diff expected.mtime b.mtime'
|
||||
test_cmp expected.mtime b.mtime'
|
||||
|
||||
test_expect_success \
|
||||
'git get-tar-commit-id' \
|
||||
'git get-tar-commit-id <b.tar >b.commitid &&
|
||||
diff .git/$(git symbolic-ref HEAD) b.commitid'
|
||||
test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
|
||||
|
||||
test_expect_success \
|
||||
'extract tar archive' \
|
||||
@@ -117,7 +117,7 @@ test_expect_success \
|
||||
test_expect_success \
|
||||
'validate filenames' \
|
||||
'(cd b/a && find .) | sort >b.lst &&
|
||||
diff a.lst b.lst'
|
||||
test_cmp a.lst b.lst'
|
||||
|
||||
test_expect_success \
|
||||
'validate file contents' \
|
||||
@@ -134,7 +134,7 @@ test_expect_success \
|
||||
test_expect_success \
|
||||
'validate filenames with prefix' \
|
||||
'(cd c/prefix/a && find .) | sort >c.lst &&
|
||||
diff a.lst c.lst'
|
||||
test_cmp a.lst c.lst'
|
||||
|
||||
test_expect_success \
|
||||
'validate file contents with prefix' \
|
||||
@@ -155,8 +155,8 @@ test_expect_success \
|
||||
'validate substfile contents' \
|
||||
'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
|
||||
>f/a/substfile1.expected &&
|
||||
diff f/a/substfile1.expected f/a/substfile1 &&
|
||||
diff a/substfile2 f/a/substfile2
|
||||
test_cmp f/a/substfile1.expected f/a/substfile1 &&
|
||||
test_cmp a/substfile2 f/a/substfile2
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
@@ -167,8 +167,8 @@ test_expect_success \
|
||||
'validate substfile contents from archive with prefix' \
|
||||
'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
|
||||
>g/prefix/a/substfile1.expected &&
|
||||
diff g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
|
||||
diff a/substfile2 g/prefix/a/substfile2
|
||||
test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
|
||||
test_cmp a/substfile2 g/prefix/a/substfile2
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
@@ -201,7 +201,7 @@ test_expect_success \
|
||||
test_expect_success \
|
||||
'validate filenames' \
|
||||
'(cd d/a && find .) | sort >d.lst &&
|
||||
diff a.lst d.lst'
|
||||
test_cmp a.lst d.lst'
|
||||
|
||||
test_expect_success \
|
||||
'validate file contents' \
|
||||
@@ -218,7 +218,7 @@ test_expect_success \
|
||||
test_expect_success \
|
||||
'validate filenames with prefix' \
|
||||
'(cd e/prefix/a && find .) | sort >e.lst &&
|
||||
diff a.lst e.lst'
|
||||
test_cmp a.lst e.lst'
|
||||
|
||||
test_expect_success \
|
||||
'validate file contents with prefix' \
|
||||
|
||||
@@ -71,6 +71,7 @@ test_expect_success 'post-checkout receives the right args when not switching br
|
||||
test $old = $new -a $flag = 0
|
||||
'
|
||||
|
||||
if test "$(git config --bool core.filemode)" = true; then
|
||||
mkdir -p templates/hooks
|
||||
cat >templates/hooks/post-checkout <<'EOF'
|
||||
#!/bin/sh
|
||||
@@ -82,5 +83,6 @@ test_expect_success 'post-checkout hook is triggered by clone' '
|
||||
git clone --template=templates . clone3 &&
|
||||
test -f clone3/.git/post-checkout.args
|
||||
'
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
||||
@@ -28,7 +28,7 @@ tokens_match () {
|
||||
}
|
||||
|
||||
check_remote_track () {
|
||||
actual=$(git remote show "$1" | sed -e '1,/Tracked/d') &&
|
||||
actual=$(git remote show "$1" | sed -ne 's|^ \(.*\) tracked$|\1|p')
|
||||
shift &&
|
||||
tokens_match "$*" "$actual"
|
||||
}
|
||||
@@ -140,47 +140,73 @@ EOF
|
||||
cat > test/expect << EOF
|
||||
* remote origin
|
||||
URL: $(pwd)/one
|
||||
Remote branch merged with 'git pull' while on branch master
|
||||
HEAD branch: master
|
||||
Remote branches:
|
||||
master new (next fetch will store in remotes/origin)
|
||||
side tracked
|
||||
Local branches configured for 'git pull':
|
||||
ahead merges with remote master
|
||||
master merges with remote master
|
||||
octopus merges with remote topic-a
|
||||
and with remote topic-b
|
||||
and with remote topic-c
|
||||
rebase rebases onto remote master
|
||||
Local refs configured for 'git push':
|
||||
master pushes to master (local out of date)
|
||||
master pushes to upstream (create)
|
||||
* remote two
|
||||
URL: ../two
|
||||
HEAD branch (remote HEAD is ambiguous, may be one of the following):
|
||||
another
|
||||
master
|
||||
New remote branch (next fetch will store in remotes/origin)
|
||||
master
|
||||
Tracked remote branches
|
||||
side
|
||||
master
|
||||
Local branches pushed with 'git push'
|
||||
master:upstream
|
||||
+refs/tags/lastbackup
|
||||
Local refs configured for 'git push':
|
||||
ahead forces to master (fast forwardable)
|
||||
master pushes to another (up to date)
|
||||
EOF
|
||||
|
||||
test_expect_success 'show' '
|
||||
(cd test &&
|
||||
git config --add remote.origin.fetch \
|
||||
refs/heads/master:refs/heads/upstream &&
|
||||
git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
|
||||
git fetch &&
|
||||
git checkout -b ahead origin/master &&
|
||||
echo 1 >> file &&
|
||||
test_tick &&
|
||||
git commit -m update file &&
|
||||
git checkout master &&
|
||||
git branch --track octopus origin/master &&
|
||||
git branch --track rebase origin/master &&
|
||||
git branch -d -r origin/master &&
|
||||
git config --add remote.two.url ../two &&
|
||||
git config branch.rebase.rebase true &&
|
||||
git config branch.octopus.merge "topic-a topic-b topic-c" &&
|
||||
(cd ../one &&
|
||||
echo 1 > file &&
|
||||
test_tick &&
|
||||
git commit -m update file) &&
|
||||
git config remote.origin.push \
|
||||
refs/heads/master:refs/heads/upstream &&
|
||||
git config --add remote.origin.push \
|
||||
+refs/tags/lastbackup &&
|
||||
git remote show origin > output &&
|
||||
git config --add remote.origin.push : &&
|
||||
git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
|
||||
git config --add remote.origin.push +refs/tags/lastbackup &&
|
||||
git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
|
||||
git config --add remote.two.push refs/heads/master:refs/heads/another &&
|
||||
git remote show origin two > output &&
|
||||
git branch -d rebase octopus &&
|
||||
test_cmp expect output)
|
||||
'
|
||||
|
||||
cat > test/expect << EOF
|
||||
* remote origin
|
||||
URL: $(pwd)/one
|
||||
Remote branch merged with 'git pull' while on branch master
|
||||
master
|
||||
Tracked remote branches
|
||||
HEAD branch: (not queried)
|
||||
Remote branches: (status not queried)
|
||||
master
|
||||
side
|
||||
Local branches pushed with 'git push'
|
||||
master:upstream
|
||||
+refs/tags/lastbackup
|
||||
Local branches configured for 'git pull':
|
||||
ahead merges with remote master
|
||||
master merges with remote master
|
||||
Local refs configured for 'git push' (status not queried):
|
||||
(matching) pushes to (matching)
|
||||
refs/heads/master pushes to refs/heads/upstream
|
||||
refs/tags/lastbackup forces to refs/tags/lastbackup
|
||||
EOF
|
||||
|
||||
test_expect_success 'show -n' '
|
||||
@@ -201,6 +227,46 @@ test_expect_success 'prune' '
|
||||
test_must_fail git rev-parse refs/remotes/origin/side)
|
||||
'
|
||||
|
||||
test_expect_success 'set-head --delete' '
|
||||
(cd test &&
|
||||
git symbolic-ref refs/remotes/origin/HEAD &&
|
||||
git remote set-head --delete origin &&
|
||||
test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
|
||||
'
|
||||
|
||||
test_expect_success 'set-head --auto' '
|
||||
(cd test &&
|
||||
git remote set-head --auto origin &&
|
||||
echo refs/remotes/origin/master >expect &&
|
||||
git symbolic-ref refs/remotes/origin/HEAD >output &&
|
||||
test_cmp expect output
|
||||
)
|
||||
'
|
||||
|
||||
cat >test/expect <<EOF
|
||||
error: Multiple remote HEAD branches. Please choose one explicitly with:
|
||||
git remote set-head two another
|
||||
git remote set-head two master
|
||||
EOF
|
||||
|
||||
test_expect_success 'set-head --auto fails w/multiple HEADs' '
|
||||
(cd test &&
|
||||
test_must_fail git remote set-head --auto two >output 2>&1 &&
|
||||
test_cmp expect output)
|
||||
'
|
||||
|
||||
cat >test/expect <<EOF
|
||||
refs/remotes/origin/side2
|
||||
EOF
|
||||
|
||||
test_expect_success 'set-head explicit' '
|
||||
(cd test &&
|
||||
git remote set-head origin side2 &&
|
||||
git symbolic-ref refs/remotes/origin/HEAD >output &&
|
||||
git remote set-head origin master &&
|
||||
test_cmp expect output)
|
||||
'
|
||||
|
||||
cat > test/expect << EOF
|
||||
Pruning origin
|
||||
URL: $(pwd)/one
|
||||
@@ -347,7 +413,7 @@ test_expect_success '"remote show" does not show symbolic refs' '
|
||||
git clone one three &&
|
||||
(cd three &&
|
||||
git remote show origin > output &&
|
||||
! grep HEAD < output &&
|
||||
! grep "^ *HEAD$" < output &&
|
||||
! grep -i stale < output)
|
||||
|
||||
'
|
||||
|
||||
@@ -191,38 +191,39 @@ test_expect_success 'bundle should be able to create a full history' '
|
||||
|
||||
'
|
||||
|
||||
test "$TEST_RSYNC" && {
|
||||
! rsync --help > /dev/null 2> /dev/null &&
|
||||
say 'Skipping rsync tests because rsync was not found' || {
|
||||
test_expect_success 'fetch via rsync' '
|
||||
git pack-refs &&
|
||||
mkdir rsynced &&
|
||||
cd rsynced &&
|
||||
git init &&
|
||||
git fetch rsync://127.0.0.1$(pwd)/../.git master:refs/heads/master &&
|
||||
git gc --prune &&
|
||||
test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
|
||||
git fsck --full
|
||||
(cd rsynced &&
|
||||
git init --bare &&
|
||||
git fetch "rsync:$(pwd)/../.git" master:refs/heads/master &&
|
||||
git gc --prune &&
|
||||
test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
|
||||
git fsck --full)
|
||||
'
|
||||
|
||||
test_expect_success 'push via rsync' '
|
||||
mkdir ../rsynced2 &&
|
||||
(cd ../rsynced2 &&
|
||||
mkdir rsynced2 &&
|
||||
(cd rsynced2 &&
|
||||
git init) &&
|
||||
git push rsync://127.0.0.1$(pwd)/../rsynced2/.git master &&
|
||||
cd ../rsynced2 &&
|
||||
git gc --prune &&
|
||||
test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
|
||||
git fsck --full
|
||||
(cd rsynced &&
|
||||
git push "rsync:$(pwd)/../rsynced2/.git" master) &&
|
||||
(cd rsynced2 &&
|
||||
git gc --prune &&
|
||||
test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
|
||||
git fsck --full)
|
||||
'
|
||||
|
||||
test_expect_success 'push via rsync' '
|
||||
cd .. &&
|
||||
mkdir rsynced3 &&
|
||||
(cd rsynced3 &&
|
||||
git init) &&
|
||||
git push --all rsync://127.0.0.1$(pwd)/rsynced3/.git &&
|
||||
cd rsynced3 &&
|
||||
test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
|
||||
git fsck --full
|
||||
git push --all "rsync:$(pwd)/rsynced3/.git" &&
|
||||
(cd rsynced3 &&
|
||||
test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
|
||||
git fsck --full)
|
||||
'
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ This test runs various sanity checks on http-push.'
|
||||
|
||||
ROOT_PATH="$PWD"
|
||||
LIB_HTTPD_DAV=t
|
||||
LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
|
||||
|
||||
if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
|
||||
then
|
||||
@@ -20,13 +21,7 @@ then
|
||||
fi
|
||||
|
||||
. "$TEST_DIRECTORY"/lib-httpd.sh
|
||||
|
||||
if ! start_httpd >&3 2>&4
|
||||
then
|
||||
say "skipping test, web server setup failed"
|
||||
test_done
|
||||
exit
|
||||
fi
|
||||
start_httpd
|
||||
|
||||
test_expect_success 'setup remote repository' '
|
||||
cd "$ROOT_PATH" &&
|
||||
|
||||
57
t/t5550-http-fetch.sh
Executable file
57
t/t5550-http-fetch.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test fetching over http'
|
||||
. ./test-lib.sh
|
||||
|
||||
if test -n "$NO_CURL"; then
|
||||
say 'skipping test, git built without http support'
|
||||
test_done
|
||||
fi
|
||||
|
||||
. "$TEST_DIRECTORY"/lib-httpd.sh
|
||||
LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
|
||||
start_httpd
|
||||
|
||||
test_expect_success 'setup repository' '
|
||||
echo content >file &&
|
||||
git add file &&
|
||||
git commit -m one
|
||||
'
|
||||
|
||||
test_expect_success 'create http-accessible bare repository' '
|
||||
mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
|
||||
(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
|
||||
git --bare init &&
|
||||
echo "exec git update-server-info" >hooks/post-update &&
|
||||
chmod +x hooks/post-update
|
||||
) &&
|
||||
git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
|
||||
git push public master:master
|
||||
'
|
||||
|
||||
test_expect_success 'clone http repository' '
|
||||
git clone $HTTPD_URL/repo.git clone &&
|
||||
test_cmp file clone/file
|
||||
'
|
||||
|
||||
test_expect_success 'fetch changes via http' '
|
||||
echo content >>file &&
|
||||
git commit -a -m two &&
|
||||
git push public
|
||||
(cd clone && git pull) &&
|
||||
test_cmp file clone/file
|
||||
'
|
||||
|
||||
test_expect_success 'http remote detects correct HEAD' '
|
||||
git push public master:other &&
|
||||
(cd clone &&
|
||||
git remote set-head origin -d &&
|
||||
git remote set-head origin -a &&
|
||||
git symbolic-ref refs/remotes/origin/HEAD > output &&
|
||||
echo refs/remotes/origin/master > expect &&
|
||||
test_cmp expect output
|
||||
)
|
||||
'
|
||||
|
||||
stop_httpd
|
||||
test_done
|
||||
@@ -88,7 +88,7 @@ cat >expected <<\EOF
|
||||
EOF
|
||||
test_expect_success \
|
||||
'Verify commandline' \
|
||||
'diff commandline1 expected'
|
||||
'test_cmp expected commandline1'
|
||||
|
||||
cat >expected-show-all-headers <<\EOF
|
||||
0001-Second.patch
|
||||
@@ -531,4 +531,15 @@ test_expect_success 'feed two files' '
|
||||
test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add master"
|
||||
'
|
||||
|
||||
test_expect_success 'in-reply-to but no threading' '
|
||||
git send-email \
|
||||
--dry-run \
|
||||
--from="Example <nobody@example.com>" \
|
||||
--to=nobody@example.com \
|
||||
--in-reply-to="<in-reply-id@example.com>" \
|
||||
--no-thread \
|
||||
$patches |
|
||||
grep "In-Reply-To: <in-reply-id@example.com>"
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
23
transport.c
23
transport.c
@@ -138,6 +138,11 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *rsync_url(const char *url)
|
||||
{
|
||||
return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
|
||||
}
|
||||
|
||||
static struct ref *get_refs_via_rsync(struct transport *transport)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
|
||||
@@ -153,7 +158,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport)
|
||||
die ("Could not make temporary directory");
|
||||
temp_dir_len = temp_dir.len;
|
||||
|
||||
strbuf_addstr(&buf, transport->url);
|
||||
strbuf_addstr(&buf, rsync_url(transport->url));
|
||||
strbuf_addstr(&buf, "/refs");
|
||||
|
||||
memset(&rsync, 0, sizeof(rsync));
|
||||
@@ -169,7 +174,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport)
|
||||
die ("Could not run rsync to get refs");
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addstr(&buf, transport->url);
|
||||
strbuf_addstr(&buf, rsync_url(transport->url));
|
||||
strbuf_addstr(&buf, "/packed-refs");
|
||||
|
||||
args[2] = buf.buf;
|
||||
@@ -206,7 +211,7 @@ static int fetch_objs_via_rsync(struct transport *transport,
|
||||
const char *args[8];
|
||||
int result;
|
||||
|
||||
strbuf_addstr(&buf, transport->url);
|
||||
strbuf_addstr(&buf, rsync_url(transport->url));
|
||||
strbuf_addstr(&buf, "/objects/");
|
||||
|
||||
memset(&rsync, 0, sizeof(rsync));
|
||||
@@ -285,7 +290,7 @@ static int rsync_transport_push(struct transport *transport,
|
||||
|
||||
/* first push the objects */
|
||||
|
||||
strbuf_addstr(&buf, transport->url);
|
||||
strbuf_addstr(&buf, rsync_url(transport->url));
|
||||
strbuf_addch(&buf, '/');
|
||||
|
||||
memset(&rsync, 0, sizeof(rsync));
|
||||
@@ -306,7 +311,8 @@ static int rsync_transport_push(struct transport *transport,
|
||||
args[i++] = NULL;
|
||||
|
||||
if (run_command(&rsync))
|
||||
return error("Could not push objects to %s", transport->url);
|
||||
return error("Could not push objects to %s",
|
||||
rsync_url(transport->url));
|
||||
|
||||
/* copy the refs to the temporary directory; they could be packed. */
|
||||
|
||||
@@ -327,10 +333,11 @@ static int rsync_transport_push(struct transport *transport,
|
||||
if (!(flags & TRANSPORT_PUSH_FORCE))
|
||||
args[i++] = "--ignore-existing";
|
||||
args[i++] = temp_dir.buf;
|
||||
args[i++] = transport->url;
|
||||
args[i++] = rsync_url(transport->url);
|
||||
args[i++] = NULL;
|
||||
if (run_command(&rsync))
|
||||
result = error("Could not push to %s", transport->url);
|
||||
result = error("Could not push to %s",
|
||||
rsync_url(transport->url));
|
||||
|
||||
if (remove_dir_recursively(&temp_dir, 0))
|
||||
warning ("Could not remove temporary directory %s.",
|
||||
@@ -723,7 +730,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
|
||||
ret->remote = remote;
|
||||
ret->url = url;
|
||||
|
||||
if (!prefixcmp(url, "rsync://")) {
|
||||
if (!prefixcmp(url, "rsync:")) {
|
||||
ret->get_refs_list = get_refs_via_rsync;
|
||||
ret->fetch = fetch_objs_via_rsync;
|
||||
ret->push = rsync_transport_push;
|
||||
|
||||
@@ -49,39 +49,20 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
|
||||
memcpy(new, ce, size);
|
||||
new->next = NULL;
|
||||
new->ce_flags = (new->ce_flags & ~clear) | set;
|
||||
add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
|
||||
add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
|
||||
}
|
||||
|
||||
/* Unlink the last component and attempt to remove leading
|
||||
* directories, in case this unlink is the removal of the
|
||||
* last entry in the directory -- empty directories are removed.
|
||||
/*
|
||||
* Unlink the last component and schedule the leading directories for
|
||||
* removal, such that empty directories get removed.
|
||||
*/
|
||||
static void unlink_entry(struct cache_entry *ce)
|
||||
{
|
||||
char *cp, *prev;
|
||||
char *name = ce->name;
|
||||
|
||||
if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
|
||||
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
|
||||
return;
|
||||
if (unlink(name))
|
||||
if (unlink(ce->name))
|
||||
return;
|
||||
prev = NULL;
|
||||
while (1) {
|
||||
int status;
|
||||
cp = strrchr(name, '/');
|
||||
if (prev)
|
||||
*prev = '/';
|
||||
if (!cp)
|
||||
break;
|
||||
|
||||
*cp = 0;
|
||||
status = rmdir(name);
|
||||
if (status) {
|
||||
*cp = '/';
|
||||
break;
|
||||
}
|
||||
prev = cp;
|
||||
}
|
||||
schedule_dir_for_removal(ce->name, ce_namelen(ce));
|
||||
}
|
||||
|
||||
static struct checkout state;
|
||||
@@ -112,11 +93,10 @@ static int check_updates(struct unpack_trees_options *o)
|
||||
display_progress(progress, ++cnt);
|
||||
if (o->update)
|
||||
unlink_entry(ce);
|
||||
remove_index_entry_at(&o->result, i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
remove_marked_cache_entries(&o->result);
|
||||
remove_scheduled_dirs();
|
||||
|
||||
for (i = 0; i < index->cache_nr; i++) {
|
||||
struct cache_entry *ce = index->cache[i];
|
||||
@@ -286,9 +266,9 @@ static int unpack_nondirectories(int n, unsigned long mask,
|
||||
if (o->merge)
|
||||
return call_unpack_fn(src, o);
|
||||
|
||||
n += o->merge;
|
||||
for (i = 0; i < n; i++)
|
||||
add_entry(o, src[i], 0, 0);
|
||||
if (src[i] && src[i] != o->df_conflict_entry)
|
||||
add_entry(o, src[i], 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -380,8 +360,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||
|
||||
memset(&o->result, 0, sizeof(o->result));
|
||||
o->result.initialized = 1;
|
||||
if (o->src_index)
|
||||
o->result.timestamp = o->src_index->timestamp;
|
||||
if (o->src_index) {
|
||||
o->result.timestamp.sec = o->src_index->timestamp.sec;
|
||||
o->result.timestamp.nsec = o->src_index->timestamp.nsec;
|
||||
}
|
||||
o->merge_size = len;
|
||||
|
||||
if (!dfc)
|
||||
@@ -446,7 +428,7 @@ static int verify_uptodate(struct cache_entry *ce,
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (o->index_only || o->reset)
|
||||
if (o->index_only || o->reset || ce_uptodate(ce))
|
||||
return 0;
|
||||
|
||||
if (!lstat(ce->name, &st)) {
|
||||
@@ -583,7 +565,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
|
||||
if (o->index_only || o->reset || !o->update)
|
||||
return 0;
|
||||
|
||||
if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
|
||||
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
|
||||
return 0;
|
||||
|
||||
if (!lstat(ce->name, &st)) {
|
||||
|
||||
Reference in New Issue
Block a user