Bring consistency to error reporting mechanism used in "refs" API.
* mh/verify-lock-error-report:
ref_transaction_commit(): do not capitalize error messages
verify_lock(): do not capitalize error messages
verify_lock(): report errors via a strbuf
verify_lock(): on errors, let the caller unlock the lock
verify_lock(): return 0/-1 rather than struct ref_lock *
--- /dev/null
+Git v2.4.3 Release Notes
+========================
+
+Fixes since v2.4.3
+------------------
+
+ * Error messages from "git branch" called remote-tracking branches as
+ "remote branches".
+
+ * "git rerere forget" in a repository without rerere enabled gave a
+ cryptic error message; it should be a silent no-op instead.
+
+ * "git pull --log" and "git pull --no-log" worked as expected, but
+ "git pull --log=20" did not.
+
+ * The pull.ff configuration was supposed to override the merge.ff
+ configuration, but it didn't.
+
+ * The code to read pack-bitmap wanted to allocate a few hundred
+ pointers to a structure, but by mistake allocated and leaked memory
+ enough to hold that many actual structures. Correct the allocation
+ size and also have it on stack, as it is small enough.
+
+ * Various documentation mark-up fixes to make the output more
+ consistent in general and also make AsciiDoctor (an alternative
+ formatter) happier.
+
+ * "git bundle verify" did not diagnose extra parameters on the
+ command line.
+
+ * Multi-ref transaction support we merged a few releases ago
+ unnecessarily kept many file descriptors open, risking to fail with
+ resource exhaustion.
+
+ * The ref API did not handle cases where 'refs/heads/xyzzy/frotz' is
+ removed at the same time as 'refs/heads/xyzzy' is added (or vice
+ versa) very well.
+
+ * The "log --decorate" enhancement in Git 2.4 that shows the commit
+ at the tip of the current branch e.g. "HEAD -> master", did not
+ work with --decorate=full.
+
+ * There was a commented-out (instead of being marked to expect
+ failure) test that documented a breakage that was fixed since the
+ test was written; turn it into a proper test.
+
+ * core.excludesfile (defaulting to $XDG_HOME/git/ignore) is supposed
+ to be overridden by repository-specific .git/info/exclude file, but
+ the order was swapped from the beginning. This belatedly fixes it.
+
+ * The connection initiation code for "ssh" transport tried to absorb
+ differences between the stock "ssh" and Putty-supplied "plink" and
+ its derivatives, but the logic to tell that we are using "plink"
+ variants were too loose and falsely triggered when "plink" appeared
+ anywhere in the path (e.g. "/home/me/bin/uplink/ssh").
+
+ * "git rebase -i" moved the "current" command from "todo" to "done" a
+ bit too prematurely, losing a step when a "pick" did not even start.
+
+ * "git add -e" did not allow the user to abort the operation by
+ killing the editor.
+
+ * Git 2.4 broke setting verbosity and progress levels on "git clone"
+ with native transports.
+
+ * Some time ago, "git blame" (incorrectly) lost the convert_to_git()
+ call when synthesizing a fake "tip" commit that represents the
+ state in the working tree, which broke folks who record the history
+ with LF line ending to make their project portabile across
+ platforms while terminating lines in their working tree files with
+ CRLF for their platform.
+
+ * Code clean-up for xdg configuration path support.
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
UI, Workflows & Features
+ * List of commands shown by "git help" are grouped along the workflow
+ elements to help early learners.
+
* "git p4" now detects the filetype (e.g. binary) correctly even when
the files are opened exclusively.
chunks from Perforce, instead of making one call to "p4 changes"
that may trigger "too many rows scanned" error from Perforce.
+ * Unlike "$EDITOR" and "$GIT_EDITOR" that can hold the path to the
+ command and initial options (e.g. "/path/to/emacs -nw"), 'git p4'
+ did not let the shell interpolate the contents of the environment
+ variable that name the editor "$P4EDITOR" (and "$EDITOR", too).
+ This release makes it in line with the rest of Git, as well as with
+ Perforce.
+
+ * A new short-hand <branch>@{push} denotes the remote-tracking branch
+ that tracks the branch at the remote the <branch> would be pushed
+ to.
+
* "git show-branch --topics HEAD" (with no other arguments) did not
do anything interesting. Instead, contrast the given revision
against all the local branches by default.
to optionally remember already seen untracked files, in order to
speed up "git status" in a working tree with tons of cruft.
+ * "git mergetool" learned to drive WinMerge as a backend.
+
+ * "git upload-pack" that serves "git fetch" can be told to serve
+ commits that are not at the tip of any ref, as long as they are
+ reachable from a ref, with uploadpack.allowReachableSHA1InWant
+ configuration variable.
+
+ * "git cat-file --batch(-check)" learned the "--follow-symlinks"
+ option that follows an in-tree symbolic link when asked about an
+ object via extended SHA-1 syntax, e.g. HEAD:RelNotes that points at
+ Documentation/RelNotes/2.5.0.txt. With the new option, the command
+ behaves as if HEAD:Documentation/RelNotes/2.5.0.txt was given as
+ input instead.
+
Performance, Internal Implementation, Development Support etc.
the semantics of the option changed back in Git 1.9 days.
(merge 19d122b pt/pull-tags-error-diag later to maint).
+ * for_each_ref() callback functions were taught to name the objects
+ not with "unsigned char sha1[20]" but with "struct object_id".
+
Also contains various documentation updates and code clean-ups.
cryptic error message; it should be a silent no-op instead.
(merge 0544574 jk/rerere-forget-check-enabled later to maint).
+ * "git rebase -i" fired post-rewrite hook when it shouldn't (namely,
+ when it was told to stop sequencing with 'exec' insn).
+ (merge 141ff8f mm/rebase-i-post-rewrite-exec later to maint).
+
+ * Clarify that "log --raw" and "log --format=raw" are unrelated
+ concepts.
+ (merge 92de921 mm/log-format-raw-doc later to maint).
+
+ * Make "git stash something --help" error out, so that users can
+ safely say "git stash drop --help".
+ (merge 5ba2831 jk/stash-options later to maint).
+
+ * The clean/smudge interface did not work well when filtering an
+ empty contents (failed and then passed the empty input through).
+ It can be argued that a filter that produces anything but empty for
+ an empty input is nonsense, but if the user wants to do strange
+ things, then why not?
+ (merge f6a1e1e jh/filter-empty-contents later to maint).
+
+ * Communication between the HTTP server and http_backend process can
+ lead to a dead-lock when relaying a large ref negotiation request.
+ Diagnose the situation better, and mitigate it by reading such a
+ request first into core (to a reasonable limit).
+ (merge 636614f jk/http-backend-deadlock later to maint).
+
+ * "git clean pathspec..." tried to lstat(2) and complain even for
+ paths outside the given pathspec.
+ (merge 838d6a9 dt/clean-pathspec-filter-then-lstat later to maint).
+
* Code cleanups and documentation updates.
(merge 0269f96 mm/usage-log-l-can-take-regex later to maint).
(merge 64f2589 nd/t1509-chroot-test later to maint).
- (merge f86a374 sb/test-bitmap-free-at-end later to maint).
+ (merge d201a1e sb/test-bitmap-free-at-end later to maint).
(merge 05bfc7d sb/line-log-plug-pairdiff-leak later to maint).
(merge 846e5df pt/xdg-config-path later to maint).
(merge 1154aa4 jc/plug-fmt-merge-msg-leak later to maint).
(merge bbf431c ps/doc-packfile-vs-pack-file later to maint).
(merge 309a9e3 jk/skip-http-tests-under-no-curl later to maint).
(merge ccd593c dl/branch-error-message later to maint).
+ (merge 22570b6 rs/janitorial later to maint).
+ (merge 5c2a581 mc/commit-doc-grammofix later to maint).
+ (merge ce41720 ah/usage-strings later to maint).
}
}
+while (<>) {
+ last if /^### command list/;
+}
+
my %cmds = ();
for (sort <>) {
next if /^#/;
color.diff.<slot>::
Use customized color for diff colorization. `<slot>` specifies
which part of the patch to use the specified color, and is one
- of `plain` (context text), `meta` (metainformation), `frag`
+ of `context` (context text - `plain` is a historical synonym),
+ `meta` (metainformation), `frag`
(hunk header), 'func' (function in hunk header), `old` (removed lines),
`new` (added lines), `commit` (commit headers), or `whitespace`
(highlighting whitespace errors).
are under the hierarchies listed on the value of this
variable is excluded, and is hidden from `git ls-remote`,
`git fetch`, etc. An attempt to fetch a hidden ref by `git
- fetch` will fail. See also `uploadpack.allowtipsha1inwant`.
+ fetch` will fail. See also `uploadpack.allowTipSHA1InWant`.
-uploadpack.allowtipsha1inwant::
+uploadpack.allowTipSHA1InWant::
When `uploadpack.hideRefs` is in effect, allow `upload-pack`
to accept a fetch request that asks for an object at the tip
of a hidden ref (by default, such a request is rejected).
see also `uploadpack.hideRefs`.
+uploadpack.allowReachableSHA1InWant::
+ Allow `upload-pack` to accept a fetch request that asks for an
+ object that is reachable from any ref tip. However, note that
+ calculating object reachability is computationally expensive.
+ Defaults to `false`.
+
uploadpack.keepAlive::
When `upload-pack` has started `pack-objects`, there may be a
quiet period while `pack-objects` prepares the pack. Normally
ifndef::git-format-patch[]
--raw::
- Generate the raw format.
+ifndef::git-log[]
+ Generate the diff in raw format.
ifdef::git-diff-core[]
This is the default.
endif::git-diff-core[]
+endif::git-log[]
+ifdef::git-log[]
+ For each commit, show a summary of changes using the raw diff
+ format. See the "RAW OUTPUT FORMAT" section of
+ linkgit:git-diff[1]. This is different from showing the log
+ itself in raw format, which you can achieve with
+ `--format=raw`.
+endif::git-log[]
endif::git-format-patch[]
ifndef::git-format-patch[]
initial indent of the line are considered whitespace errors.
Exits with non-zero status if problems are found. Not compatible
with --exit-code.
+
+--ws-error-highlight=<kind>::
+ Highlight whitespace errors on lines specified by <kind>
+ in the color specified by `color.diff.whitespace`. <kind>
+ is a comma separated list of `old`, `new`, `context`. When
+ this option is not given, only whitespace errors in `new`
+ lines are highlighted. E.g. `--ws-error-highlight=new,old`
+ highlights whitespace errors on both deleted and added lines.
+ `all` can be used as a short-hand for `old,new,context`.
+
endif::git-format-patch[]
--full-index::
--------
[verse]
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv ) <object>
-'git cat-file' (--batch | --batch-check) < <list-of-objects>
+'git cat-file' (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>
DESCRIPTION
-----------
--allow-unknown-type::
Allow -s or -t to query broken/corrupt objects of unknown type.
+--follow-symlinks::
+ With --batch or --batch-check, follow symlinks inside the
+ repository when requesting objects with extended SHA-1
+ expressions of the form tree-ish:path-in-tree. Instead of
+ providing output about the link itself, provide output about
+ the linked-to object. If a symlink points outside the
+ tree-ish (e.g. a link to /foo or a root-level link to ../foo),
+ the portion of the link which is outside the tree will be
+ printed.
++
+This option does not (currently) work correctly when an object in the
+index is specified (e.g. `:link` instead of `HEAD:link`) rather than
+one in the tree.
++
+This option cannot (currently) be used unless `--batch` or
+`--batch-check` is used.
++
+For example, consider a git repository containing:
++
+--
+ f: a file containing "hello\n"
+ link: a symlink to f
+ dir/link: a symlink to ../f
+ plink: a symlink to ../f
+ alink: a symlink to /etc/passwd
+--
++
+For a regular file `f`, `echo HEAD:f | git cat-file --batch` would print
++
+--
+ ce013625030ba8dba906f756967f9e9ca394464a blob 6
+--
++
+And `echo HEAD:link | git cat-file --batch --follow-symlinks` would
+print the same thing, as would `HEAD:dir/link`, as they both point at
+`HEAD:f`.
++
+Without `--follow-symlinks`, these would print data about the symlink
+itself. In the case of `HEAD:link`, you would see
++
+--
+ 4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
+--
++
+Both `plink` and `alink` point outside the tree, so they would
+respectively print:
++
+--
+ symlink 4
+ ../f
+
+ symlink 11
+ /etc/passwd
+--
+
+
OUTPUT
------
If '-t' is specified, one of the <type>.
<object> SP missing LF
------------
+If --follow-symlinks is used, and a symlink in the repository points
+outside the repository, then `cat-file` will ignore any custom format
+and print:
+
+------------
+symlink SP <size> LF
+<symlink> LF
+------------
+
+The symlink will either be absolute (beginning with a /), or relative
+to the tree root. For instance, if dir/link points to ../../foo, then
+<symlink> will be ../foo. <size> is the size of the symlink in bytes.
+
+If --follow-symlinks is used, the following error messages will be
+displayed:
+
+------------
+<object> SP missing LF
+------------
+is printed when the initial symlink requested does not exist.
+
+------------
+dangling SP <size> LF
+<object> LF
+------------
+is printed when the initial symlink exists, but something that
+it (transitive-of) points to does not.
+
+------------
+loop SP <size> LF
+<object> LF
+------------
+is printed for symlink loops (or any symlinks that
+require more than 40 link resolutions to resolve).
+
+------------
+notdir SP <size> LF
+<object> LF
+------------
+is printed when, during symlink resolution, a file is used as a
+directory name.
CAVEATS
-------
--reset-author::
When used with -C/-c/--amend options, or when committing after a
a conflicting cherry-pick, declare that the authorship of the
- resulting commit now belongs of the committer. This also renews
+ resulting commit now belongs to the committer. This also renews
the author timestamp.
--short::
or "=" (in sync). Has no effect if the ref does not have
tracking information associated with it.
+push::
+ The name of a local ref which represents the `@{push}` location
+ for the displayed ref. Respects `:short`, `:track`, and
+ `:trackshort` options as `upstream` does. Produces an empty
+ string if no `@{push}` ref is configured.
+
HEAD::
'*' if HEAD matches current ref (the checked out branch), ' '
otherwise.
'git-http-backend' to bypass the check for the "git-daemon-export-ok"
file in each repository before allowing export of that repository.
+The `GIT_HTTP_MAX_REQUEST_BUFFER` environment variable (or the
+`http.maxRequestBuffer` config variable) may be set to change the
+largest ref negotiation request that git will handle during a fetch; any
+fetch requiring a larger buffer will not succeed. This value should not
+normally need to be changed, but may be helpful if you are fetching from
+a repository with an extremely large number of refs. The value can be
+specified with a unit (e.g., `100M` for 100 megabytes). The default is
+10 megabytes.
+
The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and
GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}',
ensuring that any reflogs created by 'git-receive-pack' contain some
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v2.4.2/git.html[documentation for release 2.4.2]
+* link:v2.4.3/git.html[documentation for release 2.4.3]
* release notes for
+ link:RelNotes/2.4.3.txt[2.4.3],
link:RelNotes/2.4.2.txt[2.4.2],
link:RelNotes/2.4.1.txt[2.4.1],
link:RelNotes/2.4.0.txt[2.4].
that categorizes commands by type, so they can be listed in appropriate
subsections in the documentation's summary command list. Add an entry
for yours. To understand the categories, look at git-commands.txt
-in the main directory.
+in the main directory. If the new command is part of the typical Git
+workflow and you believe it common enough to be mentioned in 'git help',
+map this command to a common group in the column [common].
7. Give the maintainer one paragraph to include in the RelNotes file
to describe the new feature; a good place to do so is in the cover
displayed in full, regardless of whether --abbrev or
--no-abbrev are used, and 'parents' information show the
true parent commits, without taking grafts or history
-simplification into account.
+simplification into account. Note that this format affects the way
+commits are displayed, but not the way the diff is shown e.g. with
+`git log --raw`. To get full object names in a raw diff format,
+use `--no-abbrev`.
* 'format:<string>'
+
`branch.<name>.merge`). A missing branchname defaults to the
current one.
+'<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
+ The suffix '@\{push}' reports the branch "where we would push to" if
+ `git push` were run while `branchname` was checked out (or the current
+ 'HEAD' if no branchname is specified). Since our push destination is
+ in a remote repository, of course, we report the local tracking branch
+ that corresponds to that branch (i.e., something in 'refs/remotes/').
++
+Here's an example to make it more clear:
++
+------------------------------
+$ git config push.default current
+$ git config remote.pushdefault myfork
+$ git checkout -b mybranch origin/master
+
+$ git rev-parse --symbolic-full-name @{upstream}
+refs/remotes/origin/master
+
+$ git rev-parse --symbolic-full-name @{push}
+refs/remotes/myfork/mybranch
+------------------------------
++
+Note in the example that we set up a triangular workflow, where we pull
+from one location and push to another. In a non-triangular workflow,
+'@\{push}' is the same as '@\{upstream}', and there is no need for it.
+
'<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
A suffix '{caret}' to a revision parameter means the first parent of
that commit object. '{caret}<n>' means the <n>th parent (i.e.
callback function for every ref. The callback function has this
signature:
- int handle_one_ref(const char *refname, const unsigned char *sha1,
+ int handle_one_ref(const char *refname, const struct object_id *oid,
int flags, void *cb_data);
There are different kinds of iterate functions which all take a
The name of the remote listed in the configuration.
-`remote`::
-
- The struct remote for that remote.
-
`merge_name`::
An array of the "merge" lines in the configuration.
Clients MUST send at least one "want" command in the request body.
Clients MUST NOT reference an id in a "want" command which did not
appear in the response obtained through ref discovery unless the
-server advertises capability `allow-tip-sha1-in-want`.
+server advertises capability `allow-tip-sha1-in-want` or
+`allow-reachable-sha1-in-want`.
compute_request = want_list
have_list
send "want" lines with SHA-1s that exist at the server but are not
advertised by upload-pack.
+allow-reachable-sha1-in-want
+----------------------------
+
+If the upload-pack server advertises this capability, fetch-pack may
+send "want" lines with SHA-1s that exist at the server but are not
+advertised by upload-pack.
+
push-cert=<nonce>
-----------------
ln -s $< $@ 2>/dev/null || \
cp $< $@
-common-cmds.h: ./generate-cmdlist.sh command-list.txt
+common-cmds.h: generate-cmdlist.perl command-list.txt
common-cmds.h: $(wildcard Documentation/git-*.txt)
- $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
+ $(QUIET_GEN)$(PERL_PATH) generate-cmdlist.perl command-list.txt > $@+ && mv $@+ $@
SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
$(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
esac ; \
test -f "Documentation/$$v.txt" || \
echo "no doc: $$v"; \
- sed -e '/^#/d' command-list.txt | \
+ sed -e '1,/^### command list/d' -e '/^#/d' command-list.txt | \
grep -q "^$$v[ ]" || \
case "$$v" in \
git) ;; \
esac ; \
done; \
( \
- sed -e '/^#/d' \
+ sed -e '1,/^### command list/d' \
+ -e '/^#/d' \
-e 's/[ ].*//' \
-e 's/^/listed /' command-list.txt; \
$(MAKE) -C Documentation print-man1 | \
return best;
}
-static int register_ref(const char *refname, const unsigned char *sha1,
+static int register_ref(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
if (!strcmp(refname, "bad")) {
current_bad_oid = xmalloc(sizeof(*current_bad_oid));
- hashcpy(current_bad_oid->hash, sha1);
+ oidcpy(current_bad_oid, oid);
} else if (starts_with(refname, "good-")) {
- sha1_array_append(&good_revs, sha1);
+ sha1_array_append(&good_revs, oid->hash);
} else if (starts_with(refname, "skip-")) {
- sha1_array_append(&skipped_revs, sha1);
+ sha1_array_append(&skipped_revs, oid->hash);
}
return 0;
#include "userdiff.h"
#include "line-range.h"
#include "line-log.h"
+#include "dir.h"
-static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] file");
+static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
static const char *blame_opt_usage[] = {
blame_usage,
}
}
-/*
- * Used for the command line parsing; check if the path exists
- * in the working tree.
- */
-static int has_string_in_work_tree(const char *path)
-{
- struct stat st;
- return !lstat(path, &st);
-}
-
static unsigned parse_score(const char *arg)
{
char *end;
if (argc < 2)
usage_with_options(blame_opt_usage, options);
path = add_prefix(prefix, argv[argc - 1]);
- if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
+ if (argc == 3 && !file_exists(path)) { /* (2b) */
path = add_prefix(prefix, argv[1]);
argv[1] = argv[2];
}
argv[argc - 1] = "--";
setup_work_tree();
- if (!has_string_in_work_tree(path))
+ if (!file_exists(path))
die_errno("cannot stat path '%s'", path);
}
if (kind == REF_LOCAL_BRANCH) {
struct branch *branch = branch_get(name);
+ const char *upstream = branch_get_upstream(branch, NULL);
unsigned char sha1[20];
- if (branch &&
- branch->merge &&
- branch->merge[0] &&
- branch->merge[0]->dst &&
+ if (upstream &&
(reference_name = reference_name_to_free =
- resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING,
+ resolve_refdup(upstream, RESOLVE_REF_READING,
sha1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
return 0;
}
-static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data)
{
struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
struct ref_list *ref_list = cb->ref_list;
commit = NULL;
if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid->hash, 1);
if (!commit) {
cb->ret = error(_("branch '%s' does not point at a commit"), refname);
return 0;
int ours, theirs;
char *ref = NULL;
struct branch *branch = branch_get(branch_name);
+ const char *upstream;
struct strbuf fancy = STRBUF_INIT;
int upstream_is_gone = 0;
int added_decoration = 1;
- switch (stat_tracking_info(branch, &ours, &theirs)) {
- case 0:
- /* no base */
- return;
- case -1:
- /* with "gone" base */
+ if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
+ if (!upstream)
+ return;
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
if (show_upstream_ref) {
- ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+ ref = shorten_unambiguous_ref(upstream, 0);
if (want_color(branch_use_color))
strbuf_addf(&fancy, "%s%s%s",
branch_get_color(BRANCH_COLOR_UPSTREAM),
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
+#include "tree-walk.h"
static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
int unknown_type)
struct batch_options {
int enabled;
+ int follow_symlinks;
int print_contents;
const char *format;
};
struct expand_data *data)
{
struct strbuf buf = STRBUF_INIT;
+ struct object_context ctx;
+ int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0;
+ enum follow_symlinks_result result;
if (!obj_name)
return 1;
- if (get_sha1(obj_name, data->sha1)) {
- printf("%s missing\n", obj_name);
+ result = get_sha1_with_context(obj_name, flags, data->sha1, &ctx);
+ if (result != FOUND) {
+ switch (result) {
+ case MISSING_OBJECT:
+ printf("%s missing\n", obj_name);
+ break;
+ case DANGLING_SYMLINK:
+ printf("dangling %"PRIuMAX"\n%s\n",
+ (uintmax_t)strlen(obj_name), obj_name);
+ break;
+ case SYMLINK_LOOP:
+ printf("loop %"PRIuMAX"\n%s\n",
+ (uintmax_t)strlen(obj_name), obj_name);
+ break;
+ case NOT_DIR:
+ printf("notdir %"PRIuMAX"\n%s\n",
+ (uintmax_t)strlen(obj_name), obj_name);
+ break;
+ default:
+ die("BUG: unknown get_sha1_with_context result %d\n",
+ result);
+ break;
+ }
+ fflush(stdout);
+ return 0;
+ }
+
+ if (ctx.mode == 0) {
+ printf("symlink %"PRIuMAX"\n%s\n",
+ (uintmax_t)ctx.symlink_path.len,
+ ctx.symlink_path.buf);
fflush(stdout);
return 0;
}
static const char * const cat_file_usage[] = {
N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv) <object>"),
- N_("git cat-file (--batch | --batch-check) < <list-of-objects>"),
+ N_("git cat-file (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>"),
NULL
};
{
struct batch_options *bo = opt->value;
- if (unset) {
- memset(bo, 0, sizeof(*bo));
- return 0;
+ if (bo->enabled) {
+ return 1;
}
bo->enabled = 1;
{ OPTION_CALLBACK, 0, "batch-check", &batch, "format",
N_("show info about objects fed from the standard input"),
PARSE_OPT_OPTARG, batch_option_callback },
+ OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
+ N_("follow in-tree symlinks (used with --batch or --batch-check)")),
OPT_END()
};
usage_with_options(cat_file_usage, options);
}
+ if (batch.follow_symlinks && !batch.enabled) {
+ usage_with_options(cat_file_usage, options);
+ }
+
if (batch.enabled)
return batch_objects(&batch);
}
static int add_pending_uninteresting_ref(const char *refname,
- const unsigned char *sha1,
+ const struct object_id *oid,
int flags, void *cb_data)
{
- add_pending_sha1(cb_data, refname, sha1, UNINTERESTING);
+ add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
return 0;
}
{
struct string_list menu_list = STRING_LIST_INIT_DUP;
struct strbuf menu = STRBUF_INIT;
- struct strbuf buf = STRBUF_INIT;
struct menu_item *menu_item;
struct string_list_item *string_list_item;
int i;
pretty_print_menus(&menu_list);
strbuf_release(&menu);
- strbuf_release(&buf);
string_list_clear(&menu_list, 0);
}
if (!cache_name_is_other(ent->name, ent->len))
continue;
- if (lstat(ent->name, &st))
- die_errno("Cannot lstat '%s'", ent->name);
-
if (pathspec.nr)
matches = dir_path_match(ent, &pathspec, 0, NULL);
if (pathspec.nr && !matches)
continue;
+ if (lstat(ent->name, &st))
+ die_errno("Cannot lstat '%s'", ent->name);
+
if (S_ISDIR(st.st_mode) && !remove_directories &&
matches != MATCHED_EXACTLY)
continue;
static struct string_list option_reference;
static int option_dissociate;
-static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
-{
- struct string_list *option_reference = opt->value;
- if (!arg)
- return -1;
- string_list_append(option_reference, arg);
- return 0;
-}
-
static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
OPT_BOOL(0, "progress", &option_progress,
N_("initialize submodules in the clone")),
OPT_STRING(0, "template", &option_template, N_("template-directory"),
N_("directory from which templates will be used")),
- OPT_CALLBACK(0 , "reference", &option_reference, N_("repo"),
- N_("reference repository"), &opt_parse_reference),
+ OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"),
+ N_("reference repository")),
+ OPT_BOOL(0, "dissociate", &option_dissociate,
+ N_("use --reference only while cloning")),
OPT_STRING('o', "origin", &option_origin, N_("name"),
N_("use <name> instead of 'origin' to track upstream")),
OPT_STRING('b', "branch", &option_branch, N_("branch"),
N_("create a shallow clone of that depth")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
- OPT_BOOL(0, "dissociate", &option_dissociate,
- N_("use --reference only while cloning")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
}
}
-static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
{
int is_tag = starts_with(path, "refs/tags/");
- unsigned char peeled[20];
+ struct object_id peeled;
int is_annotated, prio;
/* Reject anything outside refs/tags/ unless --all */
return 0;
/* Is it annotated? */
- if (!peel_ref(path, peeled)) {
- is_annotated = !!hashcmp(sha1, peeled);
+ if (!peel_ref(path, peeled.hash)) {
+ is_annotated = !!oidcmp(oid, &peeled);
} else {
- hashcpy(peeled, sha1);
+ oidcpy(&peeled, oid);
is_annotated = 0;
}
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, peeled, prio, sha1);
+ add_to_known_names(all ? path + 5 : path + 10, peeled.hash, prio, oid->hash);
return 0;
}
}
}
-static int add_existing(const char *refname, const unsigned char *sha1,
+static int add_existing(const char *refname, const struct object_id *oid,
int flag, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
struct string_list_item *item = string_list_insert(list, refname);
- item->util = xmalloc(20);
- hashcpy(item->util, sha1);
+ struct object_id *old_oid = xmalloc(sizeof(*old_oid));
+
+ oidcpy(old_oid, oid);
+ item->util = old_oid;
return 0;
}
struct string_list_item *peer_item =
string_list_lookup(&existing_refs,
rm->peer_ref->name);
- if (peer_item)
- hashcpy(rm->peer_ref->old_sha1,
- peer_item->util);
+ if (peer_item) {
+ struct object_id *old_oid = peer_item->util;
+ hashcpy(rm->peer_ref->old_sha1, old_oid->hash);
+ }
}
}
{ "contents:body" },
{ "contents:signature" },
{ "upstream" },
+ { "push" },
{ "symref" },
{ "flag" },
{ "HEAD" },
else if (starts_with(name, "symref"))
refname = ref->symref ? ref->symref : "";
else if (starts_with(name, "upstream")) {
+ const char *branch_name;
/* only local branches may have an upstream */
- if (!starts_with(ref->refname, "refs/heads/"))
+ if (!skip_prefix(ref->refname, "refs/heads/",
+ &branch_name))
continue;
- branch = branch_get(ref->refname + 11);
+ branch = branch_get(branch_name);
- if (!branch || !branch->merge || !branch->merge[0] ||
- !branch->merge[0]->dst)
+ refname = branch_get_upstream(branch, NULL);
+ if (!refname)
+ continue;
+ } else if (starts_with(name, "push")) {
+ const char *branch_name;
+ if (!skip_prefix(ref->refname, "refs/heads/",
+ &branch_name))
+ continue;
+ branch = branch_get(branch_name);
+
+ refname = branch_get_push(branch, NULL);
+ if (!refname)
continue;
- refname = branch->merge[0]->dst;
} else if (starts_with(name, "color:")) {
char color[COLOR_MAXLEN] = "";
refname = shorten_unambiguous_ref(refname,
warn_ambiguous_refs);
else if (!strcmp(formatp, "track") &&
- starts_with(name, "upstream")) {
+ (starts_with(name, "upstream") ||
+ starts_with(name, "push"))) {
char buf[40];
if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
+ &num_theirs, NULL))
continue;
if (!num_ours && !num_theirs)
}
continue;
} else if (!strcmp(formatp, "trackshort") &&
- starts_with(name, "upstream")) {
+ (starts_with(name, "upstream") ||
+ starts_with(name, "push"))) {
assert(branch);
if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
+ &num_theirs, NULL))
continue;
if (!num_ours && !num_theirs)
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int grab_single_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct grab_ref_cbdata *cb = cb_data;
struct refinfo *ref;
*/
ref = xcalloc(1, sizeof(*ref));
ref->refname = xstrdup(refname);
- hashcpy(ref->objectname, sha1);
+ hashcpy(ref->objectname, oid->hash);
ref->flag = flag;
cnt = cb->grab_cnt;
static int check_full = 1;
static int check_strict;
static int keep_cache_objects;
-static unsigned char head_sha1[20];
+static struct object_id head_oid;
static const char *head_points_at;
static int errors_found;
static int write_lost_and_found;
return 0;
}
-static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
+static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
+ int flag, void *cb_data)
{
for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);
return 0;
}
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int fsck_handle_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct object *obj;
- obj = parse_object(sha1);
+ obj = parse_object(oid->hash);
if (!obj) {
- error("%s: invalid sha1 pointer %s", refname, sha1_to_hex(sha1));
+ error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
static void get_default_heads(void)
{
- if (head_points_at && !is_null_sha1(head_sha1))
- fsck_handle_ref("HEAD", head_sha1, 0, NULL);
+ if (head_points_at && !is_null_oid(&head_oid))
+ fsck_handle_ref("HEAD", &head_oid, 0, NULL);
for_each_rawref(fsck_handle_ref, NULL);
if (include_reflogs)
for_each_reflog(fsck_handle_reflog, NULL);
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", 0, head_sha1, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
else if (!starts_with(head_points_at, "refs/heads/"))
return error("HEAD points to something strange (%s)",
head_points_at);
- if (is_null_sha1(head_sha1)) {
+ if (is_null_oid(&head_oid)) {
if (null_is_error)
return error("HEAD: detached HEAD points at nothing");
fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
static const char *fmt_pretty;
static const char * const builtin_log_usage[] = {
- N_("git log [<options>] [<revision range>] [[--] <path>...]"),
+ N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
N_("git show [<options>] <object>..."),
NULL
};
break;
default:
current_branch = branch_get(NULL);
- if (!current_branch || !current_branch->merge
- || !current_branch->merge[0]
- || !current_branch->merge[0]->dst) {
+ upstream = branch_get_upstream(current_branch, NULL);
+ if (!upstream) {
fprintf(stderr, _("Could not find a tracked"
" remote branch, please"
" specify <upstream> manually.\n"));
usage_with_options(cherry_usage, options);
}
-
- upstream = current_branch->merge[0]->dst;
}
init_revisions(&revs, prefix);
if (!branch)
die(_("No current branch."));
- if (!branch->remote)
+ if (!branch->remote_name)
die(_("No remote for the current branch."));
if (!branch->merge_nr)
die(_("No default upstream defined for the current branch."));
return hashcmp(a->sha1, b->sha1);
}
-static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
+static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
{
- struct object *o = parse_object(sha1);
+ struct object *o = parse_object(oid->hash);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
}
}
- add_to_tip_table(sha1, path, can_abbreviate_output);
+ add_to_tip_table(oid->hash, path, can_abbreviate_output);
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
return WRITE_ONE_WRITTEN;
}
-static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
+static int mark_tagged(const char *path, const struct object_id *oid, int flag,
void *cb_data)
{
unsigned char peeled[20];
- struct object_entry *entry = packlist_find(&to_pack, sha1, NULL);
+ struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
if (entry)
entry->tagged = 1;
#define ll_find_deltas(l, s, w, d, p) find_deltas(l, &s, w, d, p)
#endif
-static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data)
{
- unsigned char peeled[20];
+ struct object_id peeled;
if (starts_with(path, "refs/tags/") && /* is a tag? */
- !peel_ref(path, peeled) && /* peelable? */
- packlist_find(&to_pack, peeled, NULL)) /* object packed? */
- add_object_entry(sha1, OBJ_TAG, NULL, 0);
+ !peel_ref(path, peeled.hash) && /* peelable? */
+ packlist_find(&to_pack, peeled.hash, NULL)) /* object packed? */
+ add_object_entry(oid->hash, OBJ_TAG, NULL, 0);
return 0;
}
}
}
-static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *unused)
+static int show_ref_cb(const char *path, const struct object_id *oid, int flag, void *unused)
{
path = strip_namespace(path);
/*
*/
if (!path)
path = ".have";
- show_ref(path, sha1);
+ show_ref(path, oid->hash);
return 0;
}
static void write_head_info(void)
{
struct sha1_array sa = SHA1_ARRAY_INIT;
+
for_each_alternate_ref(collect_one_alternate_ref, &sa);
sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL);
sha1_array_clear(&sa);
return 0;
}
-static int push_tip_to_list(const char *refname, const unsigned char *sha1,
+static int push_tip_to_list(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
struct commit_list **list = cb_data;
struct commit *tip_commit;
if (flags & REF_ISSYMREF)
return 0;
- tip_commit = lookup_commit_reference_gently(sha1, 1);
+ tip_commit = lookup_commit_reference_gently(oid->hash, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);
if (cb->unreachable_expire_kind != UE_ALWAYS) {
if (cb->unreachable_expire_kind == UE_HEAD) {
struct commit_list *elem;
+
for_each_ref(push_tip_to_list, &cb->tips);
for (elem = cb->tips; elem; elem = elem->next)
commit_list_insert(elem->item, &cb->mark_list);
}
}
-static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
+static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
{
struct collected_reflog *e;
struct collect_reflog_cb *cb = cb_data;
size_t namelen = strlen(ref);
e = xmalloc(sizeof(*e) + namelen + 1);
- hashcpy(e->sha1, sha1);
+ hashcpy(e->sha1, oid->hash);
memcpy(e->reflog, ref, namelen + 1);
ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
cb->e[cb->nr++] = e;
};
static int add_branch_for_removal(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
+ const struct object_id *oid, int flags, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec refspec;
- struct string_list_item *item;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
if (flags & REF_ISSYMREF)
return unlink(git_path("%s", refname));
- item = string_list_append(branches->branches, refname);
- item->util = xmalloc(20);
- hashcpy(item->util, sha1);
+ string_list_append(branches->branches, refname);
return 0;
}
};
static int read_remote_branches(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
+ const struct object_id *oid, int flags, void *cb_data)
{
struct rename_info *rename = cb_data;
struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;
int flag;
- unsigned char orig_sha1[20];
+ struct object_id orig_oid;
const char *symref;
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
- orig_sha1, &flag);
+ orig_oid.hash, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;
int flag = 0;
- unsigned char sha1[20];
+ struct object_id oid;
- read_ref_full(item->string, RESOLVE_REF_READING, sha1, &flag);
+ read_ref_full(item->string, RESOLVE_REF_READING, oid.hash, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
if (!result)
result = remove_branches(&branches);
- string_list_clear(&branches, 1);
+ string_list_clear(&branches, 0);
if (skipped.nr) {
fprintf_ln(stderr,
}
static int append_ref_to_tracked_list(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
+ const struct object_id *oid, int flags, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec refspec;
enum replace_format format;
};
-static int show_reference(const char *refname, const unsigned char *sha1,
+static int show_reference(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
{
struct show_data *data = cb_data;
if (data->format == REPLACE_FORMAT_SHORT)
printf("%s\n", refname);
else if (data->format == REPLACE_FORMAT_MEDIUM)
- printf("%s -> %s\n", refname, sha1_to_hex(sha1));
+ printf("%s -> %s\n", refname, oid_to_hex(oid));
else { /* data->format == REPLACE_FORMAT_LONG */
- unsigned char object[20];
+ struct object_id object;
enum object_type obj_type, repl_type;
- if (get_sha1(refname, object))
+ if (get_sha1(refname, object.hash))
return error("Failed to resolve '%s' as a valid ref.", refname);
- obj_type = sha1_object_info(object, NULL);
- repl_type = sha1_object_info(sha1, NULL);
+ obj_type = sha1_object_info(object.hash, NULL);
+ repl_type = sha1_object_info(oid->hash, NULL);
printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
- sha1_to_hex(sha1), typename(repl_type));
+ oid_to_hex(oid), typename(repl_type));
}
}
"valid formats are 'short', 'medium' and 'long'\n",
format);
- for_each_replace_ref(show_reference, (void *) &data);
+ for_each_replace_ref(show_reference, (void *)&data);
return 0;
}
return 0;
}
-static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
{
if (ref_excluded(ref_excludes, refname))
return 0;
- show_rev(NORMAL, sha1, refname);
+ show_rev(NORMAL, oid->hash, refname);
return 0;
}
-static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
{
- show_rev(REVERSED, sha1, refname);
+ show_rev(REVERSED, oid->hash, refname);
return 0;
}
const char *name = list.entry[i].name;
int pos;
const struct cache_entry *ce;
- struct stat st;
pos = cache_name_pos(name, strlen(name));
if (pos < 0) {
ce = active_cache[pos];
if (!S_ISGITLINK(ce->ce_mode) ||
- (lstat(ce->name, &st) < 0) ||
+ !file_exists(ce->name) ||
is_empty_dir(name))
continue;
compare_ref_name);
}
-static int append_ref(const char *refname, const unsigned char *sha1,
+static int append_ref(const char *refname, const struct object_id *oid,
int allow_dups)
{
- struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+ struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
int i;
if (!commit)
return 0;
}
-static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_head_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
- unsigned char tmp[20];
+ struct object_id tmp;
int ofs = 11;
if (!starts_with(refname, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
+ if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
ofs = 5;
- return append_ref(refname + ofs, sha1, 0);
+ return append_ref(refname + ofs, oid, 0);
}
-static int append_remote_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_remote_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
- unsigned char tmp[20];
+ struct object_id tmp;
int ofs = 13;
if (!starts_with(refname, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
+ if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
ofs = 5;
- return append_ref(refname + ofs, sha1, 0);
+ return append_ref(refname + ofs, oid, 0);
}
-static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_tag_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
if (!starts_with(refname, "refs/tags/"))
return 0;
- return append_ref(refname + 5, sha1, 0);
+ return append_ref(refname + 5, oid, 0);
}
static const char *match_ref_pattern = NULL;
return cnt;
}
-static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_matching_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
if (wildmatch(match_ref_pattern, tail, 0, NULL))
return 0;
if (starts_with(refname, "refs/heads/"))
- return append_head_ref(refname, sha1, flag, cb_data);
+ return append_head_ref(refname, oid, flag, cb_data);
if (starts_with(refname, "refs/tags/"))
- return append_tag_ref(refname, sha1, flag, cb_data);
- return append_ref(refname, sha1, 0);
+ return append_tag_ref(refname, oid, flag, cb_data);
+ return append_ref(refname, oid, 0);
}
static void snarf_refs(int head, int remotes)
{
if (head) {
int orig_cnt = ref_name_cnt;
+
for_each_ref(append_head_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
if (remotes) {
int orig_cnt = ref_name_cnt;
+
for_each_ref(append_remote_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
static void append_one_rev(const char *av)
{
- unsigned char revkey[20];
- if (!get_sha1(av, revkey)) {
- append_ref(av, revkey, 0);
+ struct object_id revkey;
+ if (!get_sha1(av, revkey.hash)) {
+ append_ref(av, &revkey, 0);
return;
}
if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
/* glob style match */
int saved_matches = ref_name_cnt;
+
match_ref_pattern = av;
match_ref_slash = count_slash(av);
for_each_ref(append_matching_ref, NULL);
char head[128];
const char *head_p;
int head_len;
- unsigned char head_sha1[20];
+ struct object_id head_oid;
int merge_base = 0;
int independent = 0;
int no_name = 0;
all_heads = 1;
if (reflog) {
- unsigned char sha1[20];
+ struct object_id oid;
char nth_desc[256];
char *ref;
int base = 0;
fake_av[0] = resolve_refdup("HEAD",
RESOLVE_REF_READING,
- sha1, NULL);
+ oid.hash, NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
if (MAX_REVS < reflog)
die("Only %d entries can be shown at one time.",
MAX_REVS);
- if (!dwim_ref(*av, strlen(*av), sha1, &ref))
+ if (!dwim_ref(*av, strlen(*av), oid.hash, &ref))
die("No such ref %s", *av);
/* Has the base been specified? */
/* Ah, that is a date spec... */
unsigned long at;
at = approxidate(reflog_base);
- read_ref_at(ref, flags, at, -1, sha1, NULL,
+ read_ref_at(ref, flags, at, -1, oid.hash, NULL,
NULL, NULL, &base);
}
}
unsigned long timestamp;
int tz;
- if (read_ref_at(ref, flags, 0, base+i, sha1, &logmsg,
+ if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
×tamp, &tz, NULL)) {
reflog = i;
break;
msg);
free(logmsg);
sprintf(nth_desc, "%s@{%d}", *av, base+i);
- append_ref(nth_desc, sha1, 1);
+ append_ref(nth_desc, &oid, 1);
}
free(ref);
}
}
head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
- head_sha1, NULL);
+ head_oid.hash, NULL);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
if (rev_is_head(head,
head_len,
ref_name[i],
- head_sha1, NULL))
+ head_oid.hash, NULL))
has_head++;
}
if (!has_head) {
}
for (num_rev = 0; ref_name[num_rev]; num_rev++) {
- unsigned char revkey[20];
+ struct object_id revkey;
unsigned int flag = 1u << (num_rev + REV_SHIFT);
if (MAX_REVS <= num_rev)
die("cannot handle more than %d revs.", MAX_REVS);
- if (get_sha1(ref_name[num_rev], revkey))
+ if (get_sha1(ref_name[num_rev], revkey.hash))
die("'%s' is not a valid ref.", ref_name[num_rev]);
- commit = lookup_commit_reference(revkey);
+ commit = lookup_commit_reference(revkey.hash);
if (!commit)
die("cannot find commit %s (%s)",
- ref_name[num_rev], revkey);
+ ref_name[num_rev], oid_to_hex(&revkey));
parse_commit(commit);
mark_seen(commit, &seen);
int is_head = rev_is_head(head,
head_len,
ref_name[i],
- head_sha1,
+ head_oid.hash,
rev[i]->object.sha1);
if (extra < 0)
printf("%c [%s] ",
static const char **pattern;
static const char *exclude_existing_arg;
-static void show_one(const char *refname, const unsigned char *sha1)
+static void show_one(const char *refname, const struct object_id *oid)
{
- const char *hex = find_unique_abbrev(sha1, abbrev);
+ const char *hex = find_unique_abbrev(oid->hash, abbrev);
if (hash_only)
printf("%s\n", hex);
else
printf("%s %s\n", hex, refname);
}
-static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+static int show_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cbdata)
{
const char *hex;
- unsigned char peeled[20];
+ struct object_id peeled;
if (show_head && !strcmp(refname, "HEAD"))
goto match;
* detect and return error if the repository is corrupt and
* ref points at a nonexistent object.
*/
- if (!has_sha1_file(sha1))
+ if (!has_sha1_file(oid->hash))
die("git show-ref: bad ref %s (%s)", refname,
- sha1_to_hex(sha1));
+ oid_to_hex(oid));
if (quiet)
return 0;
- show_one(refname, sha1);
+ show_one(refname, oid);
if (!deref_tags)
return 0;
- if (!peel_ref(refname, peeled)) {
- hex = find_unique_abbrev(peeled, abbrev);
+ if (!peel_ref(refname, peeled.hash)) {
+ hex = find_unique_abbrev(peeled.hash, abbrev);
printf("%s %s^{}\n", hex, refname);
}
return 0;
}
-static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+static int add_existing(const char *refname, const struct object_id *oid,
+ int flag, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
string_list_insert(list, refname);
if (!pattern)
die("--verify requires a reference");
while (*pattern) {
- unsigned char sha1[20];
+ struct object_id oid;
if (starts_with(*pattern, "refs/") &&
- !read_ref(*pattern, sha1)) {
+ !read_ref(*pattern, oid.hash)) {
if (!quiet)
- show_one(*pattern, sha1);
+ show_one(*pattern, &oid);
}
else if (!quiet)
die("'%s' - not a valid ref", *pattern);
return contains_test(candidate, want);
}
-static void show_tag_lines(const unsigned char *sha1, int lines)
+static void show_tag_lines(const struct object_id *oid, int lines)
{
int i;
unsigned long size;
char *buf, *sp, *eol;
size_t len;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf)
- die_errno("unable to read object %s", sha1_to_hex(sha1));
+ die_errno("unable to read object %s", oid_to_hex(oid));
if (type != OBJ_COMMIT && type != OBJ_TAG)
goto free_return;
if (!size)
die("an empty %s object %s?",
- typename(type), sha1_to_hex(sha1));
+ typename(type), oid_to_hex(oid));
/* skip header */
sp = strstr(buf, "\n\n");
free(buf);
}
-static int show_reference(const char *refname, const unsigned char *sha1,
+static int show_reference(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
{
struct tag_filter *filter = cb_data;
if (filter->with_commit) {
struct commit *commit;
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid->hash, 1);
if (!commit)
return 0;
if (!contains(commit, filter->with_commit))
return 0;
}
- if (points_at.nr && !match_points_at(refname, sha1))
+ if (points_at.nr && !match_points_at(refname, oid->hash))
return 0;
if (!filter->lines) {
return 0;
}
printf("%-15s ", refname);
- show_tag_lines(sha1, filter->lines);
+ show_tag_lines(oid, filter->lines);
putchar('\n');
}
memset(&filter.tags, 0, sizeof(filter.tags));
filter.tags.strdup_strings = 1;
- for_each_tag_ref(show_reference, (void *) &filter);
+ for_each_tag_ref(show_reference, (void *)&filter);
if (sort) {
int i;
if ((sort & SORT_MASK) == VERCMP_SORT)
unsigned char tree[20];
char path[PATH_MAX];
unsigned mode;
+ /*
+ * symlink_path is only used by get_tree_entry_follow_symlinks,
+ * and only for symlinks that point outside the repository.
+ */
+ struct strbuf symlink_path;
};
-#define GET_SHA1_QUIETLY 01
-#define GET_SHA1_COMMIT 02
-#define GET_SHA1_COMMITTISH 04
-#define GET_SHA1_TREE 010
-#define GET_SHA1_TREEISH 020
-#define GET_SHA1_BLOB 040
-#define GET_SHA1_ONLY_TO_DIE 04000
+#define GET_SHA1_QUIETLY 01
+#define GET_SHA1_COMMIT 02
+#define GET_SHA1_COMMITTISH 04
+#define GET_SHA1_TREE 010
+#define GET_SHA1_TREEISH 020
+#define GET_SHA1_BLOB 040
+#define GET_SHA1_FOLLOW_SYMLINKS 0100
+#define GET_SHA1_ONLY_TO_DIE 04000
extern int get_sha1(const char *str, unsigned char *sha1);
extern int get_sha1_commit(const char *str, unsigned char *sha1);
const char *c_func = diff_get_color(use_color, DIFF_FUNCINFO);
const char *c_new = diff_get_color(use_color, DIFF_FILE_NEW);
const char *c_old = diff_get_color(use_color, DIFF_FILE_OLD);
- const char *c_plain = diff_get_color(use_color, DIFF_PLAIN);
+ const char *c_context = diff_get_color(use_color, DIFF_CONTEXT);
const char *c_reset = diff_get_color(use_color, DIFF_RESET);
if (result_deleted)
}
if (comment_end)
printf("%s%s %s%s", c_reset,
- c_plain, c_reset,
+ c_context, c_reset,
c_func);
for (i = 0; i < comment_end; i++)
putchar(hunk_comment[i]);
*/
if (!context)
continue;
- fputs(c_plain, stdout);
+ fputs(c_context, stdout);
}
else
fputs(c_new, stdout);
-# List of known git commands.
+# common commands are grouped by themes
+# these groups are output by 'git help' in the order declared here.
+# map each common command in the command list to one of these groups.
+### common groups (do not change this line)
+init start a working area (see also: git help tutorial)
+worktree work on the current change (see also: git help everyday)
+info examine the history and state (see also: git help revisions)
+history grow, mark and tweak your common history
+remote collaborate (see also: git help workflows)
+
+### command list (do not change this line)
# command name category [deprecated] [common]
-git-add mainporcelain common
+git-add mainporcelain worktree
git-am mainporcelain
git-annotate ancillaryinterrogators
git-apply plumbingmanipulators
git-archimport foreignscminterface
git-archive mainporcelain
-git-bisect mainporcelain common
+git-bisect mainporcelain info
git-blame ancillaryinterrogators
-git-branch mainporcelain common
+git-branch mainporcelain history
git-bundle mainporcelain
git-cat-file plumbinginterrogators
git-check-attr purehelpers
git-check-ignore purehelpers
git-check-mailmap purehelpers
-git-checkout mainporcelain common
+git-checkout mainporcelain history
git-checkout-index plumbingmanipulators
git-check-ref-format purehelpers
git-cherry ancillaryinterrogators
git-cherry-pick mainporcelain
git-citool mainporcelain
git-clean mainporcelain
-git-clone mainporcelain common
+git-clone mainporcelain init
git-column purehelpers
-git-commit mainporcelain common
+git-commit mainporcelain history
git-commit-tree plumbingmanipulators
git-config ancillarymanipulators
git-count-objects ancillaryinterrogators
git-cvsserver foreignscminterface
git-daemon synchingrepositories
git-describe mainporcelain
-git-diff mainporcelain common
+git-diff mainporcelain history
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
git-diff-tree plumbinginterrogators
git-difftool ancillaryinterrogators
git-fast-export ancillarymanipulators
git-fast-import ancillarymanipulators
-git-fetch mainporcelain common
+git-fetch mainporcelain remote
git-fetch-pack synchingrepositories
git-filter-branch ancillarymanipulators
git-fmt-merge-msg purehelpers
git-fsck ancillaryinterrogators
git-gc mainporcelain
git-get-tar-commit-id ancillaryinterrogators
-git-grep mainporcelain common
+git-grep mainporcelain info
git-gui mainporcelain
git-hash-object plumbingmanipulators
git-help ancillaryinterrogators
git-http-push synchelpers
git-imap-send foreignscminterface
git-index-pack plumbingmanipulators
-git-init mainporcelain common
+git-init mainporcelain init
git-instaweb ancillaryinterrogators
git-interpret-trailers purehelpers
gitk mainporcelain
-git-log mainporcelain common
+git-log mainporcelain info
git-ls-files plumbinginterrogators
git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
git-mailinfo purehelpers
git-mailsplit purehelpers
-git-merge mainporcelain common
+git-merge mainporcelain history
git-merge-base plumbinginterrogators
git-merge-file plumbingmanipulators
git-merge-index plumbingmanipulators
git-merge-tree ancillaryinterrogators
git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
-git-mv mainporcelain common
+git-mv mainporcelain worktree
git-name-rev plumbinginterrogators
git-notes mainporcelain
git-p4 foreignscminterface
git-patch-id purehelpers
git-prune ancillarymanipulators
git-prune-packed plumbingmanipulators
-git-pull mainporcelain common
-git-push mainporcelain common
+git-pull mainporcelain remote
+git-push mainporcelain remote
git-quiltimport foreignscminterface
git-read-tree plumbingmanipulators
-git-rebase mainporcelain common
+git-rebase mainporcelain history
git-receive-pack synchelpers
git-reflog ancillarymanipulators
git-relink ancillarymanipulators
git-replace ancillarymanipulators
git-request-pull foreignscminterface
git-rerere ancillaryinterrogators
-git-reset mainporcelain common
+git-reset mainporcelain worktree
git-revert mainporcelain
git-rev-list plumbinginterrogators
git-rev-parse ancillaryinterrogators
-git-rm mainporcelain common
+git-rm mainporcelain worktree
git-send-email foreignscminterface
git-send-pack synchingrepositories
git-shell synchelpers
git-shortlog mainporcelain
-git-show mainporcelain common
+git-show mainporcelain info
git-show-branch ancillaryinterrogators
git-show-index plumbinginterrogators
git-show-ref plumbinginterrogators
git-sh-i18n purehelpers
git-sh-setup purehelpers
git-stash mainporcelain
-git-status mainporcelain common
+git-status mainporcelain info
git-stripspace purehelpers
git-submodule mainporcelain
git-svn foreignscminterface
git-symbolic-ref plumbingmanipulators
-git-tag mainporcelain common
+git-tag mainporcelain history
git-unpack-file plumbinginterrogators
git-unpack-objects plumbingmanipulators
git-update-index plumbingmanipulators
static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
- GIT_COLOR_NORMAL, /* PLAIN */
+ GIT_COLOR_NORMAL, /* CONTEXT */
GIT_COLOR_BOLD, /* METAINFO */
GIT_COLOR_CYAN, /* FRAGINFO */
GIT_COLOR_RED, /* OLD */
static int parse_diff_color_slot(const char *var)
{
- if (!strcasecmp(var, "plain"))
- return DIFF_PLAIN;
+ if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
+ return DIFF_CONTEXT;
if (!strcasecmp(var, "meta"))
return DIFF_METAINFO;
if (!strcasecmp(var, "frag"))
return ws_blank_line(line, len, ecbdata->ws_rule);
}
-static void emit_add_line(const char *reset,
- struct emit_callback *ecbdata,
- const char *line, int len)
+static void emit_line_checked(const char *reset,
+ struct emit_callback *ecbdata,
+ const char *line, int len,
+ enum color_diff color,
+ unsigned ws_error_highlight,
+ char sign)
{
- const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
- const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+ const char *set = diff_get_color(ecbdata->color_diff, color);
+ const char *ws = NULL;
- if (!*ws)
- emit_line_0(ecbdata->opt, set, reset, '+', line, len);
- else if (new_blank_line_at_eof(ecbdata, line, len))
+ if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
+ ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+ if (!*ws)
+ ws = NULL;
+ }
+
+ if (!ws)
+ emit_line_0(ecbdata->opt, set, reset, sign, line, len);
+ else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
/* Blank line at EOF - paint '+' as well */
- emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
+ emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
else {
/* Emit just the prefix, then the rest. */
- emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
+ emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
ws_check_emit(line, len, ecbdata->ws_rule,
ecbdata->opt->file, set, reset, ws);
}
}
+static void emit_add_line(const char *reset,
+ struct emit_callback *ecbdata,
+ const char *line, int len)
+{
+ emit_line_checked(reset, ecbdata, line, len,
+ DIFF_FILE_NEW, WSEH_NEW, '+');
+}
+
+static void emit_del_line(const char *reset,
+ struct emit_callback *ecbdata,
+ const char *line, int len)
+{
+ emit_line_checked(reset, ecbdata, line, len,
+ DIFF_FILE_OLD, WSEH_OLD, '-');
+}
+
+static void emit_context_line(const char *reset,
+ struct emit_callback *ecbdata,
+ const char *line, int len)
+{
+ emit_line_checked(reset, ecbdata, line, len,
+ DIFF_CONTEXT, WSEH_CONTEXT, ' ');
+}
+
static void emit_hunk_header(struct emit_callback *ecbdata,
const char *line, int len)
{
- const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
+ const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
if (len < 10 ||
memcmp(line, atat, 2) ||
!(ep = memmem(line + 2, len - 2, atat, 2))) {
- emit_line(ecbdata->opt, plain, reset, line, len);
+ emit_line(ecbdata->opt, context, reset, line, len);
return;
}
ep += 2; /* skip over @@ */
if (*ep != ' ' && *ep != '\t')
break;
if (ep != cp) {
- strbuf_addstr(&msgbuf, plain);
+ strbuf_addstr(&msgbuf, context);
strbuf_add(&msgbuf, cp, ep - cp);
strbuf_addstr(&msgbuf, reset);
}
{
const char *endp = NULL;
static const char *nneof = " No newline at end of file\n";
- const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
while (0 < size) {
len = endp ? (endp - data + 1) : size;
if (prefix != '+') {
ecb->lno_in_preimage++;
- emit_line_0(ecb->opt, old, reset, '-',
- data, len);
+ emit_del_line(reset, ecb, data, len);
} else {
ecb->lno_in_postimage++;
emit_add_line(reset, ecb, data, len);
data += len;
}
if (!endp) {
- const char *plain = diff_get_color(ecb->color_diff,
- DIFF_PLAIN);
+ const char *context = diff_get_color(ecb->color_diff,
+ DIFF_CONTEXT);
putc('\n', ecb->opt->file);
- emit_line_0(ecb->opt, plain, reset, '\\',
+ emit_line_0(ecb->opt, context, reset, '\\',
nneof, strlen(nneof));
}
}
struct diff_words_style *st = ecbdata->diff_words->style;
st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
- st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
+ st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT);
}
}
{
struct emit_callback *ecbdata = priv;
const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
- const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
+ const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
struct diff_options *o = ecbdata->opt;
const char *line_prefix = diff_line_prefix(o);
}
diff_words_flush(ecbdata);
if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
- emit_line(ecbdata->opt, plain, reset, line, len);
+ emit_line(ecbdata->opt, context, reset, line, len);
fputs("~\n", ecbdata->opt->file);
} else {
/*
line++;
len--;
}
- emit_line(ecbdata->opt, plain, reset, line, len);
+ emit_line(ecbdata->opt, context, reset, line, len);
}
return;
}
- if (line[0] != '+') {
- const char *color =
- diff_get_color(ecbdata->color_diff,
- line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
- ecbdata->lno_in_preimage++;
- if (line[0] == ' ')
- ecbdata->lno_in_postimage++;
- emit_line(ecbdata->opt, color, reset, line, len);
- } else {
+ switch (line[0]) {
+ case '+':
ecbdata->lno_in_postimage++;
emit_add_line(reset, ecbdata, line + 1, len - 1);
+ break;
+ case '-':
+ ecbdata->lno_in_preimage++;
+ emit_del_line(reset, ecbdata, line + 1, len - 1);
+ break;
+ case ' ':
+ ecbdata->lno_in_postimage++;
+ ecbdata->lno_in_preimage++;
+ emit_context_line(reset, ecbdata, line + 1, len - 1);
+ break;
+ default:
+ /* incomplete line at the end */
+ ecbdata->lno_in_preimage++;
+ emit_line(ecbdata->opt,
+ diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
+ reset, line, len);
+ break;
}
}
options->rename_limit = -1;
options->dirstat_permille = diff_dirstat_permille_default;
options->context = diff_context_default;
+ options->ws_error_highlight = WSEH_NEW;
DIFF_OPT_SET(options, RENAME_EMPTY);
/* pathchange left =NULL by default */
*fmt |= DIFF_FORMAT_PATCH;
}
+static int parse_one_token(const char **arg, const char *token)
+{
+ return skip_prefix(*arg, token, arg) && (!**arg || **arg == ',');
+}
+
+static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
+{
+ const char *orig_arg = arg;
+ unsigned val = 0;
+ while (*arg) {
+ if (parse_one_token(&arg, "none"))
+ val = 0;
+ else if (parse_one_token(&arg, "default"))
+ val = WSEH_NEW;
+ else if (parse_one_token(&arg, "all"))
+ val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
+ else if (parse_one_token(&arg, "new"))
+ val |= WSEH_NEW;
+ else if (parse_one_token(&arg, "old"))
+ val |= WSEH_OLD;
+ else if (parse_one_token(&arg, "context"))
+ val |= WSEH_CONTEXT;
+ else {
+ error("unknown value after ws-error-highlight=%.*s",
+ (int)(arg - orig_arg), orig_arg);
+ return 0;
+ }
+ if (*arg)
+ arg++;
+ }
+ opt->ws_error_highlight = val;
+ return 1;
+}
+
int diff_opt_parse(struct diff_options *options, const char **av, int ac)
{
const char *arg = av[0];
DIFF_OPT_SET(options, SUBMODULE_LOG);
else if (skip_prefix(arg, "--submodule=", &arg))
return parse_submodule_opt(options, arg);
+ else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
+ return parse_ws_error_highlight(options, arg);
/* misc options */
else if (!strcmp(arg, "-z"))
int dirstat_permille;
int setup;
int abbrev;
+/* white-space error highlighting */
+#define WSEH_NEW 1
+#define WSEH_CONTEXT 2
+#define WSEH_OLD 4
+ unsigned ws_error_highlight;
const char *prefix;
int prefix_length;
const char *stat_sep;
enum color_diff {
DIFF_RESET = 0,
- DIFF_PLAIN = 1,
+ DIFF_CONTEXT = 1,
DIFF_METAINFO = 2,
DIFF_FRAGINFO = 3,
DIFF_FILE_OLD = 4,
/*
* Make sure all pathspec matched; otherwise it is an error.
*/
- struct strbuf sb = STRBUF_INIT;
int num, errors = 0;
for (num = 0; num < pathspec->nr; num++) {
int other, found_dup;
pathspec->items[num].original);
errors++;
}
- strbuf_release(&sb);
return errors;
}
#define MAX_IN_VAIN 256
static struct prio_queue rev_list = { compare_commits_by_commit_date };
-static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
+static int non_common_revs, multi_ack, use_sideband;
+/* Allow specifying sha1 if it is a ref tip. */
+#define ALLOW_TIP_SHA1 01
+/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
+#define ALLOW_REACHABLE_SHA1 02
+static unsigned int allow_unadvertised_object_request;
static void rev_list_push(struct commit *commit, int mark)
{
}
}
-static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int rev_list_insert_ref(const char *refname, const unsigned char *sha1)
{
struct object *o = deref_tag(parse_object(sha1), refname, 0);
return 0;
}
-static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
- struct object *o = deref_tag(parse_object(sha1), refname, 0);
+ return rev_list_insert_ref(refname, oid->hash);
+}
+
+static int clear_marks(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
+{
+ struct object *o = deref_tag(parse_object(oid->hash), refname, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
{
- rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
+ rev_list_insert_ref(NULL, ref->old_sha1);
}
#define INITIAL_FLUSH 16
for_each_ref(clear_marks, NULL);
marked = 1;
- for_each_ref(rev_list_insert_ref, NULL);
+ for_each_ref(rev_list_insert_ref_oid, NULL);
for_each_alternate_ref(insert_one_alternate_ref, NULL);
fetching = 0;
static struct commit_list *complete;
-static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int mark_complete(const unsigned char *sha1)
{
struct object *o = parse_object(sha1);
return 0;
}
+static int mark_complete_oid(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
+{
+ return mark_complete(oid->hash);
+}
+
static void mark_recent_complete_commits(struct fetch_pack_args *args,
unsigned long cutoff)
{
}
/* Append unmatched requests to the list */
- if (allow_tip_sha1_in_want) {
+ if ((allow_unadvertised_object_request &
+ (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
for (i = 0; i < nr_sought; i++) {
unsigned char sha1[20];
static void mark_alternate_complete(const struct ref *ref, void *unused)
{
- mark_complete(NULL, ref->old_sha1, 0, NULL);
+ mark_complete(ref->old_sha1);
}
static int everything_local(struct fetch_pack_args *args,
}
if (!args->depth) {
- for_each_ref(mark_complete, NULL);
+ for_each_ref(mark_complete_oid, NULL);
for_each_alternate_ref(mark_alternate_complete, NULL);
commit_list_sort_by_date(&complete);
if (cutoff)
if (server_supports("allow-tip-sha1-in-want")) {
if (args->verbose)
fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
- allow_tip_sha1_in_want = 1;
+ allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
+ }
+ if (server_supports("allow-reachable-sha1-in-want")) {
+ if (args->verbose)
+ fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
+ allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
}
if (!server_supports("thin-pack"))
args->use_thin_pack = 0;
--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+print <<"EOT";
+/* Automatically generated by $0 */
+
+struct cmdname_help {
+ char name[16];
+ char help[80];
+ unsigned char group;
+};
+
+static char *common_cmd_groups[] = {
+EOT
+
+my $n = 0;
+my %grp;
+while (<>) {
+ last if /^### command list/;
+ next if (1../^### common groups/) || /^#/ || /^\s*$/;
+ chop;
+ my ($k, $v) = split ' ', $_, 2;
+ $grp{$k} = $n++;
+ print "\tN_(\"$v\"),\n";
+}
+
+print "};\n\nstatic struct cmdname_help common_cmds[] = {\n";
+
+while (<>) {
+ next if /^#/ || /^\s*$/;
+ my @tags = split;
+ my $cmd = shift @tags;
+ for my $t (@tags) {
+ if (exists $grp{$t}) {
+ my $s;
+ open my $f, '<', "Documentation/$cmd.txt" or die;
+ while (<$f>) {
+ ($s) = /^$cmd - (.+)$/;
+ last if $s;
+ }
+ close $f;
+ $cmd =~ s/^git-//;
+ print "\t{\"$cmd\", N_(\"$s\"), $grp{$t}},\n";
+ last;
+ }
+ }
+}
+
+print "};\n";
+++ /dev/null
-#!/bin/sh
-
-echo "/* Automatically generated by $0 */
-struct cmdname_help {
- char name[16];
- char help[80];
-};
-
-static struct cmdname_help common_cmds[] = {"
-
-sed -n -e 's/^git-\([^ ]*\)[ ].* common.*/\1/p' command-list.txt |
-sort |
-while read cmd
-do
- sed -n '
- /^NAME/,/git-'"$cmd"'/H
- ${
- x
- s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", N_("\1")},/
- p
- }' "Documentation/git-$cmd.txt"
-done
-echo "};"
: ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools}
+IFS='
+'
+
mode_ok () {
if diff_mode
then
printf "Merging:\n"
printf "%s\n" "$files"
-IFS='
-'
rc=0
for i in $files
do
editor = os.environ.get("P4EDITOR")
else:
editor = read_pipe("git var GIT_EDITOR").strip()
- system([editor, template_file])
+ system(["sh", "-c", ('%s "$@"' % editor), editor, template_file])
# If the file was not saved, prompt to see if this patch should
# be skipped. But skip this verification step if configured so.
}
do_next () {
- rm -f "$msg" "$author_script" "$amend" || exit
+ rm -f "$msg" "$author_script" "$amend" "$state_dir"/stopped-sha || exit
read -r command sha1 rest < "$todo"
case "$command" in
"$comment_char"*|''|noop)
read -r command rest < "$todo"
mark_action_done
printf 'Executing: %s\n' "$rest"
- # "exec" command doesn't take a sha1 in the todo-list.
- # => can't just use $sha1 here.
- git rev-parse --verify HEAD > "$state_dir"/stopped-sha
${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
status=$?
# Run in subshell because require_clean_work_tree can die.
fi
fi
- record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
+ if test -r "$state_dir"/stopped-sha
+ then
+ record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
+ fi
require_clean_work_tree "rebase"
do_rest
-a|--all)
untracked=all
;;
+ --help)
+ show_help
+ ;;
--)
shift
break
}
show_stash () {
+ ALLOW_UNKNOWN_FLAGS=t
assert_stash_like "$@"
git diff ${FLAGS:---stat} $b_commit $w_commit
}
+show_help () {
+ exec git help stash
+ exit 1
+}
+
#
# Parses the remaining options looking for flags and
# at most one revision defaulting to ${ref_stash}@{0}
#
# GIT_QUIET is set to t if -q is specified
# INDEX_OPTION is set to --index if --index is specified.
-# FLAGS is set to the remaining flags
+# FLAGS is set to the remaining flags (if allowed)
#
# dies if:
# * too many revisions specified
# * no revision is specified and there is no stash stack
# * a revision is specified which cannot be resolve to a SHA1
# * a non-existent stash reference is specified
+# * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
#
parse_flags_and_rev()
--index)
INDEX_OPTION=--index
;;
+ --help)
+ show_help
+ ;;
-*)
+ test "$ALLOW_UNKNOWN_FLAGS" = t ||
+ die "$(eval_gettext "unknown option: \$opt")"
FLAGS="${FLAGS}${FLAGS:+ }$opt"
;;
esac
}
}
+static int cmd_group_cmp(const void *elem1, const void *elem2)
+{
+ const struct cmdname_help *e1 = elem1;
+ const struct cmdname_help *e2 = elem2;
+
+ if (e1->group < e2->group)
+ return -1;
+ if (e1->group > e2->group)
+ return 1;
+ return strcmp(e1->name, e2->name);
+}
+
void list_common_cmds_help(void)
{
int i, longest = 0;
+ int current_grp = -1;
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
if (longest < strlen(common_cmds[i].name))
longest = strlen(common_cmds[i].name);
}
- puts(_("The most commonly used git commands are:"));
+ qsort(common_cmds, ARRAY_SIZE(common_cmds),
+ sizeof(common_cmds[0]), cmd_group_cmp);
+
+ puts(_("These are common Git commands used in various situations:"));
+
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+ if (common_cmds[i].group != current_grp) {
+ printf("\n%s\n", _(common_cmd_groups[common_cmds[i].group]));
+ current_grp = common_cmds[i].group;
+ }
+
printf(" %s ", common_cmds[i].name);
mput_char(' ', longest - strlen(common_cmds[i].name));
puts(_(common_cmds[i].help));
struct string_list *similar_refs;
};
-static int append_similar_ref(const char *refname, const unsigned char *sha1,
+static int append_similar_ref(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
static const char content_length[] = "Content-Length";
static const char last_modified[] = "Last-Modified";
static int getanyfile = 1;
+static unsigned long max_request_buffer = 10 * 1024 * 1024;
static struct string_list *query_params;
struct rpc_service {
const char *name;
const char *config_name;
+ unsigned buffer_input : 1;
signed enabled : 2;
};
static struct rpc_service rpc_service[] = {
- { "upload-pack", "uploadpack", 1 },
- { "receive-pack", "receivepack", -1 },
+ { "upload-pack", "uploadpack", 1, 1 },
+ { "receive-pack", "receivepack", 0, -1 },
};
static struct string_list *get_parameters(void)
struct strbuf var = STRBUF_INIT;
git_config_get_bool("http.getanyfile", &getanyfile);
+ git_config_get_ulong("http.maxrequestbuffer", &max_request_buffer);
for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
struct rpc_service *svc = &rpc_service[i];
return svc;
}
-static void inflate_request(const char *prog_name, int out)
+/*
+ * This is basically strbuf_read(), except that if we
+ * hit max_request_buffer we die (we'd rather reject a
+ * maliciously large request than chew up infinite memory).
+ */
+static ssize_t read_request(int fd, unsigned char **out)
+{
+ size_t len = 0, alloc = 8192;
+ unsigned char *buf = xmalloc(alloc);
+
+ if (max_request_buffer < alloc)
+ max_request_buffer = alloc;
+
+ while (1) {
+ ssize_t cnt;
+
+ cnt = read_in_full(fd, buf + len, alloc - len);
+ if (cnt < 0) {
+ free(buf);
+ return -1;
+ }
+
+ /* partial read from read_in_full means we hit EOF */
+ len += cnt;
+ if (len < alloc) {
+ *out = buf;
+ return len;
+ }
+
+ /* otherwise, grow and try again (if we can) */
+ if (alloc == max_request_buffer)
+ die("request was larger than our maximum size (%lu);"
+ " try setting GIT_HTTP_MAX_REQUEST_BUFFER",
+ max_request_buffer);
+
+ alloc = alloc_nr(alloc);
+ if (alloc > max_request_buffer)
+ alloc = max_request_buffer;
+ REALLOC_ARRAY(buf, alloc);
+ }
+}
+
+static void inflate_request(const char *prog_name, int out, int buffer_input)
{
git_zstream stream;
+ unsigned char *full_request = NULL;
unsigned char in_buf[8192];
unsigned char out_buf[8192];
unsigned long cnt = 0;
git_inflate_init_gzip_only(&stream);
while (1) {
- ssize_t n = xread(0, in_buf, sizeof(in_buf));
+ ssize_t n;
+
+ if (buffer_input) {
+ if (full_request)
+ n = 0; /* nothing left to read */
+ else
+ n = read_request(0, &full_request);
+ stream.next_in = full_request;
+ } else {
+ n = xread(0, in_buf, sizeof(in_buf));
+ stream.next_in = in_buf;
+ }
+
if (n <= 0)
die("request ended in the middle of the gzip stream");
-
- stream.next_in = in_buf;
stream.avail_in = n;
while (0 < stream.avail_in) {
done:
git_inflate_end(&stream);
close(out);
+ free(full_request);
+}
+
+static void copy_request(const char *prog_name, int out)
+{
+ unsigned char *buf;
+ ssize_t n = read_request(0, &buf);
+ if (n < 0)
+ die_errno("error reading request body");
+ if (write_in_full(out, buf, n) != n)
+ die("%s aborted reading request", prog_name);
+ close(out);
+ free(buf);
}
-static void run_service(const char **argv)
+static void run_service(const char **argv, int buffer_input)
{
const char *encoding = getenv("HTTP_CONTENT_ENCODING");
const char *user = getenv("REMOTE_USER");
"GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
cld.argv = argv;
- if (gzipped_request)
+ if (buffer_input || gzipped_request)
cld.in = -1;
cld.git_cmd = 1;
if (start_command(&cld))
close(1);
if (gzipped_request)
- inflate_request(argv[0], cld.in);
+ inflate_request(argv[0], cld.in, buffer_input);
+ else if (buffer_input)
+ copy_request(argv[0], cld.in);
else
close(0);
exit(1);
}
-static int show_text_ref(const char *name, const unsigned char *sha1,
- int flag, void *cb_data)
+static int show_text_ref(const char *name, const struct object_id *oid,
+ int flag, void *cb_data)
{
const char *name_nons = strip_namespace(name);
struct strbuf *buf = cb_data;
- struct object *o = parse_object(sha1);
+ struct object *o = parse_object(oid->hash);
if (!o)
return 0;
- strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name_nons);
+ strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
if (o->type == OBJ_TAG) {
o = deref_tag(o, name, 0);
if (!o)
packet_flush(1);
argv[0] = svc->name;
- run_service(argv);
+ run_service(argv, 0);
} else {
select_getanyfile();
strbuf_release(&buf);
}
-static int show_head_ref(const char *refname, const unsigned char *sha1,
- int flag, void *cb_data)
+static int show_head_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct strbuf *buf = cb_data;
if (flag & REF_ISSYMREF) {
- unsigned char unused[20];
+ struct object_id unused;
const char *target = resolve_ref_unsafe(refname,
RESOLVE_REF_READING,
- unused, NULL);
+ unused.hash, NULL);
const char *target_nons = strip_namespace(target);
strbuf_addf(buf, "ref: %s\n", target_nons);
} else {
- strbuf_addf(buf, "%s\n", sha1_to_hex(sha1));
+ strbuf_addf(buf, "%s\n", oid_to_hex(oid));
}
return 0;
end_headers();
argv[0] = svc->name;
- run_service(argv);
+ run_service(argv, svc->buffer_input);
strbuf_release(&buf);
}
+static int dead;
static NORETURN void die_webcgi(const char *err, va_list params)
{
- static int dead;
+ if (dead <= 1) {
+ vreportf("fatal: ", err, params);
- if (!dead) {
- dead = 1;
http_status(500, "Internal Server Error");
hdr_nocache();
end_headers();
-
- vreportf("fatal: ", err, params);
}
exit(0); /* we successfully reported a failure ;-) */
}
+static int die_webcgi_recursing(void)
+{
+ return dead++ > 1;
+}
+
static char* getdir(void)
{
struct strbuf buf = STRBUF_INIT;
git_extract_argv0_path(argv[0]);
set_die_routine(die_webcgi);
+ set_die_is_recursing_routine(die_webcgi_recursing);
if (!method)
die("No REQUEST_METHOD from server");
not_found("Repository not exported: '%s'", dir);
http_config();
+ max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER",
+ max_request_buffer);
+
cmd->imp(cmd_arg);
return 0;
}
const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
- const char *c_plain = diff_get_color(opt->use_color, DIFF_PLAIN);
+ const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
if (!pair || !diff)
return;
int k;
for (; t_cur < diff->target.ranges[j].start; t_cur++)
print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
- c_plain, c_reset);
+ c_context, c_reset);
for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
print_line(prefix, '-', k, p_ends, pair->one->data,
c_old, c_reset);
}
for (; t_cur < t_end; t_cur++)
print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
- c_plain, c_reset);
+ c_context, c_reset);
}
free(p_ends);
return lookup_decoration(&name_decoration, obj);
}
-static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+static int add_ref_decoration(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
{
struct object *obj;
enum decoration_type type = DECORATION_NONE;
assert(cb_data == NULL);
if (starts_with(refname, "refs/replace/")) {
- unsigned char original_sha1[20];
+ struct object_id original_oid;
if (!check_replace_refs)
return 0;
- if (get_sha1_hex(refname + 13, original_sha1)) {
+ if (get_oid_hex(refname + 13, &original_oid)) {
warning("invalid replace ref %s", refname);
return 0;
}
- obj = parse_object(original_sha1);
+ obj = parse_object(original_oid.hash);
if (obj)
add_name_decoration(DECORATION_GRAFTED, "replaced", obj);
return 0;
}
- obj = parse_object(sha1);
+ obj = parse_object(oid->hash);
if (!obj)
return 0;
void load_ref_decorations(int flags)
{
if (!decoration_loaded) {
+
decoration_loaded = 1;
decoration_flags = flags;
for_each_ref(add_ref_decoration, NULL);
{
struct strbuf newpath = STRBUF_INIT;
int suffix = 0;
- struct stat st;
size_t base_len;
strbuf_addf(&newpath, "%s~", path);
base_len = newpath.len;
while (string_list_has_string(&o->current_file_set, newpath.buf) ||
string_list_has_string(&o->current_directory_set, newpath.buf) ||
- lstat(newpath.buf, &st) == 0) {
+ file_exists(newpath.buf)) {
strbuf_setlen(&newpath, base_len);
strbuf_addf(&newpath, "_%d", suffix++);
}
--- /dev/null
+diff_cmd () {
+ "$merge_tool_path" -u -e "$LOCAL" "$REMOTE"
+ return 0
+}
+
+merge_cmd () {
+ # mergetool.winmerge.trustExitCode is implicitly false.
+ # touch $BACKUP so that we can check_unchanged.
+ touch "$BACKUP"
+ "$merge_tool_path" -u -e -dl Local -dr Remote \
+ "$LOCAL" "$REMOTE" "$MERGED"
+ check_unchanged
+}
+
+translate_merge_tool_path() {
+ # Use WinMergeU.exe if it exists in $PATH
+ if type -p WinMergeU.exe >/dev/null 2>&1
+ then
+ printf WinMergeU.exe
+ return
+ fi
+
+ # Look for WinMergeU.exe in the typical locations
+ winmerge_exe="WinMerge/WinMergeU.exe"
+ for directory in $(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' |
+ cut -d '=' -f 2- | sort -u)
+ do
+ if test -n "$directory" && test -x "$directory/$winmerge_exe"
+ then
+ printf '%s' "$directory/$winmerge_exe"
+ return
+ fi
+ done
+
+ printf WinMergeU.exe
+}
return ret;
}
-static int string_list_add_one_ref(const char *refname, const unsigned char *sha1,
+static int string_list_add_one_ref(const char *refname, const struct object_id *oid,
int flag, void *cb)
{
struct string_list *refs = cb;
else
fprintf(stderr, "Mismatch!\n");
- free(result);
+ bitmap_free(result);
}
static int rebuild_bitmap(uint32_t *reposition,
display_progress(cp->progress, cp->count);
}
-static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int add_one_ref(const char *path, const struct object_id *oid,
+ int flag, void *cb_data)
{
- struct object *object = parse_object_or_die(sha1, path);
+ struct object *object = parse_object_or_die(oid->hash, path);
struct rev_info *revs = (struct rev_info *)cb_data;
add_pending_object(revs, object, "");
char *ref_name;
char *orig_ref_name;
struct lock_file *lk;
- unsigned char old_sha1[20];
+ struct object_id old_oid;
};
/*
* null. If REF_ISSYMREF, then this is the name of the object
* referred to by the last reference in the symlink chain.
*/
- unsigned char sha1[20];
+ struct object_id oid;
/*
* If REF_KNOWS_PEELED, then this field holds the peeled value
* be peelable. See the documentation for peel_ref() for an
* exact definition of "peelable".
*/
- unsigned char peeled[20];
+ struct object_id peeled;
};
struct ref_cache;
die("Reference has invalid format: '%s'", refname);
len = strlen(refname) + 1;
ref = xmalloc(sizeof(struct ref_entry) + len);
- hashcpy(ref->u.value.sha1, sha1);
- hashclr(ref->u.value.peeled);
+ hashcpy(ref->u.value.oid.hash, sha1);
+ oidclr(&ref->u.value.peeled);
memcpy(ref->name, refname, len);
ref->flag = flag;
return ref;
/* This is impossible by construction */
die("Reference directory conflict: %s", ref1->name);
- if (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))
+ if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
warning("Duplicated ref: %s", ref1->name);
{
if (entry->flag & REF_ISBROKEN)
return 0;
- if (!has_sha1_file(entry->u.value.sha1)) {
+ if (!has_sha1_file(entry->u.value.oid.hash)) {
error("%s does not point to a valid object!", entry->name);
return 0;
}
/* Store the old value, in case this is a recursive call: */
old_current_ref = current_ref;
current_ref = entry;
- retval = data->fn(entry->name + data->trim, entry->u.value.sha1,
+ retval = data->fn(entry->name + data->trim, &entry->u.value.oid,
entry->flag, data->cb_data);
current_ref = old_current_ref;
return retval;
line.len == PEELED_LINE_LENGTH &&
line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
!get_sha1_hex(line.buf + 1, sha1)) {
- hashcpy(last->u.value.peeled, sha1);
+ hashcpy(last->u.value.peeled.hash, sha1);
/*
* Regardless of what the file header said,
* we definitely know the value of *this*
if (ref == NULL)
return -1;
- hashcpy(sha1, ref->u.value.sha1);
+ hashcpy(sha1, ref->u.value.oid.hash);
return 0;
}
*/
entry = get_packed_ref(refname);
if (entry) {
- hashcpy(sha1, entry->u.value.sha1);
+ hashcpy(sha1, entry->u.value.oid.hash);
if (flags)
*flags |= REF_ISPACKED;
return 0;
return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
}
-static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
- void *data)
+static int filter_refs(const char *refname, const struct object_id *oid,
+ int flags, void *data)
{
struct ref_filter *filter = (struct ref_filter *)data;
+
if (wildmatch(filter->pattern, refname, 0, NULL))
return 0;
- return filter->fn(refname, sha1, flags, filter->cb_data);
+ return filter->fn(refname, oid, flags, filter->cb_data);
}
enum peel_status {
if (entry->flag & REF_KNOWS_PEELED) {
if (repeel) {
entry->flag &= ~REF_KNOWS_PEELED;
- hashclr(entry->u.value.peeled);
+ oidclr(&entry->u.value.peeled);
} else {
- return is_null_sha1(entry->u.value.peeled) ?
+ return is_null_oid(&entry->u.value.peeled) ?
PEEL_NON_TAG : PEEL_PEELED;
}
}
if (entry->flag & REF_ISSYMREF)
return PEEL_IS_SYMREF;
- status = peel_object(entry->u.value.sha1, entry->u.value.peeled);
+ status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
if (status == PEEL_PEELED || status == PEEL_NON_TAG)
entry->flag |= REF_KNOWS_PEELED;
return status;
|| !strcmp(current_ref->name, refname))) {
if (peel_entry(current_ref, 0))
return -1;
- hashcpy(sha1, current_ref->u.value.peeled);
+ hashcpy(sha1, current_ref->u.value.peeled.hash);
return 0;
}
if (r) {
if (peel_entry(r, 0))
return -1;
- hashcpy(sha1, r->u.value.peeled);
+ hashcpy(sha1, r->u.value.peeled.hash);
return 0;
}
}
const char *msg_fmt;
};
-static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
+static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
struct warn_if_dangling_data *d = cb_data;
const char *resolves_to;
- unsigned char junk[20];
+ struct object_id junk;
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
+ resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
{
- unsigned char sha1[20];
+ struct object_id oid;
int flag;
if (submodule) {
- if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
- return fn("HEAD", sha1, 0, cb_data);
+ if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
+ return fn("HEAD", &oid, 0, cb_data);
return 0;
}
- if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
- return fn("HEAD", sha1, flag, cb_data);
+ if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+ return fn("HEAD", &oid, flag, cb_data);
return 0;
}
{
struct strbuf buf = STRBUF_INIT;
int ret = 0;
- unsigned char sha1[20];
+ struct object_id oid;
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
- ret = fn(buf.buf, sha1, flag, cb_data);
+ if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
+ ret = fn(buf.buf, &oid, flag, cb_data);
strbuf_release(&buf);
return ret;
if (read_ref_full(lock->ref_name,
mustexist ? RESOLVE_REF_READING : 0,
- lock->old_sha1, NULL)) {
+ lock->old_oid.hash, NULL)) {
int save_errno = errno;
strbuf_addf(err, "can't verify ref %s", lock->ref_name);
errno = save_errno;
return -1;
}
- if (hashcmp(lock->old_sha1, old_sha1)) {
+ if (hashcmp(lock->old_oid.hash, old_sha1)) {
strbuf_addf(err, "ref %s is at %s but expected %s",
lock->ref_name,
- sha1_to_hex(lock->old_sha1),
+ sha1_to_hex(lock->old_oid.hash),
sha1_to_hex(old_sha1));
errno = EBUSY;
return -1;
}
refname = resolve_ref_unsafe(refname, resolve_flags,
- lock->old_sha1, &type);
+ lock->old_oid.hash, &type);
if (!refname && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
goto error_return;
}
refname = resolve_ref_unsafe(orig_refname, resolve_flags,
- lock->old_sha1, &type);
+ lock->old_oid.hash, &type);
}
if (type_p)
*type_p = type;
* refname, nor a packed ref whose name is a proper prefix of
* our refname.
*/
- if (is_null_sha1(lock->old_sha1) &&
+ if (is_null_oid(&lock->old_oid) &&
verify_refname_available(refname, extras, skip,
get_packed_refs(&ref_cache), err)) {
last_errno = ENOTDIR;
if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
error("internal error: %s is not a valid packed reference!",
entry->name);
- write_packed_entry(cb_data, entry->name, entry->u.value.sha1,
+ write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
peel_status == PEEL_PEELED ?
- entry->u.value.peeled : NULL);
+ entry->u.value.peeled.hash : NULL);
return 0;
}
peel_status = peel_entry(entry, 1);
if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
die("internal error peeling reference %s (%s)",
- entry->name, sha1_to_hex(entry->u.value.sha1));
+ entry->name, oid_to_hex(&entry->u.value.oid));
packed_entry = find_ref(cb->packed_refs, entry->name);
if (packed_entry) {
/* Overwrite existing packed entry with info from loose entry */
packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
- hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
+ oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
} else {
- packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
+ packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
REF_ISPACKED | REF_KNOWS_PEELED, 0);
add_ref(cb->packed_refs, packed_entry);
}
- hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
+ oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
/* Schedule the loose reference for pruning if requested. */
if ((cb->flags & PACK_REFS_PRUNE)) {
int namelen = strlen(entry->name) + 1;
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
- hashcpy(n->sha1, entry->u.value.sha1);
+ hashcpy(n->sha1, entry->u.value.oid.hash);
strcpy(n->name, entry->name);
n->next = cb->ref_to_prune;
cb->ref_to_prune = n;
strbuf_release(&err);
goto rollback;
}
- hashcpy(lock->old_sha1, orig_sha1);
+ hashcpy(lock->old_oid.hash, orig_sha1);
if (write_ref_to_lockfile(lock, orig_sha1) ||
commit_ref_update(lock, orig_sha1, logmsg)) {
const unsigned char *sha1, const char *logmsg)
{
clear_loose_ref_cache(&ref_cache);
- if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
+ if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 ||
(strcmp(lock->ref_name, lock->orig_ref_name) &&
- log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
+ log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg) < 0)) {
unlock_ref(lock);
return -1;
}
head_sha1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
- log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
+ log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
}
if (commit_ref(lock)) {
error("Couldn't set %s", lock->ref_name);
strbuf_addch(name, '/');
retval = do_for_each_reflog(name, fn, cb_data);
} else {
- unsigned char sha1[20];
- if (read_ref_full(name->buf, 0, sha1, NULL))
+ struct object_id oid;
+
+ if (read_ref_full(name->buf, 0, oid.hash, NULL))
retval = error("bad ref for %s", name->buf);
else
- retval = fn(name->buf, sha1, 0, cb_data);
+ retval = fn(name->buf, &oid, 0, cb_data);
}
if (retval)
break;
(update->flags & REF_NODEREF));
if (!overwriting_symref &&
- !hashcmp(update->lock->old_sha1, update->new_sha1)) {
+ !hashcmp(update->lock->old_oid.hash, update->new_sha1)) {
/*
* The reference already has the desired
* value, so we don't need to write it.
* single callback invocation.
*/
typedef int each_ref_fn(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data);
+ const struct object_id *oid, int flags, void *cb_data);
/*
* The following functions invoke the specified callback function for
static int branches_nr;
static struct branch *current_branch;
-static const char *default_remote_name;
-static const char *branch_pushremote_name;
static const char *pushremote_name;
-static int explicit_default_remote_name;
static struct rewrites rewrites;
static struct rewrites rewrites_push;
return 0;
branch = make_branch(name, subkey - name);
if (!strcmp(subkey, ".remote")) {
- if (git_config_string(&branch->remote_name, key, value))
- return -1;
- if (branch == current_branch) {
- default_remote_name = branch->remote_name;
- explicit_default_remote_name = 1;
- }
+ return git_config_string(&branch->remote_name, key, value);
} else if (!strcmp(subkey, ".pushremote")) {
- if (branch == current_branch)
- if (git_config_string(&branch_pushremote_name, key, value))
- return -1;
+ return git_config_string(&branch->pushremote_name, key, value);
} else if (!strcmp(subkey, ".merge")) {
if (!value)
return config_error_nonbool(key);
static void read_config(void)
{
+ static int loaded;
unsigned char sha1[20];
const char *head_ref;
int flag;
- if (default_remote_name) /* did this already */
+
+ if (loaded)
return;
- default_remote_name = "origin";
+ loaded = 1;
+
current_branch = NULL;
head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
current_branch = make_branch(head_ref, 0);
}
git_config(handle_config, NULL);
- if (branch_pushremote_name) {
- free((char *)pushremote_name);
- pushremote_name = branch_pushremote_name;
- }
alias_all_urls();
}
return !strchr(name, '/'); /* no slash */
}
-static struct remote *remote_get_1(const char *name, const char *pushremote_name)
+const char *remote_for_branch(struct branch *branch, int *explicit)
+{
+ if (branch && branch->remote_name) {
+ if (explicit)
+ *explicit = 1;
+ return branch->remote_name;
+ }
+ if (explicit)
+ *explicit = 0;
+ return "origin";
+}
+
+const char *pushremote_for_branch(struct branch *branch, int *explicit)
+{
+ if (branch && branch->pushremote_name) {
+ if (explicit)
+ *explicit = 1;
+ return branch->pushremote_name;
+ }
+ if (pushremote_name) {
+ if (explicit)
+ *explicit = 1;
+ return pushremote_name;
+ }
+ return remote_for_branch(branch, explicit);
+}
+
+static struct remote *remote_get_1(const char *name,
+ const char *(*get_default)(struct branch *, int *))
{
struct remote *ret;
int name_given = 0;
+ read_config();
+
if (name)
name_given = 1;
- else {
- if (pushremote_name) {
- name = pushremote_name;
- name_given = 1;
- } else {
- name = default_remote_name;
- name_given = explicit_default_remote_name;
- }
- }
+ else
+ name = get_default(current_branch, &name_given);
ret = make_remote(name, 0);
if (valid_remote_nick(name)) {
struct remote *remote_get(const char *name)
{
- read_config();
- return remote_get_1(name, NULL);
+ return remote_get_1(name, remote_for_branch);
}
struct remote *pushremote_get(const char *name)
{
- read_config();
- return remote_get_1(name, pushremote_name);
+ return remote_get_1(name, pushremote_for_branch);
}
int remote_is_configured(const char *name)
static void set_merge(struct branch *ret)
{
+ struct remote *remote;
char *ref;
unsigned char sha1[20];
int i;
+ if (!ret)
+ return; /* no branch */
+ if (ret->merge)
+ return; /* already run */
+ if (!ret->remote_name || !ret->merge_nr) {
+ /*
+ * no merge config; let's make sure we don't confuse callers
+ * with a non-zero merge_nr but a NULL merge
+ */
+ ret->merge_nr = 0;
+ return;
+ }
+
+ remote = remote_get(ret->remote_name);
+
ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
for (i = 0; i < ret->merge_nr; i++) {
ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
ret->merge[i]->src = xstrdup(ret->merge_name[i]);
- if (!remote_find_tracking(ret->remote, ret->merge[i]) ||
+ if (!remote_find_tracking(remote, ret->merge[i]) ||
strcmp(ret->remote_name, "."))
continue;
if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
ret = current_branch;
else
ret = make_branch(name, 0);
- if (ret && ret->remote_name) {
- ret->remote = remote_get(ret->remote_name);
- if (ret->merge_nr)
- set_merge(ret);
- }
+ set_merge(ret);
return ret;
}
return refname_match(branch->merge[i]->src, refname);
}
+__attribute((format (printf,2,3)))
+static const char *error_buf(struct strbuf *err, const char *fmt, ...)
+{
+ if (err) {
+ va_list ap;
+ va_start(ap, fmt);
+ strbuf_vaddf(err, fmt, ap);
+ va_end(ap);
+ }
+ return NULL;
+}
+
+const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
+{
+ if (!branch)
+ return error_buf(err, _("HEAD does not point to a branch"));
+
+ if (!branch->merge || !branch->merge[0]) {
+ /*
+ * no merge config; is it because the user didn't define any,
+ * or because it is not a real branch, and get_branch
+ * auto-vivified it?
+ */
+ if (!ref_exists(branch->refname))
+ return error_buf(err, _("no such branch: '%s'"),
+ branch->name);
+ return error_buf(err,
+ _("no upstream configured for branch '%s'"),
+ branch->name);
+ }
+
+ if (!branch->merge[0]->dst)
+ return error_buf(err,
+ _("upstream branch '%s' not stored as a remote-tracking branch"),
+ branch->merge[0]->src);
+
+ return branch->merge[0]->dst;
+}
+
+static const char *tracking_for_push_dest(struct remote *remote,
+ const char *refname,
+ struct strbuf *err)
+{
+ char *ret;
+
+ ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
+ if (!ret)
+ return error_buf(err,
+ _("push destination '%s' on remote '%s' has no local tracking branch"),
+ refname, remote->name);
+ return ret;
+}
+
+static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
+{
+ struct remote *remote;
+
+ if (!branch)
+ return error_buf(err, _("HEAD does not point to a branch"));
+
+ remote = remote_get(pushremote_for_branch(branch, NULL));
+ if (!remote)
+ return error_buf(err,
+ _("branch '%s' has no remote for pushing"),
+ branch->name);
+
+ if (remote->push_refspec_nr) {
+ char *dst;
+ const char *ret;
+
+ dst = apply_refspecs(remote->push, remote->push_refspec_nr,
+ branch->refname);
+ if (!dst)
+ return error_buf(err,
+ _("push refspecs for '%s' do not include '%s'"),
+ remote->name, branch->name);
+
+ ret = tracking_for_push_dest(remote, dst, err);
+ free(dst);
+ return ret;
+ }
+
+ if (remote->mirror)
+ return tracking_for_push_dest(remote, branch->refname, err);
+
+ switch (push_default) {
+ case PUSH_DEFAULT_NOTHING:
+ return error_buf(err, _("push has no destination (push.default is 'nothing')"));
+
+ case PUSH_DEFAULT_MATCHING:
+ case PUSH_DEFAULT_CURRENT:
+ return tracking_for_push_dest(remote, branch->refname, err);
+
+ case PUSH_DEFAULT_UPSTREAM:
+ return branch_get_upstream(branch, err);
+
+ case PUSH_DEFAULT_UNSPECIFIED:
+ case PUSH_DEFAULT_SIMPLE:
+ {
+ const char *up, *cur;
+
+ up = branch_get_upstream(branch, err);
+ if (!up)
+ return NULL;
+ cur = tracking_for_push_dest(remote, branch->refname, err);
+ if (!cur)
+ return NULL;
+ if (strcmp(cur, up))
+ return error_buf(err,
+ _("cannot resolve 'simple' push to a single destination"));
+ return cur;
+ }
+ }
+
+ die("BUG: unhandled push situation");
+}
+
+const char *branch_get_push(struct branch *branch, struct strbuf *err)
+{
+ if (!branch->push_tracking_ref)
+ branch->push_tracking_ref = branch_get_push_1(branch, err);
+ return branch->push_tracking_ref;
+}
+
static int ignore_symref_update(const char *refname)
{
unsigned char sha1[20];
/*
* Compare a branch with its upstream, and save their differences (number
- * of commits) in *num_ours and *num_theirs.
+ * of commits) in *num_ours and *num_theirs. The name of the upstream branch
+ * (or NULL if no upstream is defined) is returned via *upstream_name, if it
+ * is not itself NULL.
*
- * Return 0 if branch has no upstream (no base), -1 if upstream is missing
- * (with "gone" base), otherwise 1 (with base).
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
+ * upstream defined, or ref does not exist), 0 otherwise.
*/
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+ const char **upstream_name)
{
unsigned char sha1[20];
struct commit *ours, *theirs;
int rev_argc;
/* Cannot stat unless we are marked to build on top of somebody else. */
- if (!branch ||
- !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
- return 0;
+ base = branch_get_upstream(branch, NULL);
+ if (upstream_name)
+ *upstream_name = base;
+ if (!base)
+ return -1;
/* Cannot stat if what we used to build on no longer exists */
- base = branch->merge[0]->dst;
if (read_ref(base, sha1))
return -1;
theirs = lookup_commit_reference(sha1);
/* are we the same? */
if (theirs == ours) {
*num_theirs = *num_ours = 0;
- return 1;
+ return 0;
}
/* Run "rev-list --left-right ours...theirs" internally... */
/* clear object flags smudged by the above traversal */
clear_commit_marks(ours, ALL_REV_FLAGS);
clear_commit_marks(theirs, ALL_REV_FLAGS);
- return 1;
+ return 0;
}
/*
int format_tracking_info(struct branch *branch, struct strbuf *sb)
{
int ours, theirs;
+ const char *full_base;
char *base;
int upstream_is_gone = 0;
- switch (stat_tracking_info(branch, &ours, &theirs)) {
- case 0:
- /* no base */
- return 0;
- case -1:
- /* with "gone" base */
+ if (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) {
+ if (!full_base)
+ return 0;
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
- base = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+ base = shorten_unambiguous_ref(full_base, 0);
if (upstream_is_gone) {
strbuf_addf(sb,
_("Your branch is based on '%s', but the upstream is gone.\n"),
return 1;
}
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int one_local_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct ref ***local_tail = cb_data;
struct ref *ref;
len = strlen(refname) + 1;
ref = xcalloc(1, sizeof(*ref) + len);
- hashcpy(ref->new_sha1, sha1);
+ hashcpy(ref->new_sha1, oid->hash);
memcpy(ref->name, refname, len);
**local_tail = ref;
*local_tail = &ref->next;
struct ref *get_local_heads(void)
{
struct ref *local_refs = NULL, **local_tail = &local_refs;
+
for_each_ref(one_local_ref, &local_tail);
return local_refs;
}
int ref_count;
};
-static int get_stale_heads_cb(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
+static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
if (stale) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- hashcpy(ref->new_sha1, sha1);
+ hashcpy(ref->new_sha1, oid->hash);
}
clean_exit:
struct ref *ref, *stale_refs = NULL;
struct string_list ref_names = STRING_LIST_INIT_NODUP;
struct stale_heads_info info;
+
info.ref_names = &ref_names;
info.stale_refs_tail = &stale_refs;
info.refs = refs;
const char *refname;
const char *remote_name;
- struct remote *remote;
+ const char *pushremote_name;
const char **merge_name;
struct refspec **merge;
int merge_nr;
int merge_alloc;
+
+ const char *push_tracking_ref;
};
struct branch *branch_get(const char *name);
+const char *remote_for_branch(struct branch *branch, int *explicit);
+const char *pushremote_for_branch(struct branch *branch, int *explicit);
int branch_has_merge_config(struct branch *branch);
int branch_merge_matches(struct branch *, int n, const char *);
+/**
+ * Return the fully-qualified refname of the tracking branch for `branch`.
+ * I.e., what "branch@{upstream}" would give you. Returns NULL if no
+ * upstream is defined.
+ *
+ * If `err` is not NULL and no upstream is defined, a more specific error
+ * message is recorded there (if the function does not return NULL, then
+ * `err` is not touched).
+ */
+const char *branch_get_upstream(struct branch *branch, struct strbuf *err);
+
+/**
+ * Return the tracking branch that corresponds to the ref we would push to
+ * given a bare `git push` while `branch` is checked out.
+ *
+ * The return value and `err` conventions match those of `branch_get_upstream`.
+ */
+const char *branch_get_push(struct branch *branch, struct strbuf *err);
+
/* Flags to match_refs. */
enum match_refs_flags {
MATCH_REFS_NONE = 0,
};
/* Reporting of tracking info */
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+ const char **upstream_name);
int format_tracking_info(struct branch *branch, struct strbuf *sb);
struct ref *get_local_heads(void);
}
static int register_replace_ref(const char *refname,
- const unsigned char *sha1,
+ const struct object_id *oid,
int flag, void *cb_data)
{
/* Get sha1 from refname */
}
/* Copy sha1 from the read ref */
- hashcpy(repl_obj->replacement, sha1);
+ hashcpy(repl_obj->replacement, oid->hash);
/* Register new object */
if (register_replace_object(repl_obj, 1))
return 0;
}
-static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int handle_one_ref(const char *path, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct object *object;
if (ref_excluded(cb->all_revs->ref_excludes, path))
return 0;
- object = get_reference(cb->all_revs, path, sha1, cb->all_flags);
+ object = get_reference(cb->all_revs, path, oid->hash, cb->all_flags);
add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
+ add_pending_sha1(cb->all_revs, path, oid->hash, cb->all_flags);
return 0;
}
return 0;
}
-static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int handle_one_reflog(const char *path, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
cb->warned_bad_reflog = 0;
void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
{
struct all_refs_cb cb;
+
cb.all_revs = revs;
cb.all_flags = flags;
for_each_reflog(handle_one_reflog, &cb);
return ret;
}
-static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int add_info_ref(const char *path, const struct object_id *oid,
+ int flag, void *cb_data)
{
FILE *fp = cb_data;
- struct object *o = parse_object(sha1);
+ struct object *o = parse_object(oid->hash);
if (!o)
return -1;
- if (fprintf(fp, "%s %s\n", sha1_to_hex(sha1), path) < 0)
+ if (fprintf(fp, "%s %s\n", oid_to_hex(oid), path) < 0)
return -1;
if (o->type == OBJ_TAG) {
int ret;
if (!size) {
- ret = index_mem(sha1, NULL, size, type, path, flags);
+ ret = index_mem(sha1, "", size, type, path, flags);
} else if (size <= SMALL_FILE_SIZE) {
char *buf = xmalloc(size);
if (size == read_in_full(fd, buf, size))
#include "tree-walk.h"
#include "refs.h"
#include "remote.h"
+#include "dir.h"
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
return slash;
}
-static inline int upstream_mark(const char *string, int len)
+static inline int at_mark(const char *string, int len,
+ const char **suffix, int nr)
{
- const char *suffix[] = { "@{upstream}", "@{u}" };
int i;
- for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+ for (i = 0; i < nr; i++) {
int suffix_len = strlen(suffix[i]);
if (suffix_len <= len
&& !memcmp(string, suffix[i], suffix_len))
return 0;
}
+static inline int upstream_mark(const char *string, int len)
+{
+ const char *suffix[] = { "@{upstream}", "@{u}" };
+ return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
+static inline int push_mark(const char *string, int len)
+{
+ const char *suffix[] = { "@{push}" };
+ return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
nth_prior = 1;
continue;
}
- if (!upstream_mark(str + at, len - at)) {
+ if (!upstream_mark(str + at, len - at) &&
+ !push_mark(str + at, len - at)) {
reflog_len = (len-1) - (at+2);
len = at;
}
/* Remember to update object flag allocation in object.h */
#define ONELINE_SEEN (1u<<20)
-static int handle_one_ref(const char *path,
- const unsigned char *sha1, int flag, void *cb_data)
+static int handle_one_ref(const char *path, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct commit_list **list = cb_data;
- struct object *object = parse_object(sha1);
+ struct object *object = parse_object(oid->hash);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
free(s);
}
-static const char *get_upstream_branch(const char *branch_buf, int len)
-{
- char *branch = xstrndup(branch_buf, len);
- struct branch *upstream = branch_get(*branch ? branch : NULL);
-
- /*
- * Upstream can be NULL only if branch refers to HEAD and HEAD
- * points to something different than a branch.
- */
- if (!upstream)
- die(_("HEAD does not point to a branch"));
- if (!upstream->merge || !upstream->merge[0]->dst) {
- if (!ref_exists(upstream->refname))
- die(_("No such branch: '%s'"), branch);
- if (!upstream->merge) {
- die(_("No upstream configured for branch '%s'"),
- upstream->name);
- }
- die(
- _("Upstream branch '%s' not stored as a remote-tracking branch"),
- upstream->merge[0]->src);
- }
- free(branch);
-
- return upstream->merge[0]->dst;
-}
-
-static int interpret_upstream_mark(const char *name, int namelen,
- int at, struct strbuf *buf)
+static int interpret_branch_mark(const char *name, int namelen,
+ int at, struct strbuf *buf,
+ int (*get_mark)(const char *, int),
+ const char *(*get_data)(struct branch *,
+ struct strbuf *))
{
int len;
+ struct branch *branch;
+ struct strbuf err = STRBUF_INIT;
+ const char *value;
- len = upstream_mark(name + at, namelen - at);
+ len = get_mark(name + at, namelen - at);
if (!len)
return -1;
if (memchr(name, ':', at))
return -1;
- set_shortened_ref(buf, get_upstream_branch(name, at));
+ if (at) {
+ char *name_str = xmemdupz(name, at);
+ branch = branch_get(name_str);
+ free(name_str);
+ } else
+ branch = branch_get(NULL);
+
+ value = get_data(branch, &err);
+ if (!value)
+ die("%s", err.buf);
+
+ set_shortened_ref(buf, value);
return len + at;
}
if (len > 0)
return reinterpret(name, namelen, len, buf);
- len = interpret_upstream_mark(name, namelen, at - name, buf);
+ len = interpret_branch_mark(name, namelen, at - name, buf,
+ upstream_mark, branch_get_upstream);
+ if (len > 0)
+ return len;
+
+ len = interpret_branch_mark(name, namelen, at - name, buf,
+ push_mark, branch_get_push);
if (len > 0)
return len;
}
const char *object_name,
int object_name_len)
{
- struct stat st;
unsigned char sha1[20];
unsigned mode;
if (!prefix)
prefix = "";
- if (!lstat(filename, &st))
+ if (file_exists(filename))
die("Path '%s' exists on disk, but not in '%.*s'.",
filename, object_name_len, object_name);
if (errno == ENOENT || errno == ENOTDIR) {
const char *prefix,
const char *filename)
{
- struct stat st;
const struct cache_entry *ce;
int pos;
unsigned namelen = strlen(filename);
ce_stage(ce), filename);
}
- if (!lstat(filename, &st))
+ if (file_exists(filename))
die("Path '%s' exists on disk, but not in the index.", filename);
if (errno == ENOENT || errno == ENOTDIR)
die("Path '%s' does not exist (neither on disk nor in the index).",
int pos;
if (!only_to_die && namelen > 2 && name[1] == '/') {
struct commit_list *list = NULL;
+
for_each_ref(handle_one_ref, &list);
commit_list_sort_by_date(&list);
return get_sha1_oneline(name + 2, sha1, list);
new_filename = resolve_relative_path(filename);
if (new_filename)
filename = new_filename;
- ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
- if (ret && only_to_die) {
- diagnose_invalid_sha1_path(prefix, filename,
- tree_sha1,
- name, len);
+ if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
+ ret = get_tree_entry_follow_symlinks(tree_sha1,
+ filename, sha1, &oc->symlink_path,
+ &oc->mode);
+ } else {
+ ret = get_tree_entry(tree_sha1, filename,
+ sha1, &oc->mode);
+ if (ret && only_to_die) {
+ diagnose_invalid_sha1_path(prefix,
+ filename,
+ tree_sha1,
+ name, len);
+ }
}
hashcpy(oc->tree, tree_sha1);
strlcpy(oc->path, filename, sizeof(oc->path));
int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
{
+ if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
+ die("BUG: incompatible flags for get_sha1_with_context");
return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
}
free(tmp);
}
-static int mark_uninteresting(const char *refname,
- const unsigned char *sha1,
+static int mark_uninteresting(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
- struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+ struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
if (!commit)
return 0;
commit->object.flags |= UNINTERESTING;
int nr, alloc;
};
-static int add_ref(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
+static int add_ref(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
{
struct commit_array *ca = cb_data;
ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
- ca->commits[ca->nr] = lookup_commit_reference_gently(sha1, 1);
+ ca->commits[ca->nr] = lookup_commit_reference_gently(oid->hash, 1);
if (ca->commits[ca->nr])
ca->nr++;
return 0;
if (!si->commits) {
struct commit_array ca;
+
memset(&ca, 0, sizeof(ca));
head_ref(add_ref, &ca);
for_each_ref(add_ref, &ca);
config_fetch_recurse_submodules = value;
}
-static int has_remote(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+static int has_remote(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
{
return 1;
}
}
}
-static int add_sha1_to_array(const char *ref, const unsigned char *sha1,
+static int add_sha1_to_array(const char *ref, const struct object_id *oid,
int flags, void *data)
{
- sha1_array_append(data, sha1);
+ sha1_array_append(data, oid->hash);
return 0;
}
int ok_to_remove_submodule(const char *path)
{
- struct stat st;
ssize_t len;
struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {
struct strbuf buf = STRBUF_INIT;
int ok_to_remove = 1;
- if ((lstat(path, &st) < 0) || is_empty_dir(path))
+ if (!file_exists(path) || is_empty_dir(path))
return 1;
if (!submodule_uses_gitfile(path))
! test -s err
'
+test_expect_success "filter: clean empty file" '
+ git config filter.in-repo-header.clean "echo cleaned && cat" &&
+ git config filter.in-repo-header.smudge "sed 1d" &&
+
+ echo "empty-in-worktree filter=in-repo-header" >>.gitattributes &&
+ >empty-in-worktree &&
+
+ echo cleaned >expected &&
+ git add empty-in-worktree &&
+ git show :empty-in-worktree >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success "filter: smudge empty file" '
+ git config filter.empty-in-repo.clean "cat >/dev/null" &&
+ git config filter.empty-in-repo.smudge "echo smudged && cat" &&
+
+ echo "empty-in-repo filter=empty-in-repo" >>.gitattributes &&
+ echo dead data walking >empty-in-repo &&
+ git add empty-in-repo &&
+
+ echo smudged >expected &&
+ git checkout-index --prefix=filtered- empty-in-repo &&
+ test_cmp expected filtered-empty-in-repo
+'
+
test_done
'
done
+for opt in t s e p
+do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ '
+done
+
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
test_cmp expect actual
'
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+ echo_without_newline "$hello_content" >morx &&
+ test_ln_s_add morx same-dir-link &&
+ test_ln_s_add dir link-to-dir &&
+ test_ln_s_add ../fleem out-of-repo-link &&
+ test_ln_s_add .. out-of-repo-link-dir &&
+ test_ln_s_add same-dir-link link-to-link &&
+ test_ln_s_add nope broken-same-dir-link &&
+ mkdir dir &&
+ test_ln_s_add ../morx dir/parent-dir-link &&
+ test_ln_s_add .. dir/link-dir &&
+ test_ln_s_add ../../escape dir/out-of-repo-link &&
+ test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+ test_ln_s_add nope dir/broken-link-in-dir &&
+ mkdir dir/subdir &&
+ test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+ test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+ test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+ test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+ test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+ echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+ echo_without_newline "$hello_content" >dir/ind1 &&
+ test_ln_s_add dir dirlink &&
+ test_ln_s_add dir/subdir subdirlink &&
+ test_ln_s_add subdir/ind2 dir/link-to-child &&
+ test_ln_s_add dir/link-to-child link-to-down-link &&
+ test_ln_s_add dir/.. up-down &&
+ test_ln_s_add dir/../ up-down-trailing &&
+ test_ln_s_add dir/../morx up-down-file &&
+ test_ln_s_add dir/../../morx up-up-down-file &&
+ test_ln_s_add subdirlink/../../morx up-two-down-file &&
+ test_ln_s_add loop1 loop2 &&
+ test_ln_s_add loop2 loop1 &&
+ git add morx dir/subdir/ind2 dir/ind1 &&
+ git commit -am "test" &&
+ echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+ echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:nope missing >expect &&
+ echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+ echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+ echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+ echo dangling 25 >expect &&
+ echo HEAD:broken-same-dir-link >>expect &&
+ echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+ echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+ echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo notdir 29 >expect &&
+ echo HEAD:dir/parent-dir-link/nope >>expect &&
+ echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+ echo dangling 22 >expect &&
+ echo HEAD:dir/link-dir/nope >>expect &&
+ echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo dangling 27 >expect &&
+ echo HEAD:dir/broken-link-in-dir >>expect &&
+ echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+ echo notdir 41 >expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+ echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+ echo dangling 17 >expect &&
+ echo HEAD:dirlink/morx >>expect &&
+ echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo $hello_sha1 blob $hello_size >expect &&
+ echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+ echo dangling 20 >expect &&
+ echo HEAD:subdirlink/morx >>expect &&
+ echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+ echo notdir 27 >expect &&
+ echo HEAD:dir/link-to-child/morx >>expect &&
+ echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+ echo symlink 8 >expect &&
+ echo ../fleem >>expect &&
+ echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+ echo symlink 9 >expect &&
+ echo ../escape >>expect &&
+ echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+ echo symlink 15 >expect &&
+ echo ../great-escape >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 2 >expect &&
+ echo .. >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo symlink 3 >expect &&
+ echo ../ >>expect &&
+ echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+ echo HEAD: | git cat-file --batch-check >expect &&
+ echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual &&
+ echo symlink 7 >expect &&
+ echo ../morx >>expect &&
+ echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual &&
+ echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+ echo loop 10 >expect &&
+ echo HEAD:loop1 >>expect &&
+ echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+ echo HEAD:morx | git cat-file --batch >expect &&
+ echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+ test_cmp expect actual
+'
+
test_done
test_expect_success 'branch@{u} error message when no upstream' '
cat >expect <<-EOF &&
- fatal: No upstream configured for branch ${sq}non-tracking${sq}
+ fatal: no upstream configured for branch ${sq}non-tracking${sq}
EOF
error_message non-tracking@{u} 2>actual &&
test_i18ncmp expect actual
test_expect_success '@{u} error message when no upstream' '
cat >expect <<-EOF &&
- fatal: No upstream configured for branch ${sq}master${sq}
+ fatal: no upstream configured for branch ${sq}master${sq}
EOF
test_must_fail git rev-parse --verify @{u} 2>actual &&
test_i18ncmp expect actual
test_expect_success 'branch@{u} error message with misspelt branch' '
cat >expect <<-EOF &&
- fatal: No such branch: ${sq}no-such-branch${sq}
+ fatal: no such branch: ${sq}no-such-branch${sq}
EOF
error_message no-such-branch@{u} 2>actual &&
test_i18ncmp expect actual
test_expect_success 'branch@{u} error message if upstream branch not fetched' '
cat >expect <<-EOF &&
- fatal: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
+ fatal: upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
EOF
error_message bad-upstream@{u} 2>actual &&
test_i18ncmp expect actual
--- /dev/null
+#!/bin/sh
+
+test_description='test <branch>@{push} syntax'
+. ./test-lib.sh
+
+resolve () {
+ echo "$2" >expect &&
+ git rev-parse --symbolic-full-name "$1" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+ git init --bare parent.git &&
+ git init --bare other.git &&
+ git remote add origin parent.git &&
+ git remote add other other.git &&
+ test_commit base &&
+ git push origin HEAD &&
+ git branch --set-upstream-to=origin/master master &&
+ git branch --track topic origin/master &&
+ git push origin topic &&
+ git push other topic
+'
+
+test_expect_success '@{push} with default=nothing' '
+ test_config push.default nothing &&
+ test_must_fail git rev-parse master@{push}
+'
+
+test_expect_success '@{push} with default=simple' '
+ test_config push.default simple &&
+ resolve master@{push} refs/remotes/origin/master
+'
+
+test_expect_success 'triangular @{push} fails with default=simple' '
+ test_config push.default simple &&
+ test_must_fail git rev-parse topic@{push}
+'
+
+test_expect_success '@{push} with default=current' '
+ test_config push.default current &&
+ resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with default=matching' '
+ test_config push.default matching &&
+ resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with pushremote defined' '
+ test_config push.default current &&
+ test_config branch.topic.pushremote other &&
+ resolve topic@{push} refs/remotes/other/topic
+'
+
+test_expect_success '@{push} with push refspecs' '
+ test_config push.default nothing &&
+ test_config remote.origin.push refs/heads/*:refs/heads/magic/* &&
+ git push &&
+ resolve topic@{push} refs/remotes/origin/magic/topic
+'
+
+test_done
)
'
+test_expect_success 'stash drop complains of extra options' '
+ test_must_fail git stash drop --foo
+'
+
test_expect_success 'drop top stash' '
git reset --hard &&
git stash list > stashlist1 &&
. ./test-lib.sh
. "$TEST_DIRECTORY"/diff-lib.sh
-# Ray Lehtiniemi's example
+test_expect_success "Ray Lehtiniemi's example" '
+ cat <<-\EOF >x &&
+ do {
+ nothing;
+ } while (0);
+ EOF
+ git update-index --add x &&
-cat << EOF > x
-do {
- nothing;
-} while (0);
-EOF
+ cat <<-\EOF >x &&
+ do
+ {
+ nothing;
+ }
+ while (0);
+ EOF
+
+ cat <<-\EOF >expect &&
+ diff --git a/x b/x
+ index adf3937..6edc172 100644
+ --- a/x
+ +++ b/x
+ @@ -1,3 +1,5 @@
+ -do {
+ +do
+ +{
+ nothing;
+ -} while (0);
+ +}
+ +while (0);
+ EOF
-git update-index --add x
+ git diff >out &&
+ test_cmp expect out &&
-cat << EOF > x
-do
-{
- nothing;
-}
-while (0);
-EOF
+ git diff -w >out &&
+ test_cmp expect out &&
-cat << EOF > expect
-diff --git a/x b/x
-index adf3937..6edc172 100644
---- a/x
-+++ b/x
-@@ -1,3 +1,5 @@
--do {
-+do
-+{
- nothing;
--} while (0);
-+}
-+while (0);
-EOF
+ git diff -b >out &&
+ test_cmp expect out
+'
-git diff > out
-test_expect_success "Ray's example without options" 'test_cmp expect out'
+test_expect_success 'another test, without options' '
+ tr Q "\015" <<-\EOF >x &&
+ whitespace at beginning
+ whitespace change
+ whitespace in the middle
+ whitespace at end
+ unchanged line
+ CR at endQ
+ EOF
-git diff -w > out
-test_expect_success "Ray's example with -w" 'test_cmp expect out'
+ git update-index x &&
-git diff -b > out
-test_expect_success "Ray's example with -b" 'test_cmp expect out'
+ tr "_" " " <<-\EOF >x &&
+ _ whitespace at beginning
+ whitespace change
+ white space in the middle
+ whitespace at end__
+ unchanged line
+ CR at end
+ EOF
-tr 'Q' '\015' << EOF > x
-whitespace at beginning
-whitespace change
-whitespace in the middle
-whitespace at end
-unchanged line
-CR at endQ
-EOF
+ tr "Q_" "\015 " <<-\EOF >expect &&
+ diff --git a/x b/x
+ index d99af23..22d9f73 100644
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,6 @@
+ -whitespace at beginning
+ -whitespace change
+ -whitespace in the middle
+ -whitespace at end
+ + whitespace at beginning
+ +whitespace change
+ +white space in the middle
+ +whitespace at end__
+ unchanged line
+ -CR at endQ
+ +CR at end
+ EOF
-git update-index x
+ git diff >out &&
+ test_cmp expect out &&
-tr '_' ' ' << EOF > x
- whitespace at beginning
-whitespace change
-white space in the middle
-whitespace at end__
-unchanged line
-CR at end
-EOF
+ >expect &&
+ git diff -w >out &&
+ test_cmp expect out &&
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
--whitespace change
--whitespace in the middle
--whitespace at end
-+ whitespace at beginning
-+whitespace change
-+white space in the middle
-+whitespace at end__
- unchanged line
--CR at endQ
-+CR at end
-EOF
-git diff > out
-test_expect_success 'another test, without options' 'test_cmp expect out'
+ git diff -w -b >out &&
+ test_cmp expect out &&
-cat << EOF > expect
-EOF
-git diff -w > out
-test_expect_success 'another test, with -w' 'test_cmp expect out'
-git diff -w -b > out
-test_expect_success 'another test, with -w -b' 'test_cmp expect out'
-git diff -w --ignore-space-at-eol > out
-test_expect_success 'another test, with -w --ignore-space-at-eol' 'test_cmp expect out'
-git diff -w -b --ignore-space-at-eol > out
-test_expect_success 'another test, with -w -b --ignore-space-at-eol' 'test_cmp expect out'
-
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
-+ whitespace at beginning
- whitespace change
--whitespace in the middle
-+white space in the middle
- whitespace at end__
- unchanged line
- CR at end
-EOF
-git diff -b > out
-test_expect_success 'another test, with -b' 'test_cmp expect out'
-git diff -b --ignore-space-at-eol > out
-test_expect_success 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out'
-
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
--whitespace change
--whitespace in the middle
-+ whitespace at beginning
-+whitespace change
-+white space in the middle
- whitespace at end__
- unchanged line
- CR at end
-EOF
-git diff --ignore-space-at-eol > out
-test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
+ git diff -w --ignore-space-at-eol >out &&
+ test_cmp expect out &&
+
+ git diff -w -b --ignore-space-at-eol >out &&
+ test_cmp expect out &&
+
+
+ tr "Q_" "\015 " <<-\EOF >expect &&
+ diff --git a/x b/x
+ index d99af23..22d9f73 100644
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,6 @@
+ -whitespace at beginning
+ +_ whitespace at beginning
+ whitespace change
+ -whitespace in the middle
+ +white space in the middle
+ whitespace at end__
+ unchanged line
+ CR at end
+ EOF
+ git diff -b >out &&
+ test_cmp expect out &&
+
+ git diff -b --ignore-space-at-eol >out &&
+ test_cmp expect out &&
+
+ tr "Q_" "\015 " <<-\EOF >expect &&
+ diff --git a/x b/x
+ index d99af23..22d9f73 100644
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,6 @@
+ -whitespace at beginning
+ -whitespace change
+ -whitespace in the middle
+ +_ whitespace at beginning
+ +whitespace change
+ +white space in the middle
+ whitespace at end__
+ unchanged line
+ CR at end
+ EOF
+ git diff --ignore-space-at-eol >out &&
+ test_cmp expect out
+'
test_expect_success 'ignore-blank-lines: only new lines' '
test_seq 5 >x &&
'
test_expect_success 'check mixed spaces and tabs in indent' '
-
# This is indented with SP HT SP.
- echo " foo();" > x &&
+ echo " foo();" >x &&
git diff --check | grep "space before tab in indent"
-
'
test_expect_success 'check mixed tabs and spaces in indent' '
-
# This is indented with HT SP HT.
- echo " foo();" > x &&
+ echo " foo();" >x &&
git diff --check | grep "space before tab in indent"
-
'
test_expect_success 'check with no whitespace errors' '
-
git commit -m "snapshot" &&
- echo "foo();" > x &&
+ echo "foo();" >x &&
git diff --check
-
'
test_expect_success 'check with trailing whitespace' '
-
- echo "foo(); " > x &&
+ echo "foo(); " >x &&
test_must_fail git diff --check
-
'
test_expect_success 'check with space before tab in indent' '
-
# indent has space followed by hard tab
- echo " foo();" > x &&
+ echo " foo();" >x &&
test_must_fail git diff --check
-
'
test_expect_success '--check and --exit-code are not exclusive' '
-
git checkout x &&
git diff --check --exit-code
-
'
test_expect_success '--check and --quiet are not exclusive' '
-
git diff --check --quiet
-
'
test_expect_success 'check staged with no whitespace errors' '
-
- echo "foo();" > x &&
+ echo "foo();" >x &&
git add x &&
git diff --cached --check
-
'
test_expect_success 'check staged with trailing whitespace' '
-
- echo "foo(); " > x &&
+ echo "foo(); " >x &&
git add x &&
test_must_fail git diff --cached --check
-
'
test_expect_success 'check staged with space before tab in indent' '
-
# indent has space followed by hard tab
- echo " foo();" > x &&
+ echo " foo();" >x &&
git add x &&
test_must_fail git diff --cached --check
-
'
test_expect_success 'check with no whitespace errors (diff-index)' '
-
- echo "foo();" > x &&
+ echo "foo();" >x &&
git add x &&
git diff-index --check HEAD
-
'
test_expect_success 'check with trailing whitespace (diff-index)' '
-
- echo "foo(); " > x &&
+ echo "foo(); " >x &&
git add x &&
test_must_fail git diff-index --check HEAD
-
'
test_expect_success 'check with space before tab in indent (diff-index)' '
-
# indent has space followed by hard tab
- echo " foo();" > x &&
+ echo " foo();" >x &&
git add x &&
test_must_fail git diff-index --check HEAD
-
'
test_expect_success 'check staged with no whitespace errors (diff-index)' '
-
- echo "foo();" > x &&
+ echo "foo();" >x &&
git add x &&
git diff-index --cached --check HEAD
-
'
test_expect_success 'check staged with trailing whitespace (diff-index)' '
-
- echo "foo(); " > x &&
+ echo "foo(); " >x &&
git add x &&
test_must_fail git diff-index --cached --check HEAD
-
'
test_expect_success 'check staged with space before tab in indent (diff-index)' '
-
# indent has space followed by hard tab
- echo " foo();" > x &&
+ echo " foo();" >x &&
git add x &&
test_must_fail git diff-index --cached --check HEAD
-
'
test_expect_success 'check with no whitespace errors (diff-tree)' '
-
- echo "foo();" > x &&
+ echo "foo();" >x &&
git commit -m "new commit" x &&
git diff-tree --check HEAD^ HEAD
-
'
test_expect_success 'check with trailing whitespace (diff-tree)' '
-
- echo "foo(); " > x &&
+ echo "foo(); " >x &&
git commit -m "another commit" x &&
test_must_fail git diff-tree --check HEAD^ HEAD
-
'
test_expect_success 'check with space before tab in indent (diff-tree)' '
-
# indent has space followed by hard tab
- echo " foo();" > x &&
+ echo " foo();" >x &&
git commit -m "yet another" x &&
test_must_fail git diff-tree --check HEAD^ HEAD
-
'
test_expect_success 'check trailing whitespace (trailing-space: off)' '
-
git config core.whitespace "-trailing-space" &&
- echo "foo (); " > x &&
+ echo "foo (); " >x &&
git diff --check
-
'
test_expect_success 'check trailing whitespace (trailing-space: on)' '
-
git config core.whitespace "trailing-space" &&
- echo "foo (); " > x &&
+ echo "foo (); " >x &&
test_must_fail git diff --check
-
'
test_expect_success 'check space before tab in indent (space-before-tab: off)' '
-
# indent contains space followed by HT
git config core.whitespace "-space-before-tab" &&
- echo " foo ();" > x &&
+ echo " foo ();" >x &&
git diff --check
-
'
test_expect_success 'check space before tab in indent (space-before-tab: on)' '
-
# indent contains space followed by HT
git config core.whitespace "space-before-tab" &&
- echo " foo (); " > x &&
+ echo " foo (); " >x &&
test_must_fail git diff --check
-
'
test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
-
git config core.whitespace "-indent-with-non-tab" &&
- echo " foo ();" > x &&
+ echo " foo ();" >x &&
git diff --check
-
'
test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
-
git config core.whitespace "indent-with-non-tab" &&
- echo " foo ();" > x &&
+ echo " foo ();" >x &&
test_must_fail git diff --check
-
'
test_expect_success 'ditto, but tabwidth=9' '
-
git config core.whitespace "indent-with-non-tab,tabwidth=9" &&
git diff --check
-
'
test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
-
git config core.whitespace "indent-with-non-tab" &&
- echo " foo ();" > x &&
+ echo " foo ();" >x &&
test_must_fail git diff --check
-
'
test_expect_success 'ditto, but tabwidth=10' '
-
git config core.whitespace "indent-with-non-tab,tabwidth=10" &&
test_must_fail git diff --check
-
'
test_expect_success 'ditto, but tabwidth=20' '
-
git config core.whitespace "indent-with-non-tab,tabwidth=20" &&
git diff --check
-
'
test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
-
git config core.whitespace "-tab-in-indent" &&
- echo " foo ();" > x &&
+ echo " foo ();" >x &&
git diff --check
-
'
test_expect_success 'check tabs as indentation (tab-in-indent: on)' '
-
git config core.whitespace "tab-in-indent" &&
- echo " foo ();" > x &&
+ echo " foo ();" >x &&
test_must_fail git diff --check
-
'
test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
-
git config core.whitespace "tab-in-indent" &&
- echo " foo ();" > x &&
+ echo " foo ();" >x &&
test_must_fail git diff --check
-
'
test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '
-
git config core.whitespace "tab-in-indent,tabwidth=1" &&
test_must_fail git diff --check
-
'
test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
-
git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
- echo "foo ();" > x &&
+ echo "foo ();" >x &&
test_must_fail git diff --check
-
'
test_expect_success 'check tab-in-indent excluded from wildcard whitespace attribute' '
-
git config --unset core.whitespace &&
- echo "x whitespace" > .gitattributes &&
- echo " foo ();" > x &&
+ echo "x whitespace" >.gitattributes &&
+ echo " foo ();" >x &&
git diff --check &&
rm -f .gitattributes
-
'
test_expect_success 'line numbers in --check output are correct' '
-
- echo "" > x &&
- echo "foo(); " >> x &&
+ echo "" >x &&
+ echo "foo(); " >>x &&
git diff --check | grep "x:2:"
-
'
test_expect_success 'checkdiff detects new trailing blank lines (1)' '
git config color.diff.old red &&
git config color.diff.new green &&
git config color.diff.commit yellow &&
- git config color.diff.whitespace "normal red" &&
+ git config color.diff.whitespace blue &&
git config core.autocrlf false
'
-cat >expected <<\EOF
-<BOLD>diff --git a/x b/x<RESET>
-<BOLD>index 9daeafb..2874b91 100644<RESET>
-<BOLD>--- a/x<RESET>
-<BOLD>+++ b/x<RESET>
-<CYAN>@@ -1 +1,4 @@<RESET>
- test<RESET>
-<GREEN>+<RESET><GREEN>{<RESET>
-<GREEN>+<RESET><BRED> <RESET>
-<GREEN>+<RESET><GREEN>}<RESET>
-EOF
test_expect_success 'diff that introduces a line with only tabs' '
git config core.whitespace blank-at-eol &&
git reset --hard &&
- echo "test" > x &&
+ echo "test" >x &&
git commit -m "initial" x &&
- echo "{NTN}" | tr "NT" "\n\t" >> x &&
+ echo "{NTN}" | tr "NT" "\n\t" >>x &&
git -c color.diff=always diff | test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index 9daeafb..2874b91 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1 +1,4 @@<RESET>
+ test<RESET>
+ <GREEN>+<RESET><GREEN>{<RESET>
+ <GREEN>+<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>}<RESET>
+ EOF
+
+ test_cmp expected current
+'
+
+test_expect_success 'diff that introduces and removes ws breakages' '
+ git reset --hard &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. blank-at-eol "
+ } >x &&
+ git commit -a --allow-empty -m preimage &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. still-blank-at-eol " &&
+ echo "2. and a new line "
+ } >x &&
+
+ git -c color.diff=always diff |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ 0. blank-at-eol <RESET>
+ <RED>-1. blank-at-eol <RESET>
+ <GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+ EOF
+
+ test_cmp expected current
+'
+
+test_expect_success 'the same with --ws-error-highlight' '
+ git reset --hard &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. blank-at-eol "
+ } >x &&
+ git commit -a --allow-empty -m preimage &&
+ {
+ echo "0. blank-at-eol " &&
+ echo "1. still-blank-at-eol " &&
+ echo "2. and a new line "
+ } >x &&
+
+ git -c color.diff=always diff --ws-error-highlight=default,old |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ 0. blank-at-eol <RESET>
+ <RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+ EOF
+
+ test_cmp expected current &&
+
+ git -c color.diff=always diff --ws-error-highlight=all |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ <RESET>0. blank-at-eol<RESET><BLUE> <RESET>
+ <RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+ <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+ EOF
+
+ test_cmp expected current &&
+
+ git -c color.diff=always diff --ws-error-highlight=none |
+ test_decode_color >current &&
+
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/x b/x<RESET>
+ <BOLD>index d0233a2..700886e 100644<RESET>
+ <BOLD>--- a/x<RESET>
+ <BOLD>+++ b/x<RESET>
+ <CYAN>@@ -1,2 +1,3 @@<RESET>
+ 0. blank-at-eol <RESET>
+ <RED>-1. blank-at-eol <RESET>
+ <GREEN>+1. still-blank-at-eol <RESET>
+ <GREEN>+2. and a new line <RESET>
+ EOF
+
test_cmp expected current
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse C) $(git rev-parse HEAD^)
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
test_must_fail git rebase --onto D A &&
git rebase --skip &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse E) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse E) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse C) $(git rev-parse HEAD^)
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse C) $(git rev-parse HEAD^)
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse C) $(git rev-parse HEAD)
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
clear_hook_input &&
FAKE_LINES="1 fixup 2" git rebase -i B &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse C) $(git rev-parse HEAD)
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
- cat >expected.data <<EOF &&
-$(git rev-parse C) $(git rev-parse HEAD^)
-$(git rev-parse D) $(git rev-parse HEAD)
-EOF
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase -i (exec)' '
+ git reset --hard D &&
+ clear_hook_input &&
+ FAKE_LINES="edit 1 exec_false 2" git rebase -i B &&
+ echo something >bar &&
+ git add bar &&
+ # Fails because of exec false
+ test_must_fail git rebase --continue &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<-EOF &&
+ $(git rev-parse C) $(git rev-parse HEAD^)
+ $(git rev-parse D) $(git rev-parse HEAD)
+ EOF
verify_hook_input
'
)
'
+for configallowtipsha1inwant in true false
+do
+ test_expect_success "shallow fetch reachable SHA1 (but not a ref), allowtipsha1inwant=$configallowtipsha1inwant" '
+ mk_empty testrepo &&
+ (
+ cd testrepo &&
+ git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
+ git commit --allow-empty -m foo &&
+ git commit --allow-empty -m bar
+ ) &&
+ SHA1=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
+ mk_empty shallow &&
+ (
+ cd shallow &&
+ test_must_fail git fetch --depth=1 ../testrepo/.git $SHA1 &&
+ git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
+ git fetch --depth=1 ../testrepo/.git $SHA1 &&
+ git cat-file commit $SHA1
+ )
+ '
+
+ test_expect_success "deny fetch unreachable SHA1, allowtipsha1inwant=$configallowtipsha1inwant" '
+ mk_empty testrepo &&
+ (
+ cd testrepo &&
+ git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
+ git commit --allow-empty -m foo &&
+ git commit --allow-empty -m bar &&
+ git commit --allow-empty -m xyz
+ ) &&
+ SHA1_1=$(git --git-dir=testrepo/.git rev-parse HEAD^^) &&
+ SHA1_2=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
+ SHA1_3=$(git --git-dir=testrepo/.git rev-parse HEAD) &&
+ (
+ cd testrepo &&
+ git reset --hard $SHA1_2 &&
+ git cat-file commit $SHA1_1 &&
+ git cat-file commit $SHA1_3
+ ) &&
+ mk_empty shallow &&
+ (
+ cd shallow &&
+ test_must_fail git fetch ../testrepo/.git $SHA1_3 &&
+ test_must_fail git fetch ../testrepo/.git $SHA1_1 &&
+ git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
+ git fetch ../testrepo/.git $SHA1_1 &&
+ git cat-file commit $SHA1_1 &&
+ test_must_fail git cat-file commit $SHA1_2 &&
+ git fetch ../testrepo/.git $SHA1_2 &&
+ git cat-file commit $SHA1_2 &&
+ test_must_fail git fetch ../testrepo/.git $SHA1_3
+ )
+ '
+done
+
test_expect_success 'fetch follows tags by default' '
mk_test testrepo heads/master &&
rm -fr src dst &&
git -C hidden.git rev-parse --verify b
'
-test_expect_success 'create 2,000 tags in the repo' '
- (
- cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
- for i in $(test_seq 2000)
+# create an arbitrary number of tags, numbered from tag-$1 to tag-$2
+create_tags () {
+ rm -f marks &&
+ for i in $(test_seq "$1" "$2")
do
- echo "commit refs/heads/too-many-refs"
- echo "mark :$i"
- echo "committer git <git@example.com> $i +0000"
- echo "data 0"
- echo "M 644 inline bla.txt"
- echo "data 4"
- echo "bla"
+ # don't use here-doc, because it requires a process
+ # per loop iteration
+ echo "commit refs/heads/too-many-refs-$1" &&
+ echo "mark :$i" &&
+ echo "committer git <git@example.com> $i +0000" &&
+ echo "data 0" &&
+ echo "M 644 inline bla.txt" &&
+ echo "data 4" &&
+ echo "bla" &&
# make every commit dangling by always
# rewinding the branch after each commit
- echo "reset refs/heads/too-many-refs"
- echo "from :1"
+ echo "reset refs/heads/too-many-refs-$1" &&
+ echo "from :$1"
done | git fast-import --export-marks=marks &&
# now assign tags to all the dangling commits we created above
tag=$(perl -e "print \"bla\" x 30") &&
sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+}
+
+test_expect_success 'create 2,000 tags in the repo' '
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ create_tags 1 2000
)
'
test_line_count = 2 posts
'
+test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ create_tags 2001 50000
+ ) &&
+ git -C too-many-refs fetch -q --tags &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ create_tags 50001 100000
+ ) &&
+ git -C too-many-refs fetch -q --tags &&
+ git -C too-many-refs for-each-ref refs/tags >tags &&
+ test_line_count = 100000 tags
+'
+
stop_httpd
test_done
git update-ref refs/remotes/origin/master master &&
git remote add origin nowhere &&
git config branch.master.remote origin &&
- git config branch.master.merge refs/heads/master
+ git config branch.master.merge refs/heads/master &&
+ git remote add myfork elsewhere &&
+ git config remote.pushdefault myfork &&
+ git config push.default current
'
test_atom() {
test_atom head refname refs/heads/master
test_atom head upstream refs/remotes/origin/master
+test_atom head push refs/remotes/myfork/master
test_atom head objecttype commit
test_atom head objectsize 171
test_atom head objectname $(git rev-parse refs/heads/master)
test_atom tag refname refs/tags/testtag
test_atom tag upstream ''
+test_atom tag push ''
test_atom tag objecttype tag
test_atom tag objectsize 154
test_atom tag objectname $(git rev-parse refs/tags/testtag)
test_cmp expected actual
'
+test_expect_success '%(push) supports tracking specifiers, too' '
+ echo "[ahead 1]" >expected &&
+ git for-each-ref --format="%(push:track)" refs/heads >actual &&
+ test_cmp expected actual
+'
+
cat >expected <<EOF
$(git rev-parse --short HEAD)
EOF
sleep 1
}
-git update-index --untracked-cache
# It's fine if git update-index returns an error code other than one,
# it'll be caught in the first test.
-if test $? -eq 1; then
+test_lazy_prereq UNTRACKED_CACHE '
+ { git update-index --untracked-cache; ret=$?; } &&
+ test $ret -ne 1
+'
+
+if ! test_have_prereq UNTRACKED_CACHE; then
skip_all='This system does not support untracked cache'
test_done
fi
echo f2 >"file with spaces" &&
git add "file with spaces" &&
git commit -m "add files" &&
- P4EDITOR=touch git p4 submit
+ P4EDITOR="test-chmtime +5" git p4 submit
) &&
(
cd "$cli" &&
git rm foo\$bar &&
git rm file\ with\ spaces &&
git commit -m "remove files" &&
- P4EDITOR=touch git p4 submit
+ P4EDITOR="test-chmtime +5" git p4 submit
) &&
(
cd "$cli" &&
cd "$git" &&
echo line >>file1 &&
git commit -a -m "change 5" &&
- P4EDITOR="$TRASH_DIRECTORY/ed.sh" &&
+ P4EDITOR="\"$TRASH_DIRECTORY/ed.sh\"" &&
export P4EDITOR &&
git p4 submit &&
p4 changes //depot/... >wc &&
git commit --author "Alice <alice@example.com>" -m "a change by alice" file1 &&
git commit --author "Bob <bob@example.com>" -m "a change by bob" file2 &&
git config git-p4.skipSubmitEditCheck true &&
- P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user &&
+ P4EDITOR="test-chmtime +5" P4USER=alice P4PASSWD=secret &&
+ export P4EDITOR P4USER P4PASSWD &&
+ git p4 commit --preserve-user &&
p4_check_commit_author file1 alice &&
p4_check_commit_author file2 bob
)
git config git-p4.skipSubmitEditCheck true &&
echo "username-noperms: a change by alice" >>file1 &&
git commit --author "Alice <alice@example.com>" -m "perms: a change by alice" file1 &&
- P4EDITOR=touch P4USER=bob P4PASSWD=secret &&
+ P4EDITOR="test-chmtime +5" P4USER=bob P4PASSWD=secret &&
export P4EDITOR P4USER P4PASSWD &&
test_must_fail git p4 commit --preserve-user &&
! git diff --exit-code HEAD..p4/master
git commit --author "Bob <bob@example.com>" -m "preserve: a change by bob" file1 &&
echo "username-unknown: a change by charlie" >>file1 &&
git commit --author "Charlie <charlie@example.com>" -m "preserve: a change by charlie" file1 &&
- P4EDITOR=touch P4USER=alice P4PASSWD=secret &&
+ P4EDITOR="test-chmtime +5" P4USER=alice P4PASSWD=secret &&
export P4EDITOR P4USER P4PASSWD &&
test_must_fail git p4 commit --preserve-user &&
! git diff --exit-code HEAD..p4/master &&
--- /dev/null
+#!/bin/sh
+
+test_description='git p4 handling of EDITOR'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "file1"
+ )
+'
+
+# Check that the P4EDITOR argument can be given command-line
+# options, which git-p4 will then pass through to the shell.
+test_expect_success 'EDITOR with options' '
+ git p4 clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ echo change >file1 &&
+ git commit -m "change" file1 &&
+ P4EDITOR=": >\"$git/touched\" && test-chmtime +5" git p4 submit &&
+ test_path_is_file "$git/touched"
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
return run_command(&rsync);
}
-static int write_one_ref(const char *name, const unsigned char *sha1,
- int flags, void *data)
+static int write_one_ref(const char *name, const struct object_id *oid,
+ int flags, void *data)
{
struct strbuf *buf = data;
int len = buf->len;
strbuf_addstr(buf, name);
if (safe_create_leading_directories(buf->buf) ||
- write_file(buf->buf, 0, "%s\n", sha1_to_hex(sha1)))
+ write_file(buf->buf, 0, "%s\n", oid_to_hex(oid)))
return error("problems writing temporary file %s: %s",
buf->buf, strerror(errno));
strbuf_setlen(buf, len);
}
static int write_refs_to_temp_dir(struct strbuf *temp_dir,
- int refspec_nr, const char **refspec)
+ int refspec_nr, const char **refspec)
{
int i;
for (i = 0; i < refspec_nr; i++) {
- unsigned char sha1[20];
+ struct object_id oid;
char *ref;
- if (dwim_ref(refspec[i], strlen(refspec[i]), sha1, &ref) != 1)
+ if (dwim_ref(refspec[i], strlen(refspec[i]), oid.hash, &ref) != 1)
return error("Could not get ref %s", refspec[i]);
- if (write_one_ref(ref, sha1, 0, temp_dir)) {
+ if (write_one_ref(ref, &oid, 0, temp_dir)) {
free(ref);
return -1;
}
return error;
}
+struct dir_state {
+ void *tree;
+ unsigned long size;
+ unsigned char sha1[20];
+};
+
static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
{
int namelen = strlen(name);
return retval;
}
+/*
+ * This is Linux's built-in max for the number of symlinks to follow.
+ * That limit, of course, does not affect git, but it's a reasonable
+ * choice.
+ */
+#define GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS 40
+
+/**
+ * Find a tree entry by following symlinks in tree_sha (which is
+ * assumed to be the root of the repository). In the event that a
+ * symlink points outside the repository (e.g. a link to /foo or a
+ * root-level link to ../foo), the portion of the link which is
+ * outside the repository will be returned in result_path, and *mode
+ * will be set to 0. It is assumed that result_path is uninitialized.
+ * If there are no symlinks, or the end result of the symlink chain
+ * points to an object inside the repository, result will be filled in
+ * with the sha1 of the found object, and *mode will hold the mode of
+ * the object.
+ *
+ * See the code for enum follow_symlink_result for a description of
+ * the return values.
+ */
+enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode)
+{
+ int retval = MISSING_OBJECT;
+ struct dir_state *parents = NULL;
+ size_t parents_alloc = 0;
+ ssize_t parents_nr = 0;
+ unsigned char current_tree_sha1[20];
+ struct strbuf namebuf = STRBUF_INIT;
+ struct tree_desc t;
+ int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
+ int i;
+
+ init_tree_desc(&t, NULL, 0UL);
+ strbuf_init(result_path, 0);
+ strbuf_addstr(&namebuf, name);
+ hashcpy(current_tree_sha1, tree_sha1);
+
+ while (1) {
+ int find_result;
+ char *first_slash;
+ char *remainder = NULL;
+
+ if (!t.buffer) {
+ void *tree;
+ unsigned char root[20];
+ unsigned long size;
+ tree = read_object_with_reference(current_tree_sha1,
+ tree_type, &size,
+ root);
+ if (!tree)
+ goto done;
+
+ ALLOC_GROW(parents, parents_nr + 1, parents_alloc);
+ parents[parents_nr].tree = tree;
+ parents[parents_nr].size = size;
+ hashcpy(parents[parents_nr].sha1, root);
+ parents_nr++;
+
+ if (namebuf.buf[0] == '\0') {
+ hashcpy(result, root);
+ retval = FOUND;
+ goto done;
+ }
+
+ if (!size)
+ goto done;
+
+ /* descend */
+ init_tree_desc(&t, tree, size);
+ }
+
+ /* Handle symlinks to e.g. a//b by removing leading slashes */
+ while (namebuf.buf[0] == '/') {
+ strbuf_remove(&namebuf, 0, 1);
+ }
+
+ /* Split namebuf into a first component and a remainder */
+ if ((first_slash = strchr(namebuf.buf, '/'))) {
+ *first_slash = 0;
+ remainder = first_slash + 1;
+ }
+
+ if (!strcmp(namebuf.buf, "..")) {
+ struct dir_state *parent;
+ /*
+ * We could end up with .. in the namebuf if it
+ * appears in a symlink.
+ */
+
+ if (parents_nr == 1) {
+ if (remainder)
+ *first_slash = '/';
+ strbuf_add(result_path, namebuf.buf,
+ namebuf.len);
+ *mode = 0;
+ retval = FOUND;
+ goto done;
+ }
+ parent = &parents[parents_nr - 1];
+ free(parent->tree);
+ parents_nr--;
+ parent = &parents[parents_nr - 1];
+ init_tree_desc(&t, parent->tree, parent->size);
+ strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
+ continue;
+ }
+
+ /* We could end up here via a symlink to dir/.. */
+ if (namebuf.buf[0] == '\0') {
+ hashcpy(result, parents[parents_nr - 1].sha1);
+ retval = FOUND;
+ goto done;
+ }
+
+ /* Look up the first (or only) path component in the tree. */
+ find_result = find_tree_entry(&t, namebuf.buf,
+ current_tree_sha1, mode);
+ if (find_result) {
+ goto done;
+ }
+
+ if (S_ISDIR(*mode)) {
+ if (!remainder) {
+ hashcpy(result, current_tree_sha1);
+ retval = FOUND;
+ goto done;
+ }
+ /* Descend the tree */
+ t.buffer = NULL;
+ strbuf_remove(&namebuf, 0,
+ 1 + first_slash - namebuf.buf);
+ } else if (S_ISREG(*mode)) {
+ if (!remainder) {
+ hashcpy(result, current_tree_sha1);
+ retval = FOUND;
+ } else {
+ retval = NOT_DIR;
+ }
+ goto done;
+ } else if (S_ISLNK(*mode)) {
+ /* Follow a symlink */
+ unsigned long link_len;
+ size_t len;
+ char *contents, *contents_start;
+ struct dir_state *parent;
+ enum object_type type;
+
+ if (follows_remaining-- == 0) {
+ /* Too many symlinks followed */
+ retval = SYMLINK_LOOP;
+ goto done;
+ }
+
+ /*
+ * At this point, we have followed at a least
+ * one symlink, so on error we need to report this.
+ */
+ retval = DANGLING_SYMLINK;
+
+ contents = read_sha1_file(current_tree_sha1, &type,
+ &link_len);
+
+ if (!contents)
+ goto done;
+
+ if (contents[0] == '/') {
+ strbuf_addstr(result_path, contents);
+ free(contents);
+ *mode = 0;
+ retval = FOUND;
+ goto done;
+ }
+
+ if (remainder)
+ len = first_slash - namebuf.buf;
+ else
+ len = namebuf.len;
+
+ contents_start = contents;
+
+ parent = &parents[parents_nr - 1];
+ init_tree_desc(&t, parent->tree, parent->size);
+ strbuf_splice(&namebuf, 0, len,
+ contents_start, link_len);
+ if (remainder)
+ namebuf.buf[link_len] = '/';
+ free(contents);
+ }
+ }
+done:
+ for (i = 0; i < parents_nr; i++)
+ free(parents[i].tree);
+ free(parents);
+
+ strbuf_release(&namebuf);
+ return retval;
+}
+
static int match_entry(const struct pathspec_item *item,
const struct name_entry *entry, int pathlen,
const char *match, int matchlen,
typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
+enum follow_symlinks_result {
+ FOUND = 0, /* This includes out-of-tree links */
+ MISSING_OBJECT = -1, /* The initial symlink is missing */
+ DANGLING_SYMLINK = -2, /*
+ * The initial symlink is there, but
+ * (transitively) points to a missing
+ * in-tree file
+ */
+ SYMLINK_LOOP = -3,
+ NOT_DIR = -4, /*
+ * Somewhere along the symlink chain, a path is
+ * requested which contains a file as a
+ * non-final element.
+ */
+};
+
+enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode);
+
struct traverse_info {
struct traverse_info *prev;
struct name_entry name;
static int no_done;
static int use_thin_pack, use_ofs_delta, use_include_tag;
static int no_progress, daemon_mode;
-static int allow_tip_sha1_in_want;
+/* Allow specifying sha1 if it is a ref tip. */
+#define ALLOW_TIP_SHA1 01
+/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
+#define ALLOW_REACHABLE_SHA1 02
+static unsigned int allow_unadvertised_object_request;
static int shallow_nr;
static struct object_array have_obj;
static struct object_array want_obj;
static int is_our_ref(struct object *o)
{
- return o->flags &
- ((allow_tip_sha1_in_want ? HIDDEN_REF : 0) | OUR_REF);
+ int allow_hidden_ref = (allow_unadvertised_object_request &
+ (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
+ return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
}
static void check_non_tip(void)
char namebuf[42]; /* ^ + SHA-1 + LF */
int i;
- /* In the normal in-process case non-tip request can never happen */
- if (!stateless_rpc)
+ /*
+ * In the normal in-process case without
+ * uploadpack.allowReachableSHA1InWant,
+ * non-tip requests can never happen.
+ */
+ if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
goto error;
cmd.argv = argv;
}
/* return non-zero if the ref is hidden, otherwise 0 */
-static int mark_our_ref(const char *refname, const unsigned char *sha1)
+static int mark_our_ref(const char *refname, const struct object_id *oid)
{
- struct object *o = lookup_unknown_object(sha1);
+ struct object *o = lookup_unknown_object(oid->hash);
if (ref_is_hidden(refname)) {
o->flags |= HIDDEN_REF;
return 0;
}
-static int check_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int check_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
- mark_our_ref(refname, sha1);
+ mark_our_ref(refname, oid);
return 0;
}
strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
}
-static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int send_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow no-progress"
" include-tag multi_ack_detailed";
const char *refname_nons = strip_namespace(refname);
- unsigned char peeled[20];
+ struct object_id peeled;
- if (mark_our_ref(refname, sha1))
+ if (mark_our_ref(refname, oid))
return 0;
if (capabilities) {
struct strbuf symref_info = STRBUF_INIT;
format_symref_info(&symref_info, cb_data);
- packet_write(1, "%s %s%c%s%s%s%s agent=%s\n",
- sha1_to_hex(sha1), refname_nons,
+ packet_write(1, "%s %s%c%s%s%s%s%s agent=%s\n",
+ oid_to_hex(oid), refname_nons,
0, capabilities,
- allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
+ (allow_unadvertised_object_request & ALLOW_TIP_SHA1) ?
+ " allow-tip-sha1-in-want" : "",
+ (allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1) ?
+ " allow-reachable-sha1-in-want" : "",
stateless_rpc ? " no-done" : "",
symref_info.buf,
git_user_agent_sanitized());
strbuf_release(&symref_info);
} else {
- packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
+ packet_write(1, "%s %s\n", oid_to_hex(oid), refname_nons);
}
capabilities = NULL;
- if (!peel_ref(refname, peeled))
- packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
+ if (!peel_ref(refname, peeled.hash))
+ packet_write(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return 0;
}
-static int find_symref(const char *refname, const unsigned char *sha1, int flag,
- void *cb_data)
+static int find_symref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
- unsigned char unused[20];
+ struct object_id unused;
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, 0, unused, &flag);
+ symref_target = resolve_ref_unsafe(refname, 0, unused.hash, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, refname);
static int upload_pack_config(const char *var, const char *value, void *unused)
{
- if (!strcmp("uploadpack.allowtipsha1inwant", var))
- allow_tip_sha1_in_want = git_config_bool(var, value);
- else if (!strcmp("uploadpack.keepalive", var)) {
+ if (!strcmp("uploadpack.allowtipsha1inwant", var)) {
+ if (git_config_bool(var, value))
+ allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
+ else
+ allow_unadvertised_object_request &= ~ALLOW_TIP_SHA1;
+ } else if (!strcmp("uploadpack.allowreachablesha1inwant", var)) {
+ if (git_config_bool(var, value))
+ allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
+ else
+ allow_unadvertised_object_request &= ~ALLOW_REACHABLE_SHA1;
+ } else if (!strcmp("uploadpack.keepalive", var)) {
keepalive = git_config_int(var, value);
if (!keepalive)
keepalive = -1;
return -1;
}
-static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int mark_complete(const char *path, const struct object_id *oid,
+ int flag, void *cb_data)
{
- struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+ struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
+
if (commit) {
commit->object.flags |= COMPLETE;
commit_list_insert(commit, &complete);
color_fprintf(s->fp, branch_color_local, "%s", branch_name);
- switch (stat_tracking_info(branch, &num_ours, &num_theirs)) {
- case 0:
- /* no base */
- fputc(s->null_termination ? '\0' : '\n', s->fp);
- return;
- case -1:
- /* with "gone" base */
+ if (stat_tracking_info(branch, &num_ours, &num_theirs, &base) < 0) {
+ if (!base) {
+ fputc(s->null_termination ? '\0' : '\n', s->fp);
+ return;
+ }
+
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
- base = branch->merge[0]->dst;
base = shorten_unambiguous_ref(base, 0);
color_fprintf(s->fp, header_color, "...");
color_fprintf(s->fp, branch_color_remote, "%s", base);